图表可视化#

注意

下面的示例假设您正在使用 Jupyter

本节演示了通过图表进行可视化。有关表格数据的可视化信息,请参阅 表格可视化 一节。

我们使用引用 matplotlib API 的标准约定

In [1]: import matplotlib.pyplot as plt

In [2]: plt.close("all")

我们在 pandas 中提供了基础功能,可以轻松创建美观的图表。有关超出此处介绍的基础功能的更多可视化库,请参阅 生态系统页面

注意

所有对 np.random 的调用均使用种子 123456 进行初始化。

基本绘图:plot#

我们将演示基础知识,有关一些高级策略,请参阅 cookbook (速查手册)

Series 和 DataFrame 上的 plot 方法只是对 plt.plot() 的简单封装。

In [3]: np.random.seed(123456)

In [4]: ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))

In [5]: ts = ts.cumsum()

In [6]: ts.plot();
../_images/series_plot_basic.png

如果索引包含日期,它会调用 gcf().autofmt_xdate() 尝试按上述方式美化 x 轴的格式。

在 DataFrame 上,plot() 是一种方便的方式,可以绘制所有带标签的列。

In [7]: df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index, columns=list("ABCD"))

In [8]: df = df.cumsum()

In [9]: plt.figure();

In [10]: df.plot();
../_images/frame_plot_basic.png

您可以使用 plot() 中的 xy 关键字来绘制某一列与另一列的关系图。

In [11]: df3 = pd.DataFrame(np.random.randn(1000, 2), columns=["B", "C"]).cumsum()

In [12]: df3["A"] = pd.Series(list(range(len(df))))

In [13]: df3.plot(x="A", y="B");
../_images/df_plot_xy.png

注意

更多格式和样式选项,请参阅下面的 格式设置

其他图表#

绘图方法支持除默认折线图之外的多种图表样式。这些方法可以作为 plot()kind 关键字参数提供,包括

例如,可以通过以下方式创建柱状图

In [14]: plt.figure();

In [15]: df.iloc[5].plot(kind="bar");
../_images/bar_plot_ex.png

您也可以使用方法 DataFrame.plot.<kind> 来创建这些其他图表,而不是提供 kind 关键字参数。这使得更容易发现绘图方法及其使用的具体参数。

In [16]: df = pd.DataFrame()

In [17]: df.plot.<TAB>  # noqa: E225, E999
df.plot.area     df.plot.barh     df.plot.density  df.plot.hist     df.plot.line     df.plot.scatter
df.plot.bar      df.plot.box      df.plot.hexbin   df.plot.kde      df.plot.pie

除了这些 kind 类型之外,还有 DataFrame.hist()DataFrame.boxplot() 方法,它们使用独立的接口。

最后,pandas.plotting 中有几个 绘图函数 接收 SeriesDataFrame 作为参数。这些函数包括

图表还可以附加 误差条表格

柱状图#

对于带标签的非时间序列数据,您可能希望生成柱状图

In [18]: plt.figure();

In [19]: df.iloc[5].plot.bar();

In [20]: plt.axhline(0, color="k");
../_images/bar_plot_ex.png

调用 DataFrame 的 plot.bar() 方法会生成多列柱状图

In [21]: df2 = pd.DataFrame(np.random.rand(10, 4), columns=["a", "b", "c", "d"])

In [22]: df2.plot.bar();
../_images/bar_plot_multi_ex.png

要生成堆积柱状图,请传递 stacked=True

In [23]: df2.plot.bar(stacked=True);
../_images/bar_plot_stacked_ex.png

要获得水平柱状图,请使用 barh 方法

In [24]: df2.plot.barh(stacked=True);
../_images/barh_plot_stacked_ex.png

直方图#

可以使用 DataFrame.plot.hist()Series.plot.hist() 方法绘制直方图。

In [25]: df4 = pd.DataFrame(
   ....:     {
   ....:         "a": np.random.randn(1000) + 1,
   ....:         "b": np.random.randn(1000),
   ....:         "c": np.random.randn(1000) - 1,
   ....:     },
   ....:     columns=["a", "b", "c"],
   ....: )
   ....: 

In [26]: plt.figure();

In [27]: df4.plot.hist(alpha=0.5);
../_images/hist_new.png

可以使用 stacked=True 堆叠直方图。可以使用 bins 关键字更改分箱大小。

In [28]: plt.figure();

In [29]: df4.plot.hist(stacked=True, bins=20);
../_images/hist_new_stacked.png

您可以传递 matplotlib hist 支持的其他关键字。例如,可以通过设置 orientation='horizontal'cumulative=True 来绘制水平和累积直方图。

In [30]: plt.figure();

In [31]: df4["a"].plot.hist(orientation="horizontal", cumulative=True);
../_images/hist_new_kwargs.png

更多信息请参阅 hist 方法和 matplotlib hist 文档

用于绘制直方图的现有接口 DataFrame.hist 仍然可以使用。

In [32]: plt.figure();

In [33]: df["A"].diff().hist();
../_images/hist_plot_ex.png

DataFrame.hist() 在多个子图上绘制各列的直方图

In [34]: plt.figure();

In [35]: df.diff().hist(color="k", alpha=0.5, bins=50);
../_images/frame_hist_ex.png

可以指定 by 关键字来绘制分组直方图

In [36]: data = pd.Series(np.random.randn(1000))

In [37]: data.hist(by=np.random.randint(0, 4, 1000), figsize=(6, 4));
../_images/grouped_hist.png

此外,也可以在 DataFrame.plot.hist() 中指定 by 关键字。

版本 1.4.0 中有所改变。

In [38]: data = pd.DataFrame(
   ....:     {
   ....:         "a": np.random.choice(["x", "y", "z"], 1000),
   ....:         "b": np.random.choice(["e", "f", "g"], 1000),
   ....:         "c": np.random.randn(1000),
   ....:         "d": np.random.randn(1000) - 1,
   ....:     },
   ....: )
   ....: 

In [39]: data.plot.hist(by=["a", "b"], figsize=(10, 5));
../_images/grouped_hist_by.png

箱线图#

可以通过调用 Series.plot.box()DataFrame.plot.box()DataFrame.boxplot() 来绘制箱线图,以可视化每列中值的分布。

例如,这里是一个箱线图,表示对 [0,1) 上的均匀随机变量进行五次试验,每次试验包含 10 个观测值。

In [40]: df = pd.DataFrame(np.random.rand(10, 5), columns=["A", "B", "C", "D", "E"])

In [41]: df.plot.box();
../_images/box_plot_new.png

可以通过传递 color 关键字来为箱线图着色。您可以传递一个 dict,其键为 boxeswhiskersmedianscaps。如果 dict 中缺少某些键,则使用默认颜色渲染相应的艺术家对象。此外,箱线图还有 sym 关键字用于指定异常值样式。

当您通过 color 关键字传递其他类型的参数时,这些参数将直接传递给 matplotlib,用于对所有 boxeswhiskersmedianscaps 进行着色。

颜色将应用于绘制的每个箱体。如果您想要更复杂的着色,可以通过传递 return_type 获取每个绘制的艺术家对象。

In [42]: color = {
   ....:     "boxes": "DarkGreen",
   ....:     "whiskers": "DarkOrange",
   ....:     "medians": "DarkBlue",
   ....:     "caps": "Gray",
   ....: }
   ....: 

In [43]: df.plot.box(color=color, sym="r+");
../_images/box_new_colorize.png

此外,您还可以传递 matplotlib boxplot 支持的其他关键字。例如,可以通过 vert=Falsepositions 关键字绘制水平和自定义位置的箱线图。

In [44]: df.plot.box(vert=False, positions=[1, 4, 5, 6, 8]);
../_images/box_new_kwargs.png

更多信息请参阅 boxplot 方法和 matplotlib boxplot 文档

用于绘制箱线图的现有接口 DataFrame.boxplot 仍然可以使用。

In [45]: df = pd.DataFrame(np.random.rand(10, 5))

In [46]: plt.figure();

In [47]: bp = df.boxplot()
../_images/box_plot_ex.png

您可以使用 by 关键字参数创建分组的箱线图。例如,

In [48]: df = pd.DataFrame(np.random.rand(10, 2), columns=["Col1", "Col2"])

In [49]: df["X"] = pd.Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"])

In [50]: plt.figure();

In [51]: bp = df.boxplot(by="X")
../_images/box_plot_ex2.png

您也可以传递需要绘制的列的子集,并按多列进行分组

In [52]: df = pd.DataFrame(np.random.rand(10, 3), columns=["Col1", "Col2", "Col3"])

In [53]: df["X"] = pd.Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"])

In [54]: df["Y"] = pd.Series(["A", "B", "A", "B", "A", "B", "A", "B", "A", "B"])

In [55]: plt.figure();

In [56]: bp = df.boxplot(column=["Col1", "Col2"], by=["X", "Y"])
../_images/box_plot_ex3.png

您也可以使用 DataFrame.plot.box() 创建分组,例如

版本 1.4.0 中有所改变。

In [57]: df = pd.DataFrame(np.random.rand(10, 3), columns=["Col1", "Col2", "Col3"])

In [58]: df["X"] = pd.Series(["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"])

In [59]: plt.figure();

In [60]: bp = df.plot.box(column=["Col1", "Col2"], by="X")
../_images/box_plot_ex4.png

boxplot 中,返回类型可以通过 return_type 关键字控制。有效选项为 {"axes", "dict", "both", None}。使用 DataFrame.boxplotby 关键字创建的分面也会影响输出类型。

return_type

分面

输出类型

None

axes

None

axes 的二维 ndarray

'axes'

axes

'axes'

axes 的 Series

'dict'

艺术家对象的 dict

'dict'

艺术家对象 dict 的 Series

'both'

namedtuple

'both'

namedtuple 的 Series

Groupby.boxplot 总是返回一个 return_typeSeries

In [61]: np.random.seed(1234)

In [62]: df_box = pd.DataFrame(np.random.randn(50, 2))

In [63]: df_box["g"] = np.random.choice(["A", "B"], size=50)

In [64]: df_box.loc[df_box["g"] == "B", 1] += 3

In [65]: bp = df_box.boxplot(by="g")
../_images/boxplot_groupby.png

上面的子图首先按数值列分割,然后按 g 列的值分割。下面的子图首先按 g 的值分割,然后按数值列分割。

In [66]: bp = df_box.groupby("g").boxplot()
../_images/groupby_boxplot_vis.png

面积图#

您可以使用 Series.plot.area()DataFrame.plot.area() 创建面积图。面积图默认为堆积图。要生成堆积面积图,每列的值必须全部为正或全部为负。

当输入数据包含 NaN 时,它会自动填充为 0。如果您想删除或使用不同的值填充,请在调用 plot 之前使用 dataframe.dropna()dataframe.fillna()

In [67]: df = pd.DataFrame(np.random.rand(10, 4), columns=["a", "b", "c", "d"])

In [68]: df.plot.area();
../_images/area_plot_stacked.png

要生成非堆积图,请传递 stacked=False。除非另有指定,Alpha 值设置为 0.5

In [69]: df.plot.area(stacked=False);
../_images/area_plot_unstacked.png

散点图#

可以使用 DataFrame.plot.scatter() 方法绘制散点图。散点图需要数值列作为 x 轴和 y 轴。这些可以通过 xy 关键字指定。

In [70]: df = pd.DataFrame(np.random.rand(50, 4), columns=["a", "b", "c", "d"])

In [71]: df["species"] = pd.Categorical(
   ....:     ["setosa"] * 20 + ["versicolor"] * 20 + ["virginica"] * 10
   ....: )
   ....: 

In [72]: df.plot.scatter(x="a", y="b");
../_images/scatter_plot.png

要在单个坐标轴中绘制多个列组,请重复调用 plot 方法并指定目标 ax。建议指定 colorlabel 关键字以区分各组。

In [73]: ax = df.plot.scatter(x="a", y="b", color="DarkBlue", label="Group 1")

In [74]: df.plot.scatter(x="c", y="d", color="DarkGreen", label="Group 2", ax=ax);
../_images/scatter_plot_repeated.png

关键字 c 可以指定为列名,以便为每个点提供颜色

In [75]: df.plot.scatter(x="a", y="b", c="c", s=50);
../_images/scatter_plot_colored.png

如果将分类列传递给 c,则会生成离散的颜色条

版本 1.3.0 新增。

In [76]: df.plot.scatter(x="a", y="b", c="species", cmap="viridis", s=50);
../_images/scatter_plot_categorical.png

您可以传递 matplotlib scatter 支持的其他关键字。下面的示例展示了如何使用 DataFrame 的一列作为气泡大小来绘制气泡图。

In [77]: df.plot.scatter(x="a", y="b", s=df["c"] * 200);
../_images/scatter_plot_bubble.png

更多信息请参阅 scatter 方法和 matplotlib scatter 文档

六边形分箱图#

您可以使用 DataFrame.plot.hexbin() 创建六边形分箱图。如果您的数据过于密集,无法单独绘制每个点,则六边形分箱图可以作为散点图的有用替代方案。

In [78]: df = pd.DataFrame(np.random.randn(1000, 2), columns=["a", "b"])

In [79]: df["b"] = df["b"] + np.arange(1000)

In [80]: df.plot.hexbin(x="a", y="b", gridsize=25);
../_images/hexbin_plot.png

一个有用的关键字参数是 gridsize;它控制 x 方向上六边形的数量,默认为 100。较大的 gridsize 意味着更多、更小的分箱。

默认情况下,计算每个 (x, y) 点周围计数的直方图。您可以通过将值传递给 Creduce_C_function 参数来指定其他聚合方式。C 指定每个 (x, y) 点的值,而 reduce_C_function 是一个单参数函数,它将分箱中的所有值汇总为一个数字(例如 meanmaxsumstd)。在这个示例中,位置由列 ab 给出,而值由列 z 给出。分箱使用 NumPy 的 max 函数进行聚合。

In [81]: df = pd.DataFrame(np.random.randn(1000, 2), columns=["a", "b"])

In [82]: df["b"] = df["b"] + np.arange(1000)

In [83]: df["z"] = np.random.uniform(0, 3, 1000)

In [84]: df.plot.hexbin(x="a", y="b", C="z", reduce_C_function=np.max, gridsize=25);
../_images/hexbin_plot_agg.png

更多信息请参阅 hexbin 方法和 matplotlib hexbin 文档

饼图#

您可以使用 DataFrame.plot.pie()Series.plot.pie() 创建饼图。如果您的数据包含任何 NaN,它们会自动填充为 0。如果您的数据中有任何负值,则会引发 ValueError

In [85]: series = pd.Series(3 * np.random.rand(4), index=["a", "b", "c", "d"], name="series")

In [86]: series.plot.pie(figsize=(6, 6));
../_images/series_pie_plot.png

对于饼图,最好使用方形图,即图表纵横比为 1。您可以创建宽度和高度相等的图形,或者在绘制后通过对返回的 axes 对象调用 ax.set_aspect('equal') 来强制纵横比相等。

请注意,使用 DataFrame 绘制饼图需要您通过 y 参数指定目标列或指定 subplots=True。当指定 y 时,将绘制选定列的饼图。如果指定 subplots=True,则为每列绘制饼图作为子图。默认情况下,每个饼图中都会绘制图例;指定 legend=False 以隐藏图例。

In [87]: df = pd.DataFrame(
   ....:     3 * np.random.rand(4, 2), index=["a", "b", "c", "d"], columns=["x", "y"]
   ....: )
   ....: 

In [88]: df.plot.pie(subplots=True, figsize=(8, 4));
../_images/df_pie_plot.png

您可以使用 labelscolors 关键字来指定每个扇形的标签和颜色。

警告

大多数 pandas 图表使用 labelcolor 参数(请注意它们没有复数形式的“s”)。为了与 matplotlib.pyplot.pie() 保持一致,您必须使用 labelscolors

如果您想隐藏扇形标签,请指定 labels=None。如果指定了 fontsize,则该值将应用于扇形标签。此外,也可以使用 matplotlib.pyplot.pie() 支持的其他关键字。

In [89]: series.plot.pie(
   ....:     labels=["AA", "BB", "CC", "DD"],
   ....:     colors=["r", "g", "b", "c"],
   ....:     autopct="%.2f",
   ....:     fontsize=20,
   ....:     figsize=(6, 6),
   ....: );
   ....: 
../_images/series_pie_plot_options.png

如果您传入的总和小于 1.0 的值,它们将被重新缩放,使其总和为 1。

In [90]: series = pd.Series([0.1] * 4, index=["a", "b", "c", "d"], name="series2")

In [91]: series.plot.pie(figsize=(6, 6));
../_images/series_pie_plot_semi.png

更多信息请参阅 matplotlib pie 文档

绘制包含缺失数据的图表#

pandas 在绘制包含缺失数据的 DataFramesSeries 时力求务实。缺失值会根据图表类型被丢弃、忽略或填充。

图表类型

NaN 处理方式

折线图

在 NaN 处留空

折线图 (堆积)

填充 0

柱状图

填充 0

散点图

丢弃 NaN

直方图

丢弃 NaN (按列)

箱线图

丢弃 NaN (按列)

面积图

填充 0

KDE

丢弃 NaN (按列)

六边形分箱图

丢弃 NaN

饼图

填充 0

如果这些默认处理方式不是您想要的,或者您想明确指定如何处理缺失值,请考虑在绘图之前使用 fillna()dropna()

绘图工具#

这些函数可以从 pandas.plotting 导入,并接收一个 SeriesDataFrame 作为参数。

散点矩阵图#

您可以使用 pandas.plotting 中的 scatter_matrix 方法创建散点图矩阵

In [92]: from pandas.plotting import scatter_matrix

In [93]: df = pd.DataFrame(np.random.randn(1000, 4), columns=["a", "b", "c", "d"])

In [94]: scatter_matrix(df, alpha=0.2, figsize=(6, 6), diagonal="kde");
../_images/scatter_matrix_kde.png

密度图#

您可以使用 Series.plot.kde()DataFrame.plot.kde() 方法创建密度图。

In [95]: ser = pd.Series(np.random.randn(1000))

In [96]: ser.plot.kde();
../_images/kde_plot.png

Andrews 曲线#

Andrews 曲线可以将多元数据绘制成大量曲线,这些曲线是使用样本的属性作为傅里叶级数的系数创建的,更多信息请参阅 维基百科条目。通过为每个类别分配不同的颜色,可以可视化数据聚类。属于同一类别的样本曲线通常会靠得更近,并形成更大的结构。

注意:“Iris”数据集可从此处获取。

In [97]: from pandas.plotting import andrews_curves

In [98]: data = pd.read_csv("data/iris.data")

In [99]: plt.figure();

In [100]: andrews_curves(data, "Name");
../_images/andrews_curves.png

平行坐标#

平行坐标是一种用于绘制多元数据的绘图技术,请参阅 维基百科条目 获取介绍。平行坐标可以帮助观察数据中的聚类并直观估计其他统计量。使用平行坐标时,数据点表示为连接的线段。每条垂直线代表一个属性。一组连接的线段代表一个数据点。倾向于聚类的点会显示得更靠近。

In [101]: from pandas.plotting import parallel_coordinates

In [102]: data = pd.read_csv("data/iris.data")

In [103]: plt.figure();

In [104]: parallel_coordinates(data, "Name");
../_images/parallel_coordinates.png

滞后图#

滞后图用于检查数据集或时间序列是否随机。随机数据在滞后图中不应表现出任何结构。非随机结构意味着底层数据不是随机的。可以传递 lag 参数,当 lag=1 时,图表基本上是 data[:-1]data[1:] 的关系图。

In [105]: from pandas.plotting import lag_plot

In [106]: plt.figure();

In [107]: spacing = np.linspace(-99 * np.pi, 99 * np.pi, num=1000)

In [108]: data = pd.Series(0.1 * np.random.rand(1000) + 0.9 * np.sin(spacing))

In [109]: lag_plot(data);
../_images/lag_plot.png

自相关图#

自相关图常用于检查时间序列的随机性。这是通过计算不同时间滞后下数据值的自相关来实现的。如果时间序列是随机的,则对于任何和所有时间滞后间隔,这些自相关值应接近零。如果时间序列是非随机的,则一个或多个自相关值将显着非零。图中显示的水平线对应于 95% 和 99% 的置信区间。虚线是 99% 的置信区间。有关自相关图的更多信息,请参阅 维基百科条目

In [110]: from pandas.plotting import autocorrelation_plot

In [111]: plt.figure();

In [112]: spacing = np.linspace(-9 * np.pi, 9 * np.pi, num=1000)

In [113]: data = pd.Series(0.7 * np.random.rand(1000) + 0.3 * np.sin(spacing))

In [114]: autocorrelation_plot(data);
../_images/autocorrelation_plot.png

自助法图#

自助法图用于直观评估统计量的不确定性,例如均值、中位数、中范围等。从数据集中随机选择指定大小的子集,计算该子集的相应统计量,并重复该过程指定次数。生成的图表和直方图构成了自助法图。

In [115]: from pandas.plotting import bootstrap_plot

In [116]: data = pd.Series(np.random.rand(1000))

In [117]: bootstrap_plot(data, size=50, samples=500, color="grey");
../_images/bootstrap_plot.png

RadViz#

RadViz 是一种可视化多元数据的方法。它基于一个简单的弹簧张力最小化算法。基本思想是在平面上设置一堆点。在本例中,这些点均匀分布在一个单位圆上。每个点代表一个属性。然后,假设数据集中的每个样本都通过弹簧连接到这些点,弹簧的刚度与该属性的数值成比例(它们被归一化到单位区间)。样本在平面中稳定下来的点(即作用在样本上的力达到平衡的点)就是绘制代表该样本的点的位置。根据样本所属的类别,它会被着上不同的颜色。更多信息请参阅 R 包 Radviz

注意:“Iris”数据集可从此处获取。

In [118]: from pandas.plotting import radviz

In [119]: data = pd.read_csv("data/iris.data")

In [120]: plt.figure();

In [121]: radviz(data, "Name");
../_images/radviz.png

图表格式设置#

设置图表样式#

从版本 1.5 及更高版本开始,matplotlib 提供了一系列预配置的绘图样式。设置样式可以轻松地为图表赋予您想要的整体外观。设置样式就像在创建图表之前调用 matplotlib.style.use(my_plot_style) 一样简单。例如,您可以写 matplotlib.style.use('ggplot') 来获取 ggplot 风格的图表。

您可以在 matplotlib.style.available 查看各种可用的样式名称,并且很容易尝试它们。

通用图表样式参数#

大多数绘图方法都有一组关键字参数,用于控制返回图表的布局和格式。

In [122]: plt.figure();

In [123]: ts.plot(style="k--", label="Series");
../_images/series_plot_basic2.png

对于每种类型的图表(例如 linebarscatter),任何额外的参数关键字都会传递给相应的 matplotlib 函数(ax.plot()ax.bar()ax.scatter())。这些可以用于控制超出 pandas 提供范围的额外样式设置。

控制图例#

您可以将 legend 参数设置为 False 来隐藏图例,图例默认是显示的。

In [124]: df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index, columns=list("ABCD"))

In [125]: df = df.cumsum()

In [126]: df.plot(legend=False);
../_images/frame_plot_basic_noleg.png

控制标签#

您可以设置 xlabelylabel 参数来为图表的 x 轴和 y 轴设置自定义标签。默认情况下,pandas 会将索引名称作为 x 轴标签,而 y 轴标签留空。


In [127]: df.plot();

In [128]: df.plot(xlabel="new x", ylabel="new y");
../_images/plot_xlabel_ylabel.png

刻度#

你可以传递 logy 参数以获得对数刻度的 Y 轴。

In [129]: ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))

In [130]: ts = np.exp(ts.cumsum())

In [131]: ts.plot(logy=True);
../_images/series_plot_logy.png

另请参见 logxloglog 关键字参数。

在次要 Y 轴上绘图#

要在次要 Y 轴上绘制数据,请使用 secondary_y 关键字参数

In [132]: df["A"].plot();

In [133]: df["B"].plot(secondary_y=True, style="g");
../_images/series_plot_secondary_y.png

要在 DataFrame 中绘制某些列,请将列名传递给 secondary_y 关键字参数

In [134]: plt.figure();

In [135]: ax = df.plot(secondary_y=["A", "B"])

In [136]: ax.set_ylabel("CD scale");

In [137]: ax.right_ax.set_ylabel("AB scale");
../_images/frame_plot_secondary_y.png

请注意,在次要 Y 轴上绘制的列会在图例中自动标记为“(右侧)”。要关闭自动标记,请使用 mark_right=False 关键字参数

In [138]: plt.figure();

In [139]: df.plot(secondary_y=["A", "B"], mark_right=False);
../_images/frame_plot_secondary_y_no_right.png

时间序列图的自定义格式化程序#

pandas 为时间序列图提供自定义格式化程序。这些格式化程序会更改日期和时间的轴标签的格式。默认情况下,自定义格式化程序仅应用于使用 pandas 的 DataFrame.plot()Series.plot() 创建的图。要使其应用于所有图,包括由 matplotlib 制作的图,请设置选项 pd.options.plotting.matplotlib.register_converters = True 或使用 pandas.plotting.register_matplotlib_converters()

抑制刻度分辨率调整#

pandas 为常规频率时间序列数据包含了自动刻度分辨率调整。在 pandas 无法推断频率信息(例如,在外部创建的 twinx 中)的有限情况下,你可以选择为了对齐目的而抑制此行为。

这是默认行为,注意 X 轴刻度标签的显示方式

In [140]: plt.figure();

In [141]: df["A"].plot();
../_images/ser_plot_suppress.png

使用 x_compat 参数,你可以抑制此行为

In [142]: plt.figure();

In [143]: df["A"].plot(x_compat=True);
../_images/ser_plot_suppress_parm.png

如果你有多个需要抑制的图,可以在 with 语句中使用 pandas.plotting.plot_params 中的 use 方法

In [144]: plt.figure();

In [145]: with pd.plotting.plot_params.use("x_compat", True):
   .....:     df["A"].plot(color="r")
   .....:     df["B"].plot(color="g")
   .....:     df["C"].plot(color="b")
   .....: 
../_images/ser_plot_suppress_context.png

自动日期刻度调整#

TimedeltaIndex 现在使用原生 matplotlib 刻度定位器方法,对于刻度标签重叠的图形,调用 matplotlib 的自动日期刻度调整非常有用。

有关更多信息,请参阅 autofmt_xdate 方法和 matplotlib 文档

子图#

DataFrame 中的每个 Series 都可以使用 subplots 关键字参数绘制在不同的轴上。

In [146]: df.plot(subplots=True, figsize=(6, 6));
../_images/frame_plot_subplots.png

使用布局并指定多个轴#

子图的布局可以使用 layout 关键字参数指定。它可以接受 (行数, 列数)layout 关键字参数也可以用于 histboxplot。如果输入无效,将引发 ValueError

layout 指定的行数 x 列数所能容纳的轴数必须大于所需子图的数量。如果布局能容纳的轴数多于所需,则不会绘制空白轴。类似于 NumPy 数组的 reshape 方法,你可以对其中一个维度使用 -1,以便根据另一个维度自动计算所需的行数或列数。

In [147]: df.plot(subplots=True, layout=(2, 3), figsize=(6, 6), sharex=False);
../_images/frame_plot_subplots_layout.png

上面的例子与使用以下方法相同

In [148]: df.plot(subplots=True, layout=(2, -1), figsize=(6, 6), sharex=False);

所需的列数 (3) 是根据要绘制的系列数量和给定的行数 (2) 推断出来的。

你可以通过 ax 关键字参数传递预先创建的多个轴(列表形式)。这允许更复杂的布局。传递的轴数量必须与要绘制的子图数量相同。

通过 ax 关键字参数传递多个轴时,layoutsharexsharey 关键字参数不影响输出。你应该明确传递 sharex=Falsesharey=False,否则你会看到警告。

In [149]: fig, axes = plt.subplots(4, 4, figsize=(9, 9))

In [150]: plt.subplots_adjust(wspace=0.5, hspace=0.5)

In [151]: target1 = [axes[0][0], axes[1][1], axes[2][2], axes[3][3]]

In [152]: target2 = [axes[3][0], axes[2][1], axes[1][2], axes[0][3]]

In [153]: df.plot(subplots=True, ax=target1, legend=False, sharex=False, sharey=False);

In [154]: (-df).plot(subplots=True, ax=target2, legend=False, sharex=False, sharey=False);
../_images/frame_plot_subplots_multi_ax.png

另一种选择是将 ax 参数传递给 Series.plot() 以绘制在特定轴上

In [155]: np.random.seed(123456)

In [156]: ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))

In [157]: ts = ts.cumsum()

In [158]: df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index, columns=list("ABCD"))

In [159]: df = df.cumsum()
In [160]: fig, axes = plt.subplots(nrows=2, ncols=2)

In [161]: plt.subplots_adjust(wspace=0.2, hspace=0.5)

In [162]: df["A"].plot(ax=axes[0, 0]);

In [163]: axes[0, 0].set_title("A");

In [164]: df["B"].plot(ax=axes[0, 1]);

In [165]: axes[0, 1].set_title("B");

In [166]: df["C"].plot(ax=axes[1, 0]);

In [167]: axes[1, 0].set_title("C");

In [168]: df["D"].plot(ax=axes[1, 1]);

In [169]: axes[1, 1].set_title("D");
../_images/series_plot_multi.png

绘制带误差条的图#

DataFrame.plot()Series.plot() 支持绘制带误差条的图。

水平和垂直误差条可以通过 xerryerr 关键字参数提供给 plot()。误差值可以用多种格式指定:

  • 作为误差的 DataFramedict,其列名与绘图 DataFramecolumns 属性匹配,或与 Seriesname 属性匹配。

  • 作为 str,指示绘图 DataFrame 的哪些列包含误差值。

  • 作为原始值(listtuplenp.ndarray)。长度必须与绘图 DataFrame/Series 的长度相同。

这里是一个示例,展示了一种轻松绘制包含原始数据标准差的分组平均值的方法。

# Generate the data
In [170]: ix3 = pd.MultiIndex.from_arrays(
   .....:     [
   .....:         ["a", "a", "a", "a", "a", "b", "b", "b", "b", "b"],
   .....:         ["foo", "foo", "foo", "bar", "bar", "foo", "foo", "bar", "bar", "bar"],
   .....:     ],
   .....:     names=["letter", "word"],
   .....: )
   .....: 

In [171]: df3 = pd.DataFrame(
   .....:     {
   .....:         "data1": [9, 3, 2, 4, 3, 2, 4, 6, 3, 2],
   .....:         "data2": [9, 6, 5, 7, 5, 4, 5, 6, 5, 1],
   .....:     },
   .....:     index=ix3,
   .....: )
   .....: 

# Group by index labels and take the means and standard deviations
# for each group
In [172]: gp3 = df3.groupby(level=("letter", "word"))

In [173]: means = gp3.mean()

In [174]: errors = gp3.std()

In [175]: means
Out[175]: 
                data1     data2
letter word                    
a      bar   3.500000  6.000000
       foo   4.666667  6.666667
b      bar   3.666667  4.000000
       foo   3.000000  4.500000

In [176]: errors
Out[176]: 
                data1     data2
letter word                    
a      bar   0.707107  1.414214
       foo   3.785939  2.081666
b      bar   2.081666  2.645751
       foo   1.414214  0.707107

# Plot
In [177]: fig, ax = plt.subplots()

In [178]: means.plot.bar(yerr=errors, ax=ax, capsize=4, rot=0);
../_images/errorbar_example.png

还支持非对称误差条,但在此情况下必须提供原始误差值。对于长度为 NSeries,应提供一个 2xN 数组,指示下限和上限(或左侧和右侧)误差。对于 MxNDataFrame,非对称误差应在一个 Mx2xN 数组中。

这里是一个示例,展示了一种使用非对称误差条绘制最小/最大范围的方法。

In [179]: mins = gp3.min()

In [180]: maxs = gp3.max()

# errors should be positive, and defined in the order of lower, upper
In [181]: errors = [[means[c] - mins[c], maxs[c] - means[c]] for c in df3.columns]

# Plot
In [182]: fig, ax = plt.subplots()

In [183]: means.plot.bar(yerr=errors, ax=ax, capsize=4, rot=0);
../_images/errorbar_asymmetrical_example.png

绘制表格#

现在 DataFrame.plot()Series.plot() 支持使用 table 关键字参数绘制 matplotlib 表格。table 关键字参数可以接受 boolDataFrameSeries。绘制表格的简单方法是指定 table=True。数据将被转置以符合 matplotlib 的默认布局。

In [184]: np.random.seed(123456)

In [185]: fig, ax = plt.subplots(1, 1, figsize=(7, 6.5))

In [186]: df = pd.DataFrame(np.random.rand(5, 3), columns=["a", "b", "c"])

In [187]: ax.xaxis.tick_top()  # Display x-axis ticks on top.

In [188]: df.plot(table=True, ax=ax);
../_images/line_plot_table_true.png

此外,你可以将不同的 DataFrameSeries 传递给 table 关键字参数。数据将按照 print 方法中显示的方式绘制(不会自动转置)。如果需要,应手动转置,如下例所示。

In [189]: fig, ax = plt.subplots(1, 1, figsize=(7, 6.75))

In [190]: ax.xaxis.tick_top()  # Display x-axis ticks on top.

In [191]: df.plot(table=np.round(df.T, 2), ax=ax);
../_images/line_plot_table_data.png

还存在一个辅助函数 pandas.plotting.table,它可以从 DataFrameSeries 创建表格,并将其添加到 matplotlib.Axes 实例。此函数可以接受 matplotlib table 所具有的关键字参数。

In [192]: from pandas.plotting import table

In [193]: fig, ax = plt.subplots(1, 1)

In [194]: table(ax, np.round(df.describe(), 2), loc="upper right", colWidths=[0.2, 0.2, 0.2]);

In [195]: df.plot(ax=ax, ylim=(0, 2), legend=None);
../_images/line_plot_table_describe.png

注意:你可以使用 axes.tables 属性获取轴上的表格实例以进行进一步装饰。有关更多信息,请参阅 matplotlib 表格文档

色图#

绘制大量列时的一个潜在问题是,由于默认颜色重复,可能难以区分某些系列。为了解决这个问题,DataFrame 绘图支持使用 colormap 参数,该参数接受 Matplotlib 色图或在 Matplotlib 中注册的色图名称的字符串。默认 Matplotlib 色图的可视化效果可在此处查看:此处

由于 matplotlib 不直接支持基于线的图的色图,因此颜色是根据 DataFrame 中列数确定的等间距选择的。没有考虑背景颜色,因此某些色图会生成不易见的线条。

要使用 cubehelix 色图,我们可以传递 colormap='cubehelix'

In [196]: np.random.seed(123456)

In [197]: df = pd.DataFrame(np.random.randn(1000, 10), index=ts.index)

In [198]: df = df.cumsum()

In [199]: plt.figure();

In [200]: df.plot(colormap="cubehelix");
../_images/cubehelix.png

或者,我们可以传递色图对象本身

In [201]: from matplotlib import cm

In [202]: plt.figure();

In [203]: df.plot(colormap=cm.cubehelix);
../_images/cubehelix_cm.png

色图也可以用于其他类型的图,例如条形图

In [204]: np.random.seed(123456)

In [205]: dd = pd.DataFrame(np.random.randn(10, 10)).map(abs)

In [206]: dd = dd.cumsum()

In [207]: plt.figure();

In [208]: dd.plot.bar(colormap="Greens");
../_images/greens.png

平行坐标图

In [209]: plt.figure();

In [210]: parallel_coordinates(data, "Name", colormap="gist_rainbow");
../_images/parallel_gist_rainbow.png

安德鲁斯曲线图

In [211]: plt.figure();

In [212]: andrews_curves(data, "Name", colormap="winter");
../_images/andrews_curve_winter.png

直接使用 Matplotlib 绘图#

在某些情况下,直接使用 matplotlib 准备绘图可能仍然更可取或必需,例如当 pandas 尚不支持某种类型的图或自定义时。SeriesDataFrame 对象表现得像数组,因此可以直接传递给 matplotlib 函数而无需显式转换。

pandas 还自动注册识别日期索引的格式化程序和定位器,从而将日期和时间支持扩展到 matplotlib 中几乎所有可用的图类型。尽管这种格式化提供的精细程度不如通过 pandas 绘图时获得的,但在绘制大量点时速度可能更快。

In [213]: np.random.seed(123456)

In [214]: price = pd.Series(
   .....:     np.random.randn(150).cumsum(),
   .....:     index=pd.date_range("2000-1-1", periods=150, freq="B"),
   .....: )
   .....: 

In [215]: ma = price.rolling(20).mean()

In [216]: mstd = price.rolling(20).std()

In [217]: plt.figure();

In [218]: plt.plot(price.index, price, "k");

In [219]: plt.plot(ma.index, ma, "b");

In [220]: plt.fill_between(mstd.index, ma - 2 * mstd, ma + 2 * mstd, color="b", alpha=0.2);
../_images/bollinger.png

绘图后端#

pandas 可以通过第三方绘图后端进行扩展。主要思想是让用户选择与基于 Matplotlib 提供的后端不同的绘图后端。

这可以通过将 ‘backend.module’ 作为 plot 函数中的参数 backend 来实现。例如:

>>> Series([1, 2, 3]).plot(backend="backend.module")

或者,你也可以全局设置此选项,这样就无需在每次 plot 调用中指定关键字。例如:

>>> pd.set_option("plotting.backend", "backend.module")
>>> pd.Series([1, 2, 3]).plot()

>>> pd.options.plotting.backend = "backend.module"
>>> pd.Series([1, 2, 3]).plot()

这大致相当于

>>> import backend.module
>>> backend.module.plot(pd.Series([1, 2, 3]))

然后,后端模块可以使用其他可视化工具(如 Bokeh、Altair、hvplot 等)生成图。在生态系统页面上列出了一些实现了 pandas 后端的库。

开发者指南可在以下链接找到:https://pandas.ac.cn/docs/dev/development/extending.html#plotting-backends