子类化 pandas.plotting._core.PlanePlot 导致 ValueError

Subclassing pandas.plotting._core.PlanePlot results in ValueError

背景

我正在尝试将我自己的绘图方法添加到下面示例中的 pandas.DataFrame.plot 我已经将 ScatterPlot from pandas and changed the name to VscatterPlot. Then I've added that class along with it's corresponding helper functionpandas.DataFrame.plot 复制并粘贴到它们各自的位置(见下文).

示例 class VscatterPlot

import pandas
import numpy as np


class VscatterPlot(pandas.plotting._core.PlanePlot):
    _kind = 'vscatter'

    def __init__(self, data, x, y, s=None, c=None, **kwargs):
        if s is None:
            # hide the matplotlib default for size, in case we want to change
            # the handling of this argument later
            s = 20
        
        super(VscatterPlot, self).__init__(data, x, y, s=s, **kwargs)
        
        if is_integer(c) and not self.data.columns.holds_integer():
            c = self.data.columns[c]
        
        self.c = c

    def _make_plot(self):
        x, y, c, data = self.x, self.y, self.c, self.data
        ax = self.axes[0]

        c_is_column = is_hashable(c) and c in self.data.columns

        # plot a colorbar only if a colormap is provided or necessary
        cb = self.kwds.pop('colorbar', self.colormap or c_is_column)

        # pandas uses colormap, matplotlib uses cmap.
        cmap = self.colormap or 'Greys'
        cmap = self.plt.cm.get_cmap(cmap)
        color = self.kwds.pop("color", None)
        
        if c is not None and color is not None:
            raise TypeError('Specify exactly one of `c` and `color`')
        
        elif c is None and color is None:
            c_values = self.plt.rcParams['patch.facecolor']
        
        elif color is not None:
            c_values = color
        
        elif c_is_column:
            c_values = self.data[c].values
        
        else:
            c_values = c

        if self.legend and hasattr(self, 'label'):
            label = self.label
        
        else:
            label = None
        
        scatter = ax.scatter(data[x].values, data[y].values, c=c_values,
                             label=label, cmap=cmap, **self.kwds)
        
        if cb:
            img = ax.collections[0]
            kws = dict(ax=ax)
            if self.mpl_ge_1_3_1():
                kws['label'] = c if c_is_column else ''
            self.fig.colorbar(img, **kws)

        if label is not None:
            self._add_legend_handle(scatter, label)
        
        else:
            self.legend = False

        errors_x = self._get_errorbars(label=x, index=0, yerr=False)
        errors_y = self._get_errorbars(label=y, index=0, xerr=False)
        
        if len(errors_x) > 0 or len(errors_y) > 0:
            err_kwds = dict(errors_x, **errors_y)
            err_kwds['ecolor'] = scatter.get_facecolor()[0]
            ax.errorbar(data[x].values, data[y].values,
                        linestyle='none', **err_kwds)

修改pandas.plotting._core

# Set VscatterPlot as an attribute of pandas.plotting._core
setattr(pandas.plotting._core, "VscatterPlot", VscatterPlot)

# Create the vscatter helper function
def vscatter(self, x, y, s=None, c=None, **kwds):
    return self(kind='vscatter', x=x, y=y, c=c, s=s, **kwds)

# Set the helper function
setattr(pandas.plotting._core.FramePlotMethods, "vscatter", vscatter)

# Append the class to pandas.plotting._core._klasses
pandas.plotting._core._klasses.append(pandas.plotting._core.VscatterPlot)

# Add the class to the pandas.plotting._core._plot_klass dict
pandas.plotting._core._plot_klass[VscatterPlot._kind] = pandas.plotting._core.VscatterPlot

测试

example = pandas.DataFrame(np.random.random((5,2)), columns=["x", "y"])
example.plot.vscatter(x="x", y="y")

输出

ValueError: 'vscatter' is not a valid plot kind

问题

我在这里错过了什么? vscatterpandas.plotting._core._plot_klass 那么为什么会抛出这个 ValueError?

pandas.plotting._core 中有两个列表决定如何实例化 class。您需要将 "vscatter" 放入这些列表中。

pandas.plotting._core._dataframe_kinds.append("vscatter")
pandas.plotting._core._all_kinds.append("vscatter")

除了缺少一些进口商品。以下代码

import matplotlib.pyplot as plt
import pandas
import numpy as np
from pandas.core.dtypes.common import is_integer, is_hashable


class VscatterPlot(pandas.plotting._core.PlanePlot):
    _kind = 'vscatter'

    def __init__(self, data, x, y, s=None, c=None, **kwargs):
        if s is None:
            # hide the matplotlib default for size, in case we want to change
            # the handling of this argument later
            s = 20

        super(VscatterPlot, self).__init__(data, x, y, s=s, **kwargs)

        if is_integer(c) and not self.data.columns.holds_integer():
            c = self.data.columns[c]

        self.c = c

    def _make_plot(self):
        x, y, c, data = self.x, self.y, self.c, self.data
        ax = self.axes[0]

        c_is_column = is_hashable(c) and c in self.data.columns

        # plot a colorbar only if a colormap is provided or necessary
        cb = self.kwds.pop('colorbar', self.colormap or c_is_column)

        # pandas uses colormap, matplotlib uses cmap.
        cmap = self.colormap or 'Greys'
        cmap = self.plt.cm.get_cmap(cmap)
        color = self.kwds.pop("color", None)

        if c is not None and color is not None:
            raise TypeError('Specify exactly one of `c` and `color`')

        elif c is None and color is None:
            c_values = self.plt.rcParams['patch.facecolor']

        elif color is not None:
            c_values = color

        elif c_is_column:
            c_values = self.data[c].values

        else:
            c_values = c

        if self.legend and hasattr(self, 'label'):
            label = self.label

        else:
            label = None

        scatter = ax.scatter(data[x].values, data[y].values, c=c_values,
                             label=label, cmap=cmap, **self.kwds)

        if cb:
            img = ax.collections[0]
            kws = dict(ax=ax)
            if self.mpl_ge_1_3_1():
                kws['label'] = c if c_is_column else ''
            self.fig.colorbar(img, **kws)

        if label is not None:
            self._add_legend_handle(scatter, label)

        else:
            self.legend = False

        errors_x = self._get_errorbars(label=x, index=0, yerr=False)
        errors_y = self._get_errorbars(label=y, index=0, xerr=False)

        if len(errors_x) > 0 or len(errors_y) > 0:
            err_kwds = dict(errors_x, **errors_y)
            err_kwds['ecolor'] = scatter.get_facecolor()[0]
            ax.errorbar(data[x].values, data[y].values,
                        linestyle='none', **err_kwds)

#Amending the pandas.plotting._core
# Set VscatterPlot as an attribute of pandas.plotting._core
setattr(pandas.plotting._core, "VscatterPlot", VscatterPlot)

# Create the vscatter helper function
def vscatter(self, x, y, s=None, c=None, **kwds):
    return self(kind='vscatter', x=x, y=y, c=c, s=s, **kwds)

# Set the helper function
setattr(pandas.plotting._core.FramePlotMethods, "vscatter", vscatter)

# Append the class to pandas.plotting._core._klasses
pandas.plotting._core._klasses.append(pandas.plotting._core.VscatterPlot)

# Add the class to the pandas.plotting._core._plot_klass dict
pandas.plotting._core._plot_klass[VscatterPlot._kind] = pandas.plotting._core.VscatterPlot

pandas.plotting._core._dataframe_kinds.append("vscatter")
pandas.plotting._core._all_kinds.append("vscatter")
#Testing
example = pandas.DataFrame(np.random.random((5,2)), columns=["x", "y"])
example.plot.vscatter(x="x", y="y")

plt.show()

产生这个输出