Python Pandas 绘制堆叠数据子图

2 投票
2 回答
729 浏览
提问于 2025-04-18 15:51

我想要制作一些图表(或者子图),这些图表是基于一个pandas数据框中的分组数据。我觉得这应该是个基础的操作,但我好像漏掉了什么。我有一些输入数据,这些数据是以“堆叠”的形式准备的,下面有个例子。我想为每个上层数据(upperLevel)制作一个像下面这样的图表:

上层数据A的示例图表

这里有一些示例数据(我把我使用的示例.csv数据粘贴在下面)。这些数据是以“堆叠”的形式呈现的,包含数据类型、时间和数据。数据类型描述了某个数据点的类别和子类别。

import pandas as pd
import re
import matplotlib.pyplot as plt

df=pd.read_csv('.....TestData.csv',index_col='T')
df=df.stack(0).reset_index(1)
df.columns=['fullType','data']
#And at this point, this is pretty much the form of my actual data

#So I split it up a bit to try to get columns for different data groupings
regexStr='~'

def upperParser(row):
    label=re.split(regexStr,row['fullType'])
    return label[1]
def lowerParser(row):
    label=re.split(regexStr,row['fullType'])
    return label[2]

df['upperLevel']=df.apply(upperParser,axis=1)
df['lowerLevel']=df.apply(lowerParser,axis=1)
df['time']=df.index


df=df.reset_index(drop=True)

plt.figure();
df.plot();

#And here is one of many attempts... I just seem to be missing something that should be simple:

for grp in df.groupby('upperLevel'):
for key,grp in df.groupby('lowerLevel'):
    plt.plot(x='time',y=grp['data'],label=key)
plt.show()

任何建议都非常感谢。我并不在意保持某种特定的格式。我的最终目标是绘制所有上层类别的图表(比如A=(0,1),B=(0,2)),并使用mpl3d来查看底层的子图(就像这个,但每个子类别1,2,3作为子图堆叠)。不过我想先从基础开始。

示例数据:

T   Col~A~1~    Col~A~2~    Col~A~3~    Col~B~1~    Col~B~2~    Col~B~3~
1   1   0.5 0.5 0.5 0.25    0.25
1.5 2   1   1   1   0.5 0.5
2   3   1.5 0.5 1.5 0.75    0.25
2.5 4   2   1   2   1   0.5
3   5   2.5 0.5 2.5 1.25    0.25
3.5 6   3   1   3   1.5 0.5
4   7   3.5 0.5 3.5 1.75    0.25
4.5 8   4   1   4   2   0.5
5   9   4.5 0.5 4.5 2.25    0.25
5.5 10  5   1   5   2.5 0.5
6   11  5.5 0.5 5.5 2.75    0.25
6.5 12  6   1   6   3   0.5
7   13  6.5 0.5 6.5 3.25    0.25
7.5 14  7   1   7   3.5 0.5
8   15  7.5 0.5 7.5 3.75    0.25
8.5 16  8   1   8   4   0.5
9   17  8.5 0.5 8.5 4.25    0.25
9.5 18  9   1   9   4.5 0.5
10  19  9.5 0.5 9.5 4.75    0.25

2 个回答

1

我同意,这确实是个很有用的功能。我希望Pandas能有一个更高级的子图功能,能够按行分组和按列分组来创建子图。

这里有一个可以实现这个功能的函数,你可以试试看:

def subplotter(df):
    numcols = list(df.select_dtypes(include=['number']).columns)
    objcols = list(df.select_dtypes(include=['object']).columns)
    grouped = df.groupby(objcols)
    l = len(grouped.groups)
    cols = dict({1:1,2:1,3:1,4:2,5:2,6:2}, **{e:3 for e in range(7,25,1)})[l]
    rows = np.ceil(l/(cols*1.0))
    i, fig = 1, plt.figure(figsize=(5*cols,4*rows))
    for name, group in grouped:
        ax = fig.add_subplot(rows, cols, i)
        plt.plot(group[numcols])
        plt.legend(numcols)
        plt.title(', '.join([': '.join(e) for e in zip(objcols, name)]))
        plt.legend(numcols)
        i += 1
    plt.tight_layout()
    return

这个函数会根据所有的对象类型列来对数据框进行分组,并为每个组创建子图。所有数字类型的列都会放在每个子图中。

我增加的复杂性主要是为了确定图形的合适大小、子图的位置(行和列),以及添加图例和标题。

2

这里有几个小建议:

  • df.groupby() 会返回一个包含 (组名, 组) 的元组,所以在遍历这些组的时候要注意这一点。
  • 如果你想要的图表已经可以通过 pandas 的绘图方法实现,通常就不需要手动使用 pyplot 了。
  • pandas 的绘图方法通常会为数据框中的每一列生成一条单独的线,所以如果你能把数据整理成不同的列,就能轻松得到你想要的图表。
  • pandas 的绘图方法默认会使用数据框的索引作为 x 轴。

也就是说,你可以用以下代码生成你想要的图表:

for group_name, grp in df.groupby('upperLevel'):
    plot_table = grp.pivot(index='time', columns='lowerLevel', values='data')
    plot_table.plot()

撰写回答