Monkey 修补 pandas 和 matplotlib 以移除 df.plot() 的脊椎

Monkey patching pandas and matplotlib to remove spines for df.plot()

问题:

我正在尝试掌握 monkey patching and at the same time make a function to produce the perfect time-series plot. How can I include the following matplotlib functionality in pandas pandas.DataFrame.plot() 的概念?

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)

complete code at the end of the question


详情:


我认为 df.plot() 中的默认设置非常简洁,特别是如果您 运行 正在使用深色主题的 Jupyter Notebook,例如来自 dunovank 的 chesterish:

我想尽可能多地将它用于我的数据分析工作流程,但我真的很想像这样删除框架(或所谓的脊柱):

可以说,这是一个完美的时间序列图。但是 df.plot() 对此没有内置参数。最接近的似乎是 grid = False,但它会在相同的 运行:

中带走整个网格


我试过的


我知道我可以将 spine 代码段与 df.plot() 一起包装在一个函数中,所以我最终得到的是:

片段 1:

def plotPerfect(df, spline):

    ax = df.plot()

    if not spline:
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['left'].set_visible(False)

    return(ax)

plotPerfect(df = df, spline = False)

输出 1:

但是,就未来修订的灵活性和可读性而言,这是 "best" 的做法吗?如果我们谈论数百个地块,甚至是执行时间最快的?

我知道如何获得 df.plot() source,但从那里得到的一切让我感到困惑。那么 do 如何将这些设置包含在 df.plot 中?也许包装函数方法和猴子修补一样好?


包含完整代码和示例数据的代码段:


要 100% 重现示例,请将其粘贴到已激活 chesterish theme 的 Jupyter Notebook 单元格中:

# imports
import pandas as pd
import numpy as np
from jupyterthemes import jtplot

# Sample data
np.random.seed(123)
rows = 50
dfx = pd.DataFrame(np.random.randint(90,110,size=(rows, 1)), columns=['Variable Y'])
dfy = pd.DataFrame(np.random.randint(25,68,size=(rows, 1)), columns=[' Variable X'])
df = pd.concat([dfx,dfy], axis = 1)
jtplot.style()

# Plot with default settings
df.plot()

# Wrap df.plot() and matplotlib spine in a function
def plotPerfect(df, spline):

    ax = df.plot()

    if not spline:
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['left'].set_visible(False)

    return(ax)

# Plot the perfect time-series plot
plotPerfect(df = df, spline = False)

我会部分回答问题的自定义部分:不是通过单独的命令隐藏每个样条曲线,而是可以将它们放在 for 循环中,如下所示。

def plotPerfect(df, spline):
    ax = df.plot()

    if not spline:
        for i in ['top', 'right', 'bottom', 'left']:
            ax.spines[i].set_visible(False)

    return(ax)

# Plot the perfect time-series plot
plotPerfect(df = df, spline = False) 

如果您想隐藏所有四个书脊并且不想手动指定顶部、右侧等,您可以按如下所示以更自动化的方式进行。第一个允许您选择隐藏哪些。

def plotPerfect(df, spline):
    ax = df.plot()

    if not spline:
        for i in ax.spines.values():
            i.set_visible(False)

    return(ax)

另一个答案的替代解决方案是使用 plt.box(False) or ax.set_frame_on(False),这两个都隐藏了轴矩形补丁。

def plotPerfect(df, spline):
    ax = df.plot()

    if not spline:
        ax.set_frame_on(False)
        # plt.box(False)  # another possible option

    return ax

请注意,set_frame_on(False) 删除了背景使其透明,这可能不是我们想要的

这似乎是 xyproblem

猴子修补(Y)

问题要求猴子修补 pandas plot 功能以添加额外的功能。在这种情况下,这可以通过用它的自定义版本替换 pandas.plotting._core.plot_frame 函数来完成。

import pandas as pd
import pandas.plotting._core
orginal = pandas.plotting._core.plot_frame

def myplot(*args, **kwargs):
    spline = kwargs.pop("spline", True)
    ax = orginal(*args, **kwargs)
    ax.set_frame_on(spline)
    ax.grid(not spline)
    ax.tick_params(left=spline, bottom=spline)
    return ax

pandas.plotting._core.plot_frame = myplot

然后用作

df = pd.DataFrame([[0.1, 0.1], [0.9, 0.9]]).set_index(0)
df.plot()             ## Normal Plot
df.plot(spline=False) ## "Despined" Plot 

注意,如果在jupyter notebook中,带有猴子补丁的单元格不能运行多次,否则它会以递归结束。

造型(X)

以上对于改变情节的风格来说是相当矫枉过正的。人们应该使用 style options of matplotlib

mystyle = {"axes.spines.left" : False,
           "axes.spines.right" : False,
           "axes.spines.bottom" : False,
           "axes.spines.top" : False,
           "axes.grid" : True,
           "xtick.bottom" : False,
           "ytick.left" : False,}

然后将其应用于笔记本中的一些图,使用 plt.style.context 管理器,

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame([[0.1, 0.1], [0.9, 0.9]]).set_index(0)

df.plot()             ## Normal Plot

with plt.style.context(mystyle):
    df.plot()         ## "Despined" Plot 

或者,如果您想全局应用此样式,请更新 rcParams

plt.rcParams.update(mystyle)