使用Pandas按两个组制作箱形图

4

我有如下数据集:

df_plots = pd.DataFrame({'Group':['A','A','A','A','A','A','B','B','B','B','B','B'],
                         'Type':['X','X','X','Y','Y','Y','X','X','X','Y','Y','Y'],
                         'Value':[1,1.2,1.4,1.3,1.8,1.5,15,19,18,17,12,13]})
df_plots
    Group   Type    Value
0   A       X       1.0
1   A       X       1.2
2   A       X       1.4
3   A       Y       1.3
4   A       Y       1.8
5   A       Y       1.5
6   B       X       15.0
7   B       X       19.0
8   B       X       18.0
9   B       Y       17.0
10  B       Y       12.0
11  B       Y       13.0

我希望创建按(此示例中有两个)分类的箱线图,并在每个图中按类型显示。 我已尝试:

fig, axs = plt.subplots(1,2,figsize=(8,6), sharey=False)
axs = axs.flatten()

for i, g in enumerate(df_plots[['Group','Type','Value']].groupby(['Group','Type'])):
    g[1].boxplot(ax=axs[i])
  • 由于循环尝试创建4个图,因此会导致IndexError错误。
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-12-8e1150950024> in <module>
      3 
      4 for i, g in enumerate(df[['Group','Type','Value']].groupby(['Group','Type'])):
----> 5     g[1].boxplot(ax=axs[i])

IndexError: index 2 is out of bounds for axis 0 with size 2

然后我尝试了这个:

fig, axs = plt.subplots(1,2,figsize=(8,6), sharey=False)
axs = axs.flatten()

for i, g in enumerate(df_plots[['Group','Type','Value']].groupby(['Group','Type'])):
    g[1].boxplot(ax=axs[i], by=['Group','Type'])

但是,我也遇到了同样的问题。期望的结果应该只有两个图,每个图上有每种类型的盒式图。这是这个想法的草图:

enter image description here

任何帮助都将不胜感激,通过这段代码,我可以控制一些无法使用 seaborn 控制的数据方面的内容。

4个回答

5
我们可以使用 groupby boxplot 来根据每个 Group 创建子图,然后按 Type 分离每个 boxplot
fig, axes = plt.subplots(1, 2, figsize=(8, 6), sharey=False)
df_plots.groupby('Group').boxplot(by='Type', ax=axes)
plt.show()

或者,通过直接在函数调用中传递参数来实现无需subplots

axes = df_plots.groupby('Group').boxplot(by='Type', figsize=(8, 6),
                                         layout=(1, 2), sharey=False)
plt.show()

plot


数据和导入:

import pandas as pd
from matplotlib import pyplot as plt

df_plots = pd.DataFrame({
    'Group': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B'],
    'Type': ['X', 'X', 'X', 'Y', 'Y', 'Y', 'X', 'X', 'X', 'Y', 'Y', 'Y'],
    'Value': [1, 1.2, 1.4, 1.3, 1.8, 1.5, 15, 19, 18, 17, 12, 13]
})

2
我很惊讶by='Type'df_plots.groupby('Group').plot(kind='box', by='Type', figsize=(8, 6), layout=(1, 2), sharey=False)似乎无法正确工作。 - Trenton McKinney
2
在我快速(绝不全面地阅读了一些源代码)的浏览中,似乎“by”被传递到matplotlib.pyplot.boxplot,但它无法处理“by”,而groupby.boxplot会传递给DataFrame.boxplot,后者可以处理“by”并将其余值传递给matplotlib.pyplot.boxplot - Henry Ecker

4
使用 seaborn.catplot
import seaborn as sns
sns.catplot(data=df, kind='box', col='Group', x='Type', y='Value', hue='Type', sharey=False, height=4)

enter image description here


1
你好@mozway,感谢你的回答,但我更喜欢给定的代码,不要用seaborn,因为我必须在循环内进行一些计算。无论如何,谢谢。 - Alexis
你不能使用 FacetGrid 吗?它可以处理图形布局,同时仍然让你访问每个组的原始数据。如果你真的不想用,那就预处理你的数据,并只按 'Group' 分组绘图。 - mozway
@Alexis:“我必须在循环内进行一些计算”并未在原始帖中说明。最好将计算步骤与绘图步骤分开。 - Trenton McKinney

3

正如@Prune提到的,问题在于您的groupby()返回了四个组(AX、AY、BX、BY),因此首先要修复索引,然后再清理一些问题:

  1. axs[i]更改为axs[i//2],将组0和1放在axs[0]上,将组2和3放在axs[1]上。
  2. 添加positions=[i]以将箱线图并排而不是堆叠。
  3. 在绘制后设置titlexticklabels(我不知道如何在主循环中实现此操作)。
for i, g in enumerate(df_plots.groupby(['Group', 'Type'])):
    g[1].boxplot(ax=axs[i//2], positions=[i])

for i, ax in enumerate(axs):
    ax.set_title('Group: ' + df_plots['Group'].unique()[i])
    ax.set_xticklabels(['Type: X', 'Type: Y'])

箱线图输出


请注意,里程可能因版本而异:

matplotlib.__version__ pd.__version__
已确认可用 3.4.2 1.3.1
已确认不可 3.0.1 1.2.4

1
你好 @tdy,我尝试过你的解决方案,但我的结果与你的图片不同(我的每边只显示一个箱线图)。我甚至复制粘贴了你的代码,但它并没有展示出你的效果。我有什么遗漏吗? - Alexis
2
不太确定。我只是添加了一个完整的最小示例,它给出了您期望的输出。如果仍然不能得到相同的结果,也许这是一个版本问题?我正在使用matplotlib 3.4.2和pandas 1.3.1。 - tdy
1
那一定是问题所在!我的matplotlib版本是3.0.1,pandas版本是1.2.4,我会尝试更新并查看是否能够重现您的图。谢谢! - Alexis
1
那就是问题所在,版本的问题!现在有了你的代码,问题解决了,谢谢! - Alexis

2
直接问题是你的groupby操作返回四个元素(AX,AY,BX,BY),你试图单独绘制它们。你尝试使用ax=axs[i] ...但是i运行0-3,而你的扁平结构中只有两个元素。没有axs[2]axs[3],这引发了给定的运行时异常。
你需要解决你的引用问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接