format/round numerical legend label in GeoPandas

穿精又带淫゛_ 提交于 2019-12-11 10:07:26

问题


I'm looking for a way to format/round the numerical legend labels in those maps produced by .plot() function in GeoPandas. For example:

gdf.plot(column='pop2010', scheme='QUANTILES', k=4)

This gives me a legend with many decimal places:

I want the legend label to be integers.


回答1:


As I recently encountered the same issue, and a solution does not appear to be readily available on Stack Overflow or other sites, I thought I would post the approach I took in case it is useful.

First, a basic plot using the geopandas world map:

# load world data set    
world_orig = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
world = world_orig[(world_orig['pop_est'] > 0) & (world_orig['name'] != "Antarctica")].copy()
world['gdp_per_cap'] = world['gdp_md_est'] / world['pop_est']

# basic plot
fig = world.plot(column='pop_est', figsize=(12,8), scheme='fisher_jenks', 
                 cmap='YlGnBu', legend=True)
leg = fig.get_legend()
leg._loc = 3
plt.show()

The method I used relied on the get_texts() method for the matplotlib.legend.Legend object, then iterating over the items in leg.get_texts(), splitting the text element into the lower and upper bounds, and then creating a new string with formatting applied and setting this with the set_text() method.

# formatted legend
fig = world.plot(column='pop_est', figsize=(12,8), scheme='fisher_jenks', 
                 cmap='YlGnBu', legend=True)
leg = fig.get_legend()
leg._loc = 3

for lbl in leg.get_texts():
    label_text = lbl.get_text()
    lower = label_text.split()[0]
    upper = label_text.split()[2]
    new_text = f'{float(lower):,.0f} - {float(upper):,.0f}'
    lbl.set_text(new_text)

plt.show()

This is very much a 'trial and error' approach, so I wouldn't be surprised if there were a better way. Still, perhaps this will be helpful.




回答2:


Method 1:

GeoPandas uses PySal's classifier. Here's an example of quantiles map (k=5).

import matplotlib.pyplot as plt
import numpy as np
import pysal.viz.mapclassify as mc
import geopandas as gpd

# load dataset
path = gpd.datasets.get_path('naturalearth_lowres')
gdf = gpd.read_file(path)
# generate a random column
gdf['random_col'] = np.random.normal(100, 10, len(gdf))

# plot quantiles map
fig, ax = plt.subplots(figsize=(10, 10))
gdf.plot(column='random_col', scheme='quantiles', k=5, cmap='Blues',
         legend=True, legend_kwds=dict(loc=6), ax=ax)

This gives us:

Assume that we want to round the numbers in the legend. We can get the classification via .Quantiles() function in pysal.viz.mapclassify.

mc.Quantiles(gdf.random_col, k=5)

The function returns an object of classifiers.Quantiles:

Quantiles                 

 Lower            Upper              Count
==========================================
          x[i] <=  93.122               36
 93.122 < x[i] <=  98.055               35
 98.055 < x[i] <= 103.076               35
103.076 < x[i] <= 109.610               35
109.610 < x[i] <= 127.971               36

The object has an attribute of bins, which returns an array containing the upper bounds in all classes.

array([ 93.12248452,  98.05536454, 103.07553581, 109.60974753,
       127.97082465])

Thus, we can use this function to get all the bounds of the classes since the upper bound in a lower class equals the lower bound in the higher class. The only one missing is the lower bound in the lowest class, which equals the minimum value of the column you try to classify in your DataFrame.

Here's an example to round all numbers to integers:

# get all upper bounds
upper_bounds = mc.Quantiles(gdf.random_col, k=5).bins

# get and format all bounds
bounds = []
for index, upper_bound in enumerate(upper_bounds):
    if index == 0:
        lower_bound = gdf.random_col.min()
    else:
        lower_bound = upper_bounds[index-1]

    # format the numerical legend here
    bound = f'{lower_bound:.0f} - {upper_bound:.0f}'
    bounds.append(bound)

# get all the legend labels
legend_labels = ax.get_legend().get_texts()

# replace the legend labels
for bound, legend_label in zip(bounds, legend_labels):
    legend_label.set_text(bound)

We will eventually get:


Method 2:

In addition to GeoPandas' .plot() method, you can also consider .choropleth() function offered by geoplot in which you can easily use different types of scheme and number of classes while passing a legend_labels arg to modify the legend labels. For example,

import geopandas as gpd
import geoplot as gplt

path = gpd.datasets.get_path('naturalearth_lowres')
gdf = gpd.read_file(path)

legend_labels = ['< 2.4', '2.4 - 6', '6 - 15', '15 - 38', '38 - 140 M']
gplt.choropleth(gdf, hue='pop_est', cmap='Blues', scheme='quantiles',
                legend=True, legend_labels=legend_labels)

which gives you



来源:https://stackoverflow.com/questions/52503899/format-round-numerical-legend-label-in-geopandas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!