来自数据框的带有子图的多个图形

Multiple figures with subplots from a dataframe

我有一个数据框,我将列中的排序值绘制为一条线,然后沿着该线绘制和标记各种百分位数。

我希望每个图有 12 个子图,并且根据列数(这将因我的真实数据集而异),我需要尽可能多的图。

这是我的脚本版本,带有一个简单的数据框。此示例生成 2 个图,每个图都有 12 个子图。数据框中只有 17 个条目,因此一个图形有 7 个可用的子图。这是我需要的结果,但是,脚本不优雅且效率不高。

我正在学习 python,当我想不出有效的 python 方法时,我倾向于恢复旧的脚本编写习惯。有人可以告诉我如何用更优雅的 python 生成相同的图形吗?如果我没有 7 个空子图,那也没关系。我尝试过各种组合。我想我是因为不了解 pandasnumpy 的内部工作原理而被绊倒了。我有一个 pandas 数据框,但我正在使用 numpy 函数操作列。在我的试验中,我在尝试将 np.sort 应用于数据框列时遇到了很多错误,这就是为什么我恢复到一次从数据框中取出一列来操作然后绘制的原因。

df = pd.DataFrame(np.random.randint(0,100,size=(15, 17)), columns=list('ABCDEFGHIJKLMNOPQ'))
p = np.array([0.0, 25.0, 50.0, 75.0, 90.0, 95.0, 99.0, 100.0])
j=2
i=1
fig, axs = plt.subplots(4,3, figsize=(8.5, 11), facecolor='w', edgecolor='k')
fig.subplots_adjust(hspace=0.4, wspace=0.4)
for f in df.columns:
    d=df[f]
    d=np.sort(d)
    perc = np.nanpercentile(d, p)
    if i >12:
       fig, axs = plt.subplots(4,3, figsize=(8.5, 11), facecolor='w', edgecolor='k')
       i=1
       j=j+1
    plt.subplot(4, 3, i)
    plt.plot(d)
    plt.plot((len(df[f])-1) * p/100., perc, 'ro')
    plt.xticks((len(df[f])-1)* p/100., map(str, p))
    plt.show()
    i=i+1

略有不同的方法

  • 预先创建所有图形和轴并展平轴数组
  • 然后使用更多最新的 Matplotlib API 来 绘制
  • 使用 pandas 而不是 numpy 因为我发现从索引定义 x 坐标更简单系列而不是数组
  • 即使在旋转后 x 轴仍然有些难看,因为您希望刻度在 90+ 个百分位数中靠得很近
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import math

df = pd.DataFrame(np.random.randint(0,100,size=(15, 17)), columns=list('ABCDEFGHIJKLMNOPQ'))
p = np.array([0.0, 25.0, 50.0, 75.0, 90.0, 95.0, 99.0, 100.0])

# create all the figures and axis that are going to be used...
axs = np.array([])
for _ in range(math.ceil(len(df.columns)/12)):
    fig, ax_t = plt.subplots(4,3, figsize=(8.5, 11), facecolor='w', edgecolor='k')
    fig.subplots_adjust(hspace=0.4, wspace=0.4)
    axs = np.concatenate((axs, np.array(ax_t).flatten()))

for i, ax in enumerate(np.array(axs)):
    # NB will have created empty axis where there is no column
    if i==len(df.columns): break
    d = df.loc[:,df.columns[i]].sort_values().reset_index(drop=True)
    ax.plot(d.index/(len(df)-1), d)
    q = d.quantile(p/100)
    ax.plot(q, "ro")
    ax.set_xticks(p/100)
    ax.tick_params(axis='x', labelrotation = 90)
    # annotate 0th, 50th and 100th percentiles
    for a in q.loc[[0,.5,1]].index:
        ax.annotate(round(q[a],1), xy=(a, q[a]),  xycoords='data', xytext=(3, 3), textcoords='offset points',)