Reputation: 11
I am trying to create a choropleth map using Geoplot. The data comes in the form of a CSV and I am trying to create a choropleth map of a grid created over a specific area, in this case over the Tampa Florida region. I am wanting the final map to look like this
This is what I am attempting to create
where the color scheme is determined by the value of the column ['InitialResponse']. Each row represents a point coordinate that has a lat / long attribute in the Tampa area.
When I run
gplt.choropleth(dfsjoin, hue="InitialResponse", linewidth=.1, scheme=scheme, cmap='inferno_r', legend=True, edgecolor='black', ax=ax );
I get the following error:
`---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
File D:\Anaconda\lib\site-packages\geoplot\geoplot.py:982, in choropleth.<locals>.ChoroplethPlot.draw(self)
981 try: # Duck test for MultiPolygon.
--> 982 for subgeom in geom:
983 feature = GeopandasPolygonPatch(
984 subgeom, facecolor=color, **self.kwargs
985 )
TypeError: 'Point' object is not iterable
During handling of the above exception, another exception occurred:
AttributeError Traceback (most recent call last)
Cell In[94], line 1
----> 1 gplt.choropleth(dfsjoin,
2 hue="InitialResponse",
3 linewidth=.1,
4 scheme=scheme, cmap='inferno_r',
5 legend=True,
6 edgecolor='black',
7 ax=ax
8 )
File D:\Anaconda\lib\site-packages\geoplot\geoplot.py:1001, in choropleth(df, projection, hue, cmap, norm, scheme, legend, legend_kwargs, legend_labels, legend_values, extent, figsize, ax, **kwargs)
993 return ax
995 plot = ChoroplethPlot(
996 df, figsize=figsize, ax=ax, extent=extent, projection=projection,
997 hue=hue, scheme=scheme, cmap=cmap, norm=norm,
998 legend=legend, legend_values=legend_values, legend_labels=legend_labels,
999 legend_kwargs=legend_kwargs, **kwargs
1000 )
-> 1001 return plot.draw()
File D:\Anaconda\lib\site-packages\geoplot\geoplot.py:988, in choropleth.<locals>.ChoroplethPlot.draw(self)
986 ax.add_patch(feature)
987 except (TypeError, AssertionError): # Shapely Polygon.
--> 988 feature = GeopandasPolygonPatch(
989 geom, facecolor=color, **self.kwargs
990 )
991 ax.add_patch(feature)
993 return ax
File D:\Anaconda\lib\site-packages\geopandas\plotting.py:120, in _PolygonPatch(polygon, **kwargs)
116 from matplotlib.patches import PathPatch
117 from matplotlib.path import Path
119 path = Path.make_compound_path(
--> 120 Path(np.asarray(polygon.exterior.coords)[:, :2]),
121 *[Path(np.asarray(ring.coords)[:, :2]) for ring in polygon.interiors],
122 )
123 return PathPatch(path, **kwargs)
AttributeError: 'Point' object has no attribute 'exterior'
`
I have imported the CSV file as a pandas dataframe, and then converted it into a geopandas dataframe.
df_call_details = pd.read_csv("Model Rerun 50k Calls Masked.csv", index_col=False) df_call_details.keys()
Index(['Date', 'NatureCode', 'Address', 'CallID', 'FirstIn', 'ProcessingTime',
'InitialResponse', 'FullComplement', 'IsFullOverwhelm',
'IsPartialOverwhelm', 'HasResponses', 'Remarks', 'Level1Cause',
'Level2Cause', 'CustomContent1', 'RegionName', 'DispatchRule',
'ResponsePlan', 'XCoordinate', 'YCoordinate', 'Longitude', 'Latitude',
'Call (Custom)', 'FirstResponding (Custom)', 'FirstArrival (Custom)',
'FullComplement (Custom)', 'STEMI_Timestamp (Custom)', 'Grid (Custom)',
'EMS_Area (Custom)', 'City (Custom)', 'FDID (Custom)',
'Downgrade_ (Custom)', 'First_Due_Station (Custom)',
'Area_Chief (Custom)', 'PD_Determinant (Custom)', 'PD_Acuity (Custom)',
'PD_AnatPath (Custom)', 'Priority_Dispatch_Code (Custom)',
'COVID_Flag (Custom)', 'RIP_Flag (Custom)', 'Downgrade_Time (Custom)',
'Brain_Attack_Time (Custom)', 'STEMI_Time (Custom)',
'Alarm_Count (Custom)'],
dtype='object')
and then created a geo data frame
gdf_call_details = gpd.GeoDataFrame(df_call_details, geometry=gpd.points_from_xy(df_call_details['Longitude'],df_call_details['Latitude']))
gdf_call_details.crs='EPSG:4326'
Which appears to correctly create a geometry column with WKT.
I have also created a grid polygon in QGIS that I have imported using
gridData = gpd.read_file("shp\Grid Polygon 4326.shp")
I performed a spatial join between the dataframe containing the point polygons and the attribute I want to display and the grid
dfsjoin = gpd.sjoin(gdf_call_details, gridData, how='inner')
When I ran the code, I was expecting the grid to darken the areas of the grid based on the values of the Initial Response value for each row.
My code:
import pandas as pd, matplotlib, datetime, re, numpy as np, geopandas as gpd, geoplot as gplt, matplotlib.pyplot as plt, contextily as cx, folium, seaborn as sns
import cartopy.crs as ccrs, mapclassify as mc
import cartopy.io.img_tiles as cimgt
from IPython.display import display, HTML
display(HTML("<style>.container { width:95% !important; }</style>"))
gridData = gpd.read_file("shp\Grid Polygon 4326.shp")
gplt.polyplot(gridData, edgecolor='darkgrey', facecolor='lightgrey', linewidth=.3,
figsize=(12, 8))
df_call_details = pd.read_csv("Model Rerun 50k Calls Masked.csv", index_col=False)
df_call_details.keys()
gdf_call_details = gpd.GeoDataFrame(df_call_details, geometry=gpd.points_from_xy(df_call_details['Longitude'],df_call_details['Latitude']))
gdf_call_details.crs='EPSG:4326'
gdf_call_details.keys()
sns.histplot(responses['InitialResponse'], kde=True, stat='count')
dfsjoin = gpd.sjoin(gdf_call_details, gridData, how='inner')
dfsjoin.head(2)
scheme = mc.Quantiles(dfsjoin['InitialResponse'], k=15)
gplt.choropleth(dfsjoin,
hue="InitialResponse",
linewidth=.1,
scheme=scheme, cmap='inferno_r',
legend=True,
edgecolor='black',
ax=ax
);
I can see that it is taking issue with the point data, however I cannot figure out if I need to try and make it a polygon with points or if I made a mistake with the spatial join.
Upvotes: 0
Views: 436
Reputation: 11
I figured out a solution, but I think it's clunky and it runs slow.
gdfjoin = gpd.sjoin(gdf_call_details, gridData, how='inner', predicate='intersects')
gdfjoin.drop(columns='index_right', inplace=True)
gdf_shapes = gdfjoin.merge(gridData, on='id')
gdf_shapes = gpd.GeoDataFrame(gdf_shapes, geometry= gdf_shapes['geometry_y'])
gdf_shapes.drop(columns='geometry_y', inplace=True)
gdf_shapes.head(2)
I found that by performing a regular dataframe merge between the newly created dataframe that was created on the .sjoin and the dataframe containing my shape grid to merge the polygon geometry into my desired dataframe that I renamed gdf_shapes.
However, I couldn't find a way to perform a merge while remaining a Geopandas dataframe, instead it became a regular pandas dataframe so I recreated a geodataframe with geometry = geometry (the new column created when I merged the shapefile dataframe into my data dataframe).
If anyone has suggestions on how to perform these steps in the .sjoin function without having to perform a merge, drop columns, and recreate the geopandas dataframe I would love to know!
Upvotes: 0