How to generate shapefiles for H3 hexagons in a particular area - shapefile

I would like to generate shapefiles for H3 hexagons in a specific geographic area. Particularly, I'm interested in the Bay Area with resolutions = 6, 7 and 9. How can I create the shapefiles for the hexagons covering this area?
I'm new to shapefiles or any other geographic data structures. I'm most comfortable with python and R.

The basic steps here are:
Take a polygon of your desired area. A bounding box should work well.
Use the polyfill method to fill the polygon with hexagons at the desired resolution.
Loop over each hexagon and get the boundary with the h3ToGeoBoundary function.
Put these boundaries into a GeoJSON file
Use a converter like ogr2ogr to convert to a shapefile.
The Python bindings have not been released, and I'm not familiar with the R bindings, but the JavaScript version might look like this:
var h3 = require('h3-js');
var bbox = [
[-123.308821530582, 38.28055644998254],
[-121.30037257250085, 38.28055644998254],
[-121.30037257250085, 37.242722073589164],
[-123.308821530582, 37.242722073589164]
];
var hexagons = h3.polyfill(bbox, 6, true);
var geojson = {
type: 'Feature',
geometry: {
type: 'MultiPolygon',
coordinates: hexagons.map(function toBoundary(hex) {
return [h3.h3ToGeoBoundary(hex, true)];
})
}
};
console.log(JSON.stringify(geojson));
and you'd use the script like this:
node bbox-geojson.js | ogr2ogr -f "ESRI Shapefile" bbox-hexagons.shp /vsistdin/

If you're looking for solution in R, the h3jsr package provides access to Uber's H3 library. The solution to your question can be done using the functions h3jsr::polyfill() and h3jsr::h3_to_polygon.
Reproducible example
library(ggplot2)
library(h3jsr)
library(sf)
library(sf)
# read the shapefile of the polygon area you're interested in
nc <- st_read(system.file("shape/nc.shp", package="sf"), quiet = TRUE)
# projection
nc <- st_transform(nc, crs = 4326)
# get the unique h3 ids of the hexagons intersecting your polygon at a given resolution
nc_5 <- polyfill(nc, res = 5, simple = FALSE)
# pass the h3 ids to return the hexagonal grid
hex_grid5 <- unlist(nc_5$h3_polyfillers) %>% h3_to_polygon(simple = FALSE)
This will return the polygons below:

Taking up John Stud's question here, because I've had the same 'problem'. In the following, I'll comment on how to read in a shapefile, hexagonize it with H3, and get a Hexagon geodataframe from it (and eventually save it as a shapefile).
Reproducible example
Let's get a shapefile for the US, e.g. here (I use the "cb_2018_us_state_500k.zip" one).
# Imports
import h3
import geopandas as gpd
import matplotlib.pyplot as plt
import pandas as pd
import shapely
from shapely.ops import unary_union
from shapely.geometry import mapping, Polygon
# Read shapefile
gdf = gpd.read_file("data/cb_2018_us_state_500k.shp")
# Get US without territories / Alaska + Hawaii
us = gdf[~gdf.NAME.isin(["Hawaii", "Alaska", "American Samoa",
"United States Virgin Islands", "Guam",
"Commonwealth of the Northern Mariana Islands",
"Puerto Rico"])]
# Plot it
fig, ax = plt.subplots(1,1)
us.plot(ax=ax)
plt.show()
# Convert to EPSG 4326 for compatibility with H3 Hexagons
us = us.to_crs(epsg=4326)
# Get union of the shape (whole US)
union_poly = unary_union(us.geometry)
# Find the hexagons within the shape boundary using PolyFill
hex_list=[]
for n,g in enumerate(union_poly):
if (n+1) % 100 == 0:
print(str(n+1)+"/"+str(len(union_poly)))
temp = mapping(g)
temp['coordinates']=[[[j[1],j[0]] for j in i] for i in temp['coordinates']]
hex_list.extend(h3.polyfill(temp,res=5))
# Create hexagon data frame
us_hex = pd.DataFrame(hex_list,columns=["hex_id"])
# Create hexagon geometry and GeoDataFrame
us_hex['geometry'] = [Polygon(h3.h3_to_geo_boundary(x, geo_json=True)) for x in us_hex["hex_id"]]
us_hex = gpd.GeoDataFrame(us_hex)
# Plot the thing
fig, ax = plt.subplots(1,1)
us_hex.plot(ax=ax, cmap="prism")
plt.show()
The above plot has resolution "5" (https://h3geo.org/docs/core-library/restable/), I suggest you look at other resolutions, too, like 4:
Of course, that depends on the "zoom level", i.e., whether you're looking at entire countries or just cities or so.
And, of course, to answer the original question: You can save the resulting shapefile using
us_hex.to_file("us_hex.shp")

Modified one from #nrabinowitz with separate polygons and index names:
var h3 = require('h3-js');
var bbox = [
[-123.308821530582, 38.28055644998254],
[-121.30037257250085, 38.28055644998254],
[-121.30037257250085, 37.242722073589164],
[-123.308821530582, 37.242722073589164]
];
var hexagons = h3.polyfill(bbox, 5, false);
var features = hexagons.map(function toBoundary(hex) {
var coords = h3.h3ToGeoBoundary(hex, true)
var feature = {"type": "Feature",
"properties": {"name": hex},
"geometry": {
"type": "Polygon",
"coordinates": [coords]}};
return feature;
});
console.log(JSON.stringify({
"type": "FeatureCollection",
"features": features}));

Related

Plotting Elevation in Python

I'm trying to create a map of Malawi with altitude shown. Something like this, but of Malawi of course:
I have downloaded some elevation data from here: http://research.jisao.washington.edu/data_sets/elevation/
This is a print of that data after I created a cube:
meters, from 5-min data / (unknown) (time: 1; latitude: 360; longitude: 720)
Dimension coordinates:
time x - -
latitude - x -
longitude - - x
Attributes:
history:
Elevations calculated from the TBASE 5-minute
latitude-longitude resolution...
invalid_units: meters, from 5-min data
I started with importing my data, forming a cube, removing the extra variables (time and history) and limiting my data to the latitudes and longitudes for Malawi.
import matplotlib.pyplot as plt
import matplotlib.cm as mpl_cm
import numpy as np
import iris
import cartopy
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import iris.analysis.cartography
def main():
#bring in altitude data
Elev = '/exports/csce/datastore/geos/users/s0899345/Climate_Modelling/Actual_Data/elev.0.5-deg.nc'
Elev= iris.load_cube(Elev)
#remove variable for time
del Elev.attributes['history']
Elev = Elev.collapsed('time', iris.analysis.MEAN)
Malawi = iris.Constraint(longitude=lambda v: 32.0 <= v <= 36., latitude=lambda v: -17. <= v <= -8.)
Elev = Elev.extract(Malawi)
print 'Elevation'
print Elev.data
print 'latitude'
print Elev.coord('latitude')
print 'longitude'
print Elev.coord('longitude')
This works well and the output is as follows:
Elevation
[[ 978. 1000. 1408. 1324. 1080. 1370. 1857. 1584.]
[ 1297. 1193. 1452. 1611. 1354. 1480. 1350. 627.]
[ 1418. 1490. 1625. 1486. 1977. 1802. 1226. 482.]
[ 1336. 1326. 1405. 728. 1105. 1559. 1139. 789.]
[ 1368. 1301. 1463. 1389. 671. 942. 947. 970.]
[ 1279. 1116. 1323. 1587. 839. 1014. 1071. 1003.]
[ 1096. 969. 1179. 1246. 855. 979. 927. 638.]
[ 911. 982. 1235. 1324. 681. 813. 814. 707.]
[ 749. 957. 1220. 1198. 613. 688. 832. 858.]
[ 707. 1049. 1037. 907. 624. 771. 1142. 1104.]
[ 836. 1044. 1124. 1120. 682. 711. 1126. 922.]
[ 1050. 1204. 1199. 1161. 777. 569. 999. 828.]
[ 1006. 869. 1183. 1230. 1354. 616. 762. 784.]
[ 838. 607. 883. 1181. 1174. 927. 591. 856.]
[ 561. 402. 626. 775. 1053. 726. 828. 733.]
[ 370. 388. 363. 422. 508. 471. 906. 1104.]
[ 504. 326. 298. 208. 246. 160. 458. 682.]
[ 658. 512. 334. 309. 156. 162. 123. 340.]]
latitude
DimCoord(array([ -8.25, -8.75, -9.25, -9.75, -10.25, -10.75, -11.25, -11.75,
-12.25, -12.75, -13.25, -13.75, -14.25, -14.75, -15.25, -15.75,
-16.25, -16.75], dtype=float32), standard_name='latitude', units=Unit('degrees'), var_name='lat', attributes={'title': 'Latitude'})
longitude
DimCoord(array([ 32.25, 32.75, 33.25, 33.75, 34.25, 34.75, 35.25, 35.75], dtype=float32), standard_name='longitude', units=Unit('degrees'), var_name='lon', attributes={'title': 'Longitude'})
However when I try to plot it, it doesn't work... this is what I did:
#plot map with physical features
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS)
ax.add_feature(cartopy.feature.LAKES, alpha=0.5)
ax.add_feature(cartopy.feature.RIVERS)
#plot altitude data
plot=ax.plot(Elev, cmap=mpl_cm.get_cmap('YlGn'), levels=np.arange(0,2000,150), extend='both')
#add colour bar index and a label
plt.colorbar(plot, label='meters above sea level')
#set map boundary
ax.set_extent([32., 36., -8, -17])
#set axis tick marks
ax.set_xticks([33, 34, 35])
ax.set_yticks([-10, -12, -14, -16])
lon_formatter = LongitudeFormatter(zero_direction_label=True)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
#save the image of the graph and include full legend
plt.savefig('Map_data_boundary', bbox_inches='tight')
plt.show()
The error I get is 'Attribute Error: Unknown property type cmap' and the following map of the whole world...
Any ideas?
I'll prepare the data the same as you, except to remove the time dimension I'll use iris.util.squeeze, which removes any length-1 dimension.
import iris
elev = iris.load_cube('elev.0.5-deg.nc')
elev = iris.util.squeeze(elev)
malawi = iris.Constraint(longitude=lambda v: 32.0 <= v <= 36.,
latitude=lambda v: -17. <= v <= -8.)
elev = elev.extract(malawi)
As #ImportanceOfBeingErnest says, you want a contour plot. When unsure what plotting function to use, I recommend browsing the matplotlib gallery to find something that looks similar to what you want to produce. Click on an image and it shows you the code.
So, to make the contour plot you can use the matplotlib.pyplot.contourf function, but you have to get the relevant data from the cube in the form of numpy arrays:
import matplotlib.pyplot as plt
import matplotlib.cm as mpl_cm
import numpy as np
import cartopy
cmap = mpl_cm.get_cmap('YlGn')
levels = np.arange(0,2000,150)
extend = 'max'
ax = plt.axes(projection=cartopy.crs.PlateCarree())
plt.contourf(elev.coord('longitude').points, elev.coord('latitude').points,
elev.data, cmap=cmap, levels=levels, extend=extend)
However, iris provides a shortcut to the maplotlib.pyplot functions in the form of iris.plot. This automatically sets up an axes instance with the right projection, and passes the data from the cube through to matplotlib.pyplot. So the last two lines can simply become:
import iris.plot as iplt
iplt.contourf(elev, cmap=cmap, levels=levels, extend=extend)
There is also iris.quickplot, which is basically the same as iris.plot, except that it automatically adds a colorbar and labels where appropriate:
import iris.quickplot as qplt
qplt.contourf(elev, cmap=cmap, levels=levels, extend=extend)
Once plotted, you can get hold of the axes instance and add your other items (for which I simply copied your code):
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
qplt.contourf(elev, cmap=cmap, levels=levels, extend=extend)
ax = plt.gca()
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS)
ax.add_feature(cartopy.feature.LAKES, alpha=0.5)
ax.add_feature(cartopy.feature.RIVERS)
ax.set_xticks([33, 34, 35])
ax.set_yticks([-10, -12, -14, -16])
lon_formatter = LongitudeFormatter(zero_direction_label=True)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
It seems you want something like a contour plot. So instead of
plot = ax.plot(...)
you probably want to use
plot = ax.contourf(...)
Most probably you also want to give latitude and longitude as arguments to contourf,
plot = ax.contourf(longitude, latitude, Elev, ...)
You can try to add this:
import matplotlib.colors as colors
color = plt.get_cmap('YlGn') # and change cmap=mpl_cm.get_cmap('YlGn') to cmap=color
And also try to update your matplotlib:
pip install --upgrade matplotlib
EDIT
color = plt.get_cmap('YlGn') # and change cmap=mpl_cm.get_cmap('YlGn') to cmap=color

Iterate Pandas Series to create a new chart legend

After grouping etc. I get a Series like in the example below. I would like to show the average numbers for each bar. The code below shows only one entry (of course, as I have only one "legend"). Could anyone one suggest a smart way of showing these numbers?
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib
matplotlib.style.use('ggplot')
import pandas
# create Series
dict_ = {"Business" : 104.04,"Economy":67.04, "Markets":58.56, "Companies":38.48}
s = pandas.Series(data=dict_)
# plot it
ax = s.plot(kind='bar', color='#43C6DB', stacked=True, figsize=(20, 10), legend=False)
plt.tick_params(axis='both', which='major', labelsize=14)
plt.xticks(rotation=30) #rotate labels
# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
#create new legend
legend = ['%s (%.1f a day)' %(i, row/7) for i, row in s.iteritems()]
# Put the legend to the right of the current axis
L = ax.legend(legend, loc='center left', bbox_to_anchor=(1, 0.5), fontsize=18)
plt.show()
The legend only has a single entry. This is a handle of a blue bar. Therefore even if you set the labels to a longer list, only the first element of that list is used as label for the existing handle.
The idea can be to duplicate the legend handle to have the same size as the labels
legend = ['%s (%.1f a day)' %(i, row/7) for i, row in s.iteritems()]
h,l = ax.get_legend_handles_labels()
L = ax.legend(handles = h*len(legend), labels=legend, loc='center left',
bbox_to_anchor=(1, 0.5), fontsize=18)

How to make tabular legend for geopandas plot

I am plotting a choropleth map using geopandas and I need to plot a customized tabular legend. This question's answer shows how to obtain a tabular legend for a contourf plot.
And I'am using it in the code bellow :
import pandas as pd
import pysal as ps
import geopandas as gp
import numpy as np
import matplotlib.pyplot as plt
pth = 'outcom.shp'
tracts = gp.GeoDataFrame.from_file(pth)
ax = tracts.plot(column='Density', scheme='QUANTILES')
valeur = np.array([.1,.45,.7])
text=[["Faible","Ng<1,5" ],["Moyenne","1,5<Ng<2,5"],[u"Elevee", "Ng>2,5"]]
colLabels = ["Exposition", u"Densite"]
tab = ax.table(cellText=text, colLabels=colLabels, colWidths = [0.2,0.2], loc='lower right', cellColours=plt.cm.hot_r(np.c_[valeur,valeur]))
plt.show()
And here's the result i get :
So basically, as you can see there is no link between the colors of the classes in the map and the table. I need to have the exact colors that i have in the table shown in the map. The 'NG value' shown in the legend should be extracted from the column 'DENSITY' that i am plotting.
However, since I do not have a contour plot to extract the colormap from, I'm lost on how to link the tabular legend and the map's colors.
Note: This answer is outdated. Modern geopandas allows to use a normal legend via legend=True argument. I still keep it here for reference though, or in case someone wants a truely tabular legend.
The geopandas plot does not support adding a legend. It also does not provide access to its plotting object and only returns an axes with the shapes as polygons. (It does not even provide a PolyCollection to work with). It is therefore a lot of tedious work to create a normal legend for such a plot.
Fortunately some of this work is already beeing done in the example notebook Choropleth classification with PySAL and GeoPandas - With legend
So we need to take this code and implement the custom tabular legend which comes from this answer.
Here is the complete code:
def __pysal_choro(values, scheme, k=5):
""" Wrapper for choropleth schemes from PySAL for use with plot_dataframe
Parameters
----------
values
Series to be plotted
scheme
pysal.esda.mapclassify classificatin scheme ['Equal_interval'|'Quantiles'|'Fisher_Jenks']
k
number of classes (2 <= k <=9)
Returns
-------
values
Series with values replaced with class identifier if PySAL is available, otherwise the original values are used
"""
try:
from pysal.esda.mapclassify import Quantiles, Equal_Interval, Fisher_Jenks
schemes = {}
schemes['equal_interval'] = Equal_Interval
schemes['quantiles'] = Quantiles
schemes['fisher_jenks'] = Fisher_Jenks
s0 = scheme
scheme = scheme.lower()
if scheme not in schemes:
scheme = 'quantiles'
print('Unrecognized scheme: ', s0)
print('Using Quantiles instead')
if k < 2 or k > 9:
print('Invalid k: ', k)
print('2<=k<=9, setting k=5 (default)')
k = 5
binning = schemes[scheme](values, k)
values = binning.yb
except ImportError:
print('PySAL not installed, setting map to default')
return binning
def plot_polygon(ax, poly, facecolor='red', edgecolor='black', alpha=0.5, linewidth=1):
""" Plot a single Polygon geometry """
from descartes.patch import PolygonPatch
a = np.asarray(poly.exterior)
# without Descartes, we could make a Patch of exterior
ax.add_patch(PolygonPatch(poly, facecolor=facecolor, alpha=alpha))
ax.plot(a[:, 0], a[:, 1], color=edgecolor, linewidth=linewidth)
for p in poly.interiors:
x, y = zip(*p.coords)
ax.plot(x, y, color=edgecolor, linewidth=linewidth)
def plot_multipolygon(ax, geom, facecolor='red', edgecolor='black', alpha=0.5, linewidth=1):
""" Can safely call with either Polygon or Multipolygon geometry
"""
if geom.type == 'Polygon':
plot_polygon(ax, geom, facecolor=facecolor, edgecolor=edgecolor, alpha=alpha, linewidth=linewidth)
elif geom.type == 'MultiPolygon':
for poly in geom.geoms:
plot_polygon(ax, poly, facecolor=facecolor, edgecolor=edgecolor, alpha=alpha, linewidth=linewidth)
import numpy as np
from geopandas.plotting import (plot_linestring, plot_point, norm_cmap)
def plot_dataframe(s, column=None, colormap=None, alpha=0.5,
categorical=False, legend=False, axes=None, scheme=None,
k=5, linewidth=1):
""" Plot a GeoDataFrame
Generate a plot of a GeoDataFrame with matplotlib. If a
column is specified, the plot coloring will be based on values
in that column. Otherwise, a categorical plot of the
geometries in the `geometry` column will be generated.
Parameters
----------
GeoDataFrame
The GeoDataFrame to be plotted. Currently Polygon,
MultiPolygon, LineString, MultiLineString and Point
geometries can be plotted.
column : str (default None)
The name of the column to be plotted.
categorical : bool (default False)
If False, colormap will reflect numerical values of the
column being plotted. For non-numerical columns (or if
column=None), this will be set to True.
colormap : str (default 'Set1')
The name of a colormap recognized by matplotlib.
alpha : float (default 0.5)
Alpha value for polygon fill regions. Has no effect for
lines or points.
legend : bool (default False)
Plot a legend (Experimental; currently for categorical
plots only)
axes : matplotlib.pyplot.Artist (default None)
axes on which to draw the plot
scheme : pysal.esda.mapclassify.Map_Classifier
Choropleth classification schemes
k : int (default 5)
Number of classes (ignored if scheme is None)
Returns
-------
matplotlib axes instance
"""
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.colors import Normalize
from matplotlib import cm
if column is None:
raise NotImplementedError
#return plot_series(s.geometry, colormap=colormap, alpha=alpha, axes=axes)
else:
if s[column].dtype is np.dtype('O'):
categorical = True
if categorical:
if colormap is None:
colormap = 'Set1'
categories = list(set(s[column].values))
categories.sort()
valuemap = dict([(j, v) for (v, j) in enumerate(categories)])
values = [valuemap[j] for j in s[column]]
else:
values = s[column]
if scheme is not None:
binning = __pysal_choro(values, scheme, k=k)
values = binning.yb
# set categorical to True for creating the legend
categorical = True
binedges = [binning.yb.min()] + binning.bins.tolist()
categories = ['{0:.2f} - {1:.2f}'.format(binedges[i], binedges[i+1]) for i in range(len(binedges)-1)]
cmap = norm_cmap(values, colormap, Normalize, cm)
if axes == None:
fig = plt.gcf()
fig.add_subplot(111, aspect='equal')
ax = plt.gca()
else:
ax = axes
for geom, value in zip(s.geometry, values):
if geom.type == 'Polygon' or geom.type == 'MultiPolygon':
plot_multipolygon(ax, geom, facecolor=cmap.to_rgba(value), alpha=alpha, linewidth=linewidth)
elif geom.type == 'LineString' or geom.type == 'MultiLineString':
raise NotImplementedError
#plot_multilinestring(ax, geom, color=cmap.to_rgba(value))
# TODO: color point geometries
elif geom.type == 'Point':
raise NotImplementedError
#plot_point(ax, geom, color=cmap.to_rgba(value))
if legend:
if categorical:
rowtitle = ["Moyenne"] * len(categories)
rowtitle[0] = "Faible"; rowtitle[-1] = u"Elevée"
text=zip(rowtitle, categories)
colors = []
for i in range(len(categories)):
color = list(cmap.to_rgba(i))
color[3] = alpha
colors.append(color)
colLabels = ["Exposition", u"Densité"]
tab=plt.table(cellText=text, colLabels=colLabels,
colWidths = [0.2,0.2], loc='upper left',
cellColours=zip(colors, colors))
else:
# TODO: show a colorbar
raise NotImplementedError
plt.draw()
return ax
if __name__ == "__main__":
import pysal as ps
import geopandas as gp
import matplotlib.pyplot as plt
pth = ps.examples.get_path("columbus.shp")
tracts = gp.GeoDataFrame.from_file(pth)
ax = plot_dataframe(tracts, column='CRIME', scheme='QUANTILES', k=5, colormap='OrRd', legend=True)
plt.show()
resulting in the following image:
your problem is in cmap :
ax = tracts.plot(......scheme='QUANTILES',cmap='jet')
and :
tab = ...... cellColours=plt.cm.jet(np.c_[valeur,valeur]))

AttributeError: draw_artist can only be used after an initial draw which caches the render

My requirement is to plot the data in polar graph. However I need to keep polar graph in particular angle to looks like "V" shape and data need to plotted in between the particular angle.
In python I don't find a solution to keep the polar graph in particular angle, Example : Graph should be display in between -60 to 60 degree radius. To achieve that I have looked into couple of existing examples and creating required polar graph with FloatingSubplot functions. However I am hitting the issue , when we try to use along with function animation function with blit=True. Error message is displayed is "AttributeError: draw_artist can only be used after an initial draw which caches the render"
Here is my code.
#
import matplotlib
matplotlib.use('Qt4Agg')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import matplotlib.animation as animation
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.transforms import Affine2D
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist import angle_helper
from mpl_toolkits.axisartist.grid_finder import MaxNLocator, DictFormatter
from mpl_toolkits.axisartist.floating_axes import GridHelperCurveLinear, FloatingSubplot
plt.close('all')
fig = plt.figure('Practice', dpi=100) # To set the fig title as pratice
ax1 = fig.add_subplot(2, 2, 1) # subplot for 1st plot
plt.ion()
ax1.grid(True)
def fractional_polar_axes(f, thlim=(0, 120), rlim=(0, 20), step=(30, 0.25),
thlabel='theta', rlabel='r', ticklabels=True, theta_offset=0, rlabels=None):
'''Return polar axes that adhere to desired theta (in deg) and r limits. steps for theta
and r are really just hints for the locators.'''
th0, th1 = thlim # deg
r0, r1 = rlim
thstep, rstep = step
tr_rotate = Affine2D().translate(theta_offset, 0)
# scale degrees to radians:
tr_scale = Affine2D().scale(np.pi / 180., 1.)
# pa = axes(polar="true") # Create a polar axis
pa = PolarAxes
tr = tr_rotate + tr_scale + pa.PolarTransform()
theta_grid_locator = angle_helper.LocatorDMS((th1 - th0) // thstep)
r_grid_locator = MaxNLocator((r1 - r0) // rstep)
theta_tick_formatter = angle_helper.FormatterDMS()
if rlabels:
rlabels = DictFormatter(rlabels)
grid_helper = GridHelperCurveLinear(tr,
extremes=(th0, th1, r0, r1),
grid_locator1=theta_grid_locator,
grid_locator2=r_grid_locator,
tick_formatter1=theta_tick_formatter,
tick_formatter2=rlabels)
a = FloatingSubplot(f, 222, grid_helper=grid_helper)
# a = Subplot(f,753, grid_helper=grid_helper)
# f.add_subplot(7,5,(3,34))
f.add_subplot(a)
# adjust x axis (theta):
print(a)
a.axis["bottom"].set_visible(False)
a.axis["top"].set_axis_direction("bottom") # tick direction
a.axis["top"].toggle(ticklabels=ticklabels, label=bool(thlabel))
a.axis["top"].major_ticklabels.set_axis_direction("top")
a.axis["top"].label.set_axis_direction("top")
a.axis["top"].major_ticklabels.set_pad(10)
# adjust y axis (r):
a.axis["left"].set_axis_direction("bottom") # tick direction
a.axis["right"].set_axis_direction("top") # tick direction
a.axis["left"].toggle(ticklabels=True, label=bool(rlabel))
# add labels:
a.axis["top"].label.set_text(thlabel)
a.axis["left"].label.set_text(rlabel)
# create a parasite axes whose transData is theta, r:
auxa = a.get_aux_axes(tr)
print(auxa)
# make aux_ax to have a clip path as in a?:
auxa.patch = a.patch
# this has a side effect that the patch is drawn twice, and possibly over some other
# artists. So, we decrease the zorder a bit to prevent this:
a.patch.zorder = -2
# add sector lines for both dimensions:
thticks = grid_helper.grid_info['lon_info'][0]
rticks = grid_helper.grid_info['lat_info'][0]
print(grid_helper.grid_info['lat_info'])
for th in thticks[1:-1]: # all but the first and last
auxa.plot([th, th], [r0, r1], ':', c='grey', zorder=-1, lw=0.5)
for ri, r in enumerate(rticks):
# plot first r line as axes border in solid black only if it isn't at r=0
if ri == 0 and r != 0:
ls, lw, color = 'solid', 1, 'black'
else:
ls, lw, color = 'dashed', 0.5, 'grey'
# From http://stackoverflow.com/a/19828753/2020363
auxa.add_artist(plt.Circle([0, 0], radius=r, ls=ls, lw=lw, color=color, fill=False,
transform=auxa.transData._b, zorder=-1))
return auxa
def animate(i):
global loopcount, th, r
th = th+.1
r = r+.1
datapoints.set_offsets(np.vstack((th,r)).T)
#print("in animate")
return datapoints,
if __name__ == '__main__':
r_locs = [0,5,10, 15, 20]
r_labels = ['0', '5', '10', '15', '20']
r_ticks = {loc: label for loc, label in zip(r_locs, r_labels)}
a1 = fractional_polar_axes(fig, thlim=(-60, 60), step=(20, 5),
theta_offset=90, rlabels=r_ticks)
th= 20
r=10
a1.scatter(th,r , c = 'r', alpha = 0.5, linewidths = '.2', s = 20) # plotting the line at thetha 20 and radius 10
datapoints = a1.scatter([], [], c='b', alpha = 0.5, linewidths = '.2', s = 20) # creating scatter line with given instruction,
ani = animation.FuncAnimation(fig, animate, frames=30, interval=20, blit=True)
plt.show(block=True)
#
"""
Above code is working perfectly fine with blit=False and also same solution working fine with line and scatter plotting in normal graph.
Please someone help me to resolve the issue.
"""

Scatter plot segregate clusters by color plotly python

I am using plotly (to be able to get point information when I hoover over) to visualise my clustered scatter plot. I am having trouble with assigning different colours to the clusters I have produced by using KMeans. When plotting this in matplotlib.pyplot (as plt) I use the following code:
plt.scatter(restult[:,0], result[:,1], c=cluster_labels
cluster_labels being:
n_clusters = 3
km = KMeans(n_clusters).fit(result)
labels = km.labels_
And it works totally fine, but I need the hoover info.
This is where I am at so far with plotly:
trace = go.Scatter(
x = result[:,0],
y = result[:,1],
mode = 'markers',
text = index, # I want to see the index of each point
)
data = [trace]
# Plot and embed in ipython notebook!
py.iplot(data, filename='basic-scatter')
I appreciate the help!
Let's use the iris data set
The labels from kmeans are used as colors (marker=dict(color=kmeans.labels_)), just like in matplotlib
from sklearn import datasets
from sklearn import cluster
import plotly
plotly.offline.init_notebook_mode()
iris = datasets.load_iris()
kmeans = cluster.KMeans(n_clusters=3,
random_state=42).fit(iris.data[:,0:2])
data = [plotly.graph_objs.Scatter(x=iris.data[:,0],
y=iris.data[:,1],
mode='markers',
marker=dict(color=kmeans.labels_)
)
]
plotly.offline.iplot(data)
Just to expand on Maxmimilian's method - if you're using sklearn version >=0.17 then you'll need to reshape your array since passing 1d arrays is deprecated in 0.17.
Here's an example with reshaping:
x = df[df.columns[1]]
x = x.values.reshape(-1,1)
y = df[df.columns[2]]
y = y.values.reshape(-1,1)
kmeans = cluster.KMeans(n_clusters = 3, random_state = 0).fit(x, y)
trace1 = go.Scatter(
x = df[df.columns[1]],
y = df[df.columns[2]],
mode = 'markers',
marker=dict(color=kmeans.labels_,
size = 7.5,
line = dict(width=2)
),
text = df.index,
name='Actual'
)