【问题标题】:re-arranging two or more pandas dataframes for a seaborn graph为 seaborn 图重新排列两个或多个 pandas 数据帧
【发布时间】:2020-03-31 01:32:18
【问题描述】:

两个实验室进行了一系列测量:

  1. 在已知仪器显示不同响应的三个实验条件下(test_1test_2test_3
  2. 使用两种不同品牌和型号的乐器(foo、bar)
  3. 在他们拥有的每个仪器型号的多个副本上重复他们的测量,这些副本具有单独的序列号,并包括对他们相互借用以进行实验室间比较的仪器的测量(在两个实验室测量的序列号相同)。

这两个实验室现在希望将他们所有的数据集中在一起,并从他们的组合数据集中对这两种仪器的属性得出一些结论。首先,他们希望查看 Seaborn 分类图中的所有数据。

稍微改编自 Pandas cookbook examples on MultiIndexing 这是每个实验室交流数据的方式:

import pandas as pd
import seaborn as sns
from matplotlib import pyplot

df = pd.DataFrame({'test': ['test_1', 'test_2' ,'test_3'],
                'foo_110': [1.1, 1.18, 1.19],
                'foo_112': [1.15, 1.25, 1.25],
                'bar_888': [1.11, 1.15, 1.16],
                'bar_657': [1.14, 1.16, 1.18]}
               ) 
df1 = pd.DataFrame({'test': ['test_1', 'test_2' ,'test_3'],
                'foo_105': [1.13, 1.17, 1.18],
                'foo_112': [1.16, 1.26, 1.28],
                'foo_167': [1.18, 1.23, 1.27],
                'bar_888': [1.10, 1.14, 1.18],
                'bar_415': [1.12, 1.15, 1.16]}
               )

为了为他们的 Seaborn 图准备数据,数据框在其索引 stacked() 中重新组织,并沿 axis = 0 连接:

df = df.set_index('test')
df.columns = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df.columns])
df = df.stack().reset_index()

df1 = df1.set_index('test')
df1.columns = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df1.columns])
df1 = df1.stack().reset_index()

dfAll = pd.concat((df, df1), axis = 0, sort= False)
dfAll.columns = ['test', 's.no.', 'bar', 'foo']

生产

print(dfAll.head(10))
     test s.no.   bar   foo
0  test_1   110   NaN  1.10
1  test_1   112   NaN  1.15
2  test_1   657  1.14   NaN
3  test_1   888  1.11   NaN
4  test_2   110   NaN  1.18
5  test_2   112   NaN  1.25
6  test_2   657  1.16   NaN
7  test_2   888  1.15   NaN
8  test_3   110   NaN  1.19
9  test_3   112   NaN  1.25

为“条形”工具绘制所有数据:

sns.set(style="whitegrid")
dfAllplot = sns.catplot(x="test", y ="bar", data=dfAll, hue='s.no.')

这两个实验室现在想要做的是保持他们区分来自Lab_1Lab_2 的数据点的能力,以及使用两个实验室提供的其他信息,其中包括误差线,test_1test_2test_3 是不同的。

通过在dfdf1 重置它们的索引之后和连接之前添加一列来轻松提供实验室数据。

df['Lab'] = 'Lab_1' 
df1['Lab'] = 'Lab_2'

我没有解决的问题是如何在为每个test_x 提供附加信息时保留stacking() 之后的数据。在将Index 重新排列为MultiIndex 后添加具有相对不确定性的列df1['urel']

df1 = df1.set_index('test')
df1.columns = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df1.columns])
df1['urel'] = [0.015, 0.014, 0.013]

产量

df1
Out[137]: 
         foo               bar         urel
         105   112   167   888   415       
test                                       
test_1  1.13  1.16  1.18  1.10  1.12  0.015
test_2  1.17  1.26  1.23  1.14  1.15  0.014
test_3  1.18  1.28  1.27  1.18  1.16  0.013

然后

df1 = df1.stack().reset_index()
df1['Lab'] = 'Lab_2'

'Lab' 列产生想要的结果,但不是'urel',它只为某些行携带原始值,不能用于进一步的绘图指令。

print(df1.head(10))
     test level_1   bar   foo   urel    Lab
0  test_1     105   NaN  1.13    NaN  Lab_2
1  test_1     112   NaN  1.16    NaN  Lab_2
2  test_1     167   NaN  1.18    NaN  Lab_2
3  test_1     415  1.12   NaN    NaN  Lab_2
4  test_1     888  1.10   NaN    NaN  Lab_2
5  test_1           NaN   NaN  0.015  Lab_2
6  test_2     105   NaN  1.17    NaN  Lab_2
7  test_2     112   NaN  1.26    NaN  Lab_2
8  test_2     167   NaN  1.23    NaN  Lab_2
9  test_2     415  1.15   NaN    NaN  Lab_2

什么时候应该将'urel' 添加到数据框中? 如果在 MultiIndexing 之前添加,即从一开始,多索引、堆叠和重置,就像这里所做的那样再次“中断”'urel'。 或者stack() 不是此处提供的示例的正确方法?

【问题讨论】:

    标签: python pandas seaborn


    【解决方案1】:

    一个不太优雅的解决方案

    假设包含所有信息的原始数据框是:

    df = pd.DataFrame({'test': ['test_1', 'test_2' ,'test_3'],
                       'foo_110': [1.1, 1.18, 1.19],
                       'foo_112': [1.15, 1.25, 1.25],
                       'bar_888': [1.11, 1.15, 1.16],
                       'bar_657': [1.14, 1.16, 1.18],
                       'urel' : [0.020, 0.025, 0.018],
                       'HVL' : [0.156, 0.180, 0.195]}
                      )
    df = df.set_index('test')
    

    其中'urel''HVL' 是两列,它们会受到前面说明的问题的影响。将它们从 stacking() 进程中取出,然后使用函数返回,即可完成所需的操作

    def stack_for_seaborn(df, separate=['urel', 'HVL']):
        '''
        Alternative stacking() of a pandas dataframe for seaborn plotting
        First, some columns are extracted from the stacking() process
        Second, stacking() is applied
        Third, the 'separate' portion of the df is appended considering the new index
        '''
        idx = df.index
        addnl = df[separate]
        df.columns = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df.columns])
        df.drop(separate, axis = 1, level=0, inplace=True)
        df = df.stack().reset_index()
        df = df.set_index('test')
        for t in idx:
            for c in addnl.columns:
                df.loc[t, c] = addnl[c].loc[t]
        return df
    
    df = stack_for_seaborn(df)
    df['Lab'] = 'Lab_1' 
    df.reset_index(inplace=True)
    df.columns =   ['test', 's.no.', 'bar', 'foo', 'urel', 'HVL', 'Lab'] 
    

    产生所寻求的结果,但是 pandas 的内置方法可能会更好。

    print(df.head(10))
         test s.no.   bar   foo   urel    HVL    Lab
    0  test_1   110   NaN  1.10  0.020  0.156  Lab_1
    1  test_1   112   NaN  1.15  0.020  0.156  Lab_1
    2  test_1   657  1.14   NaN  0.020  0.156  Lab_1
    3  test_1   888  1.11   NaN  0.020  0.156  Lab_1
    4  test_2   110   NaN  1.18  0.025  0.180  Lab_1
    5  test_2   112   NaN  1.25  0.025  0.180  Lab_1
    6  test_2   657  1.16   NaN  0.025  0.180  Lab_1
    7  test_2   888  1.15   NaN  0.025  0.180  Lab_1
    8  test_3   110   NaN  1.19  0.018  0.195  Lab_1
    9  test_3   112   NaN  1.25  0.018  0.195  Lab_1
    

    【讨论】:

      【解决方案2】:

      基于 pandas 方法的解决方案

      此解决方案对DataFrames 使用melt() 方法。 重新定义两个数据框,以便为 melt 方法处理列:

      import pandas as pd
      import seaborn as sns
      from matplotlib import pyplot as plt
      df_1 = pd.DataFrame({'urel' : [0.020, 0.025, 0.018],
                           'HVL' : [0.156, 0.180, 0.195],
                           'test': ['test_1', 'test_2' ,'test_3'],
                           'Lab' : ['Lab_1', 'Lab_1', 'Lab_1'],
                           'foo_110': [1.1, 1.18, 1.19],
                           'foo_112': [1.15, 1.25, 1.25],
                           'bar_657': [1.14, 1.16, 1.18],
                           'bar_888': [1.11, 1.15, 1.16],
                           }
                        )
      
      df_2 = pd.DataFrame({'urel' : [0.020, 0.025, 0.018],
                           'HVL' : [0.156, 0.180, 0.195],
                           'test': ['test_1', 'test_2' ,'test_3'],
                           'Lab' : ['Lab_2', 'Lab_2', 'Lab_2'],
                           'foo_105': [1.13, 1.17, 1.18],
                           'foo_112': [1.16, 1.26, 1.28],
                           'foo_167': [1.18, 1.23, 1.27],
                           'bar_888': [1.10, 1.14, 1.18],
                           'bar_415': [1.12, 1.15, 1.16],
                          }
                      )
      for df in (df_1, df_2):
          df.columns = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df.columns])
      
      df_1 = df_1.melt(id_vars=(df_1.columns.tolist()[:4]),
                        value_vars=df_1.columns.tolist()[4:],
                        var_name=['model', 'ser.no']
                        )
      df_2 = df_2.melt(id_vars=(df_2.columns.tolist()[:4]),
                        value_vars=df_2.columns.tolist()[4:],
                        var_name=['model', 'ser.no']
                        )
      
      colnames = ['urel', 'HVL', 'test', 'Lab', 'model', 'ser.no', 'value'] 
      df_1.columns = colnames
      df_2.columns = colnames
      dfAll = df_1.append(df_2, ignore_index=True)
      

      在没有不必要的NaN 值的情况下产生DataFrame,也许更重要的是,foobar 两个工具在名为model 的同一列下。

      print(dfAll.head(10))
          urel    HVL    test    Lab model ser.no  value
      0  0.020  0.156  test_1  Lab_1   foo    110   1.10
      1  0.025  0.180  test_2  Lab_1   foo    110   1.18
      2  0.018  0.195  test_3  Lab_1   foo    110   1.19
      3  0.020  0.156  test_1  Lab_1   foo    112   1.15
      4  0.025  0.180  test_2  Lab_1   foo    112   1.25
      5  0.018  0.195  test_3  Lab_1   foo    112   1.25
      6  0.020  0.156  test_1  Lab_1   bar    657   1.14
      7  0.025  0.180  test_2  Lab_1   bar    657   1.16
      8  0.018  0.195  test_3  Lab_1   bar    657   1.18
      9  0.020  0.156  test_1  Lab_1   bar    888   1.11
      

      这种更好的结构适用于使用catplotseaborn 绘图,它返回一个FacetGrid 对象,每个仪器模型占据不同的方面

      sns.set(style="whitegrid")
      sns.set(style="ticks")
      g = sns.catplot(x="test", # returns a FacetGrid object for further editing
                  # see https://seaborn.pydata.org/generated/seaborn.FacetGrid.html#seaborn.FacetGrid
                  # https://seaborn.pydata.org/generated/seaborn.catplot.html#seaborn.catplot
                  y = 'value', 
                  data=dfAll, 
                  hue='Lab', 
                  col='model', 
                  dodge=False, 
                  kind='swarm',
                  sharey = True
                  )
      (g.set_axis_labels("tests", "value")
       .savefig('facetgrid_example.jpg', bbox_inches='tight')
                )
      

      【讨论】:

        猜你喜欢
        • 2018-08-23
        • 2017-03-25
        • 2022-09-24
        • 2019-01-25
        • 2016-07-05
        • 1970-01-01
        • 1970-01-01
        • 2019-05-31
        • 2017-01-03
        相关资源
        最近更新 更多