Seaborn分组聚合Pandas Series

4
我希望将我的数据可视化成箱形图,并按另一个变量进行分组,如下图所示:

enter image description here

我使用 pandas 系列变量来告诉 pandas 我已经对变量进行了分组,具体操作如下:
import pandas as pd
import seaborn as sns
#example data for reproduciblity
a = pd.DataFrame(
[
[2, 1],
[4, 2],
[5, 1],
[10, 2],
[9, 2],
[3, 1]
])

#converting second column to Series 
a.ix[:,1] = pd.Series(a.ix[:,1])
#Plotting by seaborn
sns.boxplot(a, groupby=a.ix[:,1])

这是我得到的结果: seaborn plot 然而,我本来期望得到两个箱线图,每个箱线图只描述第一列数据,按照第二列数据(转换为Series)中对应的列进行分组,而上述图表则显示了每列数据分开的情况,这不是我想要的。
3个回答

7
Dataframe中的一列已经是一个Series,因此您不需要进行转换。此外,如果您只想使用第一列来制作两个箱线图,那么您应该将其传递给Seaborn。
所以:
#example data for reproduciblity
df = pd.DataFrame(
[
[2, 1],
[4, 2],
[5, 1],
[10, 2],
[9, 2],
[3, 1]
], columns=['a', 'b'])

#Plotting by seaborn
sns.boxplot(df.a, groupby=df.b)

我稍微修改了你的示例,给列加上标签可以使它更清晰(在我看来)。
编辑:
如果您想单独绘制所有列,则(我认为)基本上要获取组合列中值和任何其他列的所有组合。 因此,如果您的Dataframe如下所示:
    a   b  grouper
0   2   5        1
1   4   9        2
2   5   3        1
3  10   6        2
4   9   7        2
5   3  11        1

您想要对列ab进行箱线图分析,同时按照列grouper进行分组。您需要将列展开,并且更改分组列的值,例如a1a2b1等。

以下是一种简单的方法,我认为可以在上面显示的Dataframe中使用:

dfpiv = df.pivot(index=df.index, columns='grouper')

cols_flat = [dfpiv.columns.levels[0][i] + str(dfpiv.columns.levels[1][j]) for i, j in zip(dfpiv.columns.labels[0], dfpiv.columns.labels[1])]  
dfpiv.columns = cols_flat
dfpiv = dfpiv.stack(0)

sns.boxplot(dfpiv, groupby=dfpiv.index.get_level_values(1))

也许有更高级的方法来重组Dataframe。特别是在数据透视后打平层次结构很难阅读,我不喜欢它。

非常感谢,如果我有多列数据并且想要绘制它们,有没有一种方法可以做到这一点?因为您在此提出的方法只允许一次绘制一列。当然,使用for循环可以实现多个图形的绘制,但是我仍然希望将它们全部绘制在一个图形中。 - Arman
一个例子是这样的,我无法在评论中添加图片,所以这是我的AWS s3存储桶的链接:https://s3.amazonaws.com/ms-neuroimager/so3.png - Arman
您可以传递多列,只要排除 'groupby' 列:sns.boxplot(df[['a', 'a2']], groupby=df.b)。但是,它将合并所有列的所有值,然后按组计算统计信息,因此您将失去列之间的区别。保留区别并同时绘制所有内容需要对原始数据框进行一些重构。 - Rutger Kassies
谢谢,您的原始答案回答了我的问题,但是像您提到的索引多列并不起作用,这意味着它忽略了groupby变量。我将开另一个问题,让我放一些可重现的代码,然后您可能能够帮助我。再次感谢。 - Arman
你确定 sns.boxplot 接受 groupby 参数吗?这似乎不是官方 API 的一部分。 - gented
显示剩余2条评论

1
这是一道旧问题的全新答案,因为在 seabornpandas 的版本更新中有所更改。由于这些变化,Rutger 的回答已经不再适用。
最重要的更改是从 seaborn==v0.5.xseaborn==v0.6.0。我引用日志:
更改 boxplot()violinplot() 可能是最具破坏性的。这两个函数在接受的数据类型方面保持向后兼容性,但语法已更改为更类似于其他 seaborn 函数。现在,这些函数使用 x 和/或 y 参数调用,这些参数可以是数据向量,也可以是传递给新数据参数的长格式 DataFrame 中的变量名称。
现在让我们看一下例子:
# preamble
import pandas as pd # version 1.1.4
import seaborn as sns # version 0.11.0
sns.set_theme()

示例1:简单箱线图

df = pd.DataFrame([[2, 1] ,[4, 2],[5, 1],
                   [10, 2],[9, 2],[3, 1]
                  ], columns=['a', 'b'])

#Plotting by seaborn with x and y as parameter
sns.boxplot(x='b', y='a', data=df)

simple boxplot

示例2:带有分组器的箱线图。
df = pd.DataFrame([[2, 5, 1], [4, 9, 2],[5, 3, 1],
                   [10, 6, 2],[9, 7, 2],[3, 11, 1]
                  ], columns=['a', 'b', 'grouper'])
# usinge pandas melt
df_long = pd.melt(df, "grouper", var_name='a', value_name='b')
# join two columns together
df_long['a'] = df_long['a'].astype(str) + df_long['grouper'].astype(str)
sns.boxplot(x='a', y='b', data=df_long)

boxplot after melt

示例 3:重新排列DataFrame以直接传递给seaborn

def df_rename_by_group(data:pd.DataFrame, col:str)->pd.DataFrame:
    '''This function takes a DataFrame, groups by one column and returns 
       a new DataFrame where the old columnnames are extended by the group item. 
    '''
    grouper = df.groupby(col)
    max_length_of_group = max([len(values) for item, values in grouper.indices.items()])
    _df = pd.DataFrame(index=range(max_length_of_group))
    for i in grouper.groups.keys():
        helper = grouper.get_group(i).drop(col, axis=1).add_suffix(str(i))
        helper.reset_index(drop=True, inplace=True)
        _df = _df.join(helper)
    return _df

df = pd.DataFrame([[2, 5, 1], [4, 9, 2],[5, 3, 1],
                   [10, 6, 2],[9, 7, 2],[3, 11, 1]
                  ], columns=['a', 'b', 'grouper'])
df_new = df_rename_by_group(data=df, col='grouper')
sns.boxplot(data=df_new)

boxplot of rearanged data

我真的希望这个答案能够帮助避免一些混淆。

0

sns.boxplot() 不支持 groupby 参数。 可能会出现以下错误:
TypeError: boxplot() got an unexpected keyword argument 'groupby'.

将数据按组分组并将其作为 groupby 数据框的值传递以在 boxplot 中使用是最好的方法。

import seaborn as sns
grouDataFrame = nameDataFrame(['A'])['B'].agg(sum).reset_index()
sns.boxplot(y='B', x='A', data=grouDataFrame)

这里B列数据包含数字值,并且按A进行分组。将所有分组值及其各自的列加起来,然后绘制箱形图。希望这可以帮到您。


你的回答很有道理,因为在sns中有一些变化。你能否添加更多信息?你使用的是哪个版本,sns的变化是什么时候发生的?你能否在帖子中添加一个示例图和一些示例数据吗?这将有助于理解为什么你的帖子很重要。 - mosc9575

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