【问题标题】:Seaborn groupby pandas SeriesSeaborn groupby pandas 系列
【发布时间】:2014-10-06 10:30:39
【问题描述】:

我想将我的数据可视化为箱形图,这些箱形图由我可怕的绘图中显示的另一个变量分组:

所以我要做的是使用 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])

这就是我得到的:

但是,我希望得到的是两个箱线图,每个箱线图仅描述第一列,按第二列中的相应列分组(转换为系列的列),而上面的图分别显示了每一列不是我想要的。

【问题讨论】:

    标签: matplotlib pandas seaborn


    【解决方案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)
    

    示例 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)
    

    示例 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)
    

    我真的希望这个答案有助于避免一些混乱。

    【讨论】:

      【解决方案2】:

      sns.boxplot() 不采用 groupby。 可能你会看到
      TypeError: boxplot() got an unexpected keyword argument 'groupby'.

      对数据进行分组并在箱线图中使用的最佳方法是将数据作为 groupby 数据框值传递。

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

      此处B列数据包含数值,并在A的基础上进行分组。将所有分组值与其各自的列相加并绘制箱线图。希望这会有所帮助。

      【讨论】:

      • 您的回答很有道理,因为sns有一些变化。您能否添加更多信息。您使用哪个版本,sns 的更改时间是什么时候?您可以在帖子中添加示例图和一些示例数据吗?这将有助于了解您的帖子为何具有相关性。
      【解决方案3】:

      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)
      

      我稍微改变了你的例子,在我看来给列一个标签会让它更清楚一点。

      编辑:

      如果您想分别绘制所有列,那么您(我认为)基本上想要groupby 列和任何其他列中的值的所有组合。所以如果你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 分组。您应该展平列并将 groupby 列更改为包含 a1a2b1 等值。

      鉴于上面显示的数据框,我认为这是一种粗略的方式:

      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 循环可以为多个绘图执行此操作,但我再次希望将所有这些都放在一个绘图中。
      • 一个例子是这样,我不能添加图片来评论所以这是我的 AWS s3 存储桶的链接:s3.amazonaws.com/ms-neuroimager/so3.png
      • 您可以传递多个列,只要排除 'groupby' 列:sns.boxplot(df[['a', 'a2']], groupby=df.b)。但是,它将合并所有列中的所有值,然后计算每组的统计信息,因此您将失去列之间的区别。保持区别并仍然一次绘制所有内容将需要对原始数据框进行一些重构。
      • 谢谢,您的原始答案回答了我最初的问题,但是正如您提到的那样索引多个列不起作用,这意味着它忽略了 groupby 变量。我将提出另一个问题,让我放置一些可重现的代码,然后您可能会帮助我。再次感谢。
      • 我正要建议。如果您提出有关 Pandas 部分的新问题,您可能还会引起现在被 Seaborn 相关部分“推迟”的人们的更多兴趣。
      猜你喜欢
      • 2019-08-03
      • 1970-01-01
      • 2019-08-22
      • 2020-04-24
      • 2021-07-04
      • 2020-01-15
      • 2017-04-06
      • 2020-08-16
      • 1970-01-01
      相关资源
      最近更新 更多