Search code examples
pythonmatplotlibdata-visualizationseaborngeopandas

Can't plot GeoDataFrame to show change in the same area over time


I have a geopandas.geodataframe.GeoDataFrame with 3 columns: the_geom, Property Crime Rate, Year. Note that the_geom here is just a polygon of Virginia. I want to show this crime rate change over time. Here's what I have so far:

sns.set_style("white")
fig, axs = plt.subplots(nrows=3, ncols=4, sharex=True, sharey=True, figsize=(10,5))
fig.tight_layout(pad=3.0)

i = 0
for year in range(1994, 2015, 2):
    subdf = c7[c7['Year']==year]

    divider = make_axes_locatable(axs[i//4, i%4])
    cax = divider.append_axes("right", size="5%", pad=0)
    axs[i//4, i%4].set_title(str(year))

    subdf.plot(ax=axs[i//4, i%4], 
           column='Property crime rate',
           cmap='RdBu_r',
           cax=cax,
           legend=True
          )
    axs[i//4, i%4].axis('off')
    i+=1

axs[i//4, i%4].axis('off')

enter image description here

The problem is that all the states are the same color because their legends on the right are using different scales. I want them all to share the same scale so you can see the color changes over time. Something like sns.FacetGrid() seems like it could work, but I can't get it to work with GeoDataFrames. When I use plt.plot below, it doesn't show the polygon:

g = sns.FacetGrid(c7, col="Year", hue="Property crime rate")
g = (g.map(plt.plot, "Property crime rate").add_legend()) 

enter image description here

When I try replacing plt.plot with gpd.GeoDataFrame.plot I get the following error:

g = sns.FacetGrid(c7, col="Year", hue="Property crime rate")
g = (g.map(gpd.GeoDataFrame.plot, "Property crime rate").add_legend())

AttributeError: 'Series' object has no attribute 'geometry'

Can anyone help?


Solution

  • Well I found the solution after a lot of tinkering. I didn't try @martinfleis vmin and vmax, so that might work too. I abandoned plt.legend() and went with plt.colorbar(), which seemed to work great.

    import matplotlib
    import matplotlib.cm as cm #color mapping function
    import matplotlib.pyplot as plt
    from mpl_toolkits.axes_grid1.inset_locator import inset_axes
    import seaborn as sns
    %matplotlib inline
    
    sns.set_style("white")
    
    # c7 is my geodataframe
    va = c7[c7.State=='Virginia'].sort_values(by='Year').copy(deep=True)
    va.reset_index(drop=True, inplace=True)
    
    minima = min(va["Property crime rate"])
    maxima = max(va["Property crime rate"])
    norm = matplotlib.colors.Normalize(vmin=minima, vmax=maxima, clip=True)
    mapper = cm.ScalarMappable(norm=norm, cmap=cm.cool)
    
    fig, ax = plt.subplots(1, 1, figsize=(15,8))
    ax.axis('off')
    
    axinset1 = inset_axes(ax, loc='center right', borderpad=-7, # negative value borderpad pushes it out
                          width="2%", height="40%") # width/height = X% of parent_bbox width/height
    cb = plt.colorbar(cax=axinset1, mappable=mapper)
    cb.set_label("Property Crime Rate", labelpad=20)
    fig.suptitle('Virginia\'s Property Crime Rate Over Time', y=1, fontsize=20) # y<1 brings down, y>1 brings up
    
    for idx, row in va.iterrows():
        ax = fig.add_subplot(3, 4, idx+1)
        ax.set_title("{}".format(row.Year))
        va[va.Year == row.Year].plot(ax=ax, color=mapper.to_rgba(row["Property crime rate"]))
        ax.axis('off')
    ax.axis('off')
    plt.show()
    

    Yielded: enter image description here