Как сделать табличную легенду для графика геопанд

Я рисую хороплетную карту, используя геопанды, и мне нужно построить настраиваемую табличную легенду. Ответ на этот вопрос показывает, как получить табличная легенда для контурного графика. И я использую его в приведенном ниже коде:

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()

И вот результат:  введите описание изображения здесь

В общем, как видите, нет связи между цветами классов на карте и в таблице. Мне нужны именно те цвета, которые указаны в таблице на карте. «Значение NG», показанное в легенде, должно быть извлечено из столбца «ПЛОТНОСТЬ», который я рисую.

Однако, поскольку у меня нет контурного графика, из которого можно извлечь цветовую карту, я не понимаю, как связать табличную легенду и цвета карты.


person Mar    schedule 16.06.2017    source источник
comment
Вы используете HOT colormap для построения таблицы. Я подозреваю, что Geodata по умолчанию использует другую цветовую карту. Попробуйте указать одну и ту же цветовую карту для обеих операций.   -  person Imanol Luengo    schedule 16.06.2017
comment
Несмотря на то, что это не работает, я считаю, что что-то следует изменить в этой строке: text = [[Faible, Ng ‹1,5], [Moyenne, 1,5‹ Ng ‹2,5], [uElevee, Ng› 2, 5]]. Поскольку эти значения NG должны быть извлечены из столбца «Плотность», который я рисую.   -  person Mar    schedule 16.06.2017
comment
Вы можете добавить цветовую карту к вашему графику участков, чтобы она соответствовала вашей легенде, вот так ax = tracts.plot(column='Density', scheme='QUANTILES', cmap='hot')   -  person Bob Haffner    schedule 16.06.2017
comment
нет, дело не в сопоставлении, а в связывании карты с таблицей и извлечении интервалов таблицы из нанесенного столбца «Плотность».   -  person Mar    schedule 17.06.2017


Ответы (2)


Примечание. Этот ответ устарел. Современные геопанды позволяют использовать обычную легенду через аргумент legend=True. Я все еще храню его здесь для справки или на тот случай, если кому-то понадобится настоящая табличная легенда.


Сюжет геопанд не поддерживает добавление легенды. Он также не предоставляет доступа к своему объекту построения и возвращает только оси с фигурами в виде многоугольников. (Он даже не предоставляет PolyCollection для работы). Поэтому создание нормальной легенды для такого сюжета - большая утомительная работа.

К счастью, часть этой работы уже выполняется в примере записной книжки Классификация картограмм с помощью PySAL и GeoPandas - с легендой

Поэтому нам нужно взять этот код и реализовать настраиваемую табличную легенду, которая взята из этого ответа.

Вот полный код:

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()

в результате получается следующее изображение:

введите здесь описание изображения

person ImportanceOfBeingErnest    schedule 17.06.2017
comment
О, супер, это то, что я ищу, спасибо за помощь, усилия, объяснения и код, вы только что спасли мне жизнь! - person Mar; 17.06.2017
comment
Хотя я рад, что это помогает вам, я искренне надеюсь, что ваша жизнь не зависит от кода для легенд карт. ;-) - person ImportanceOfBeingErnest; 17.06.2017

ваша проблема в cmap:

ax = tracts.plot(......scheme='QUANTILES',cmap='jet')

а также :

tab = ...... cellColours=plt.cm.jet(np.c_[valeur,valeur]))
person Mius    schedule 23.07.2020