将具有多列的计算结果添加到具有 MultiIndex 列的 Pandas DataFrame

Add a calculated result with multiple columns to Pandas DataFrame with MultiIndex columns

我有一个像这样的 DataFrame:

In [10]: df.head()
Out[10]: 
      sand              silt              clay             rho_b  ...      n  \
        5     25    60    5     25    60    5     25    60    5   ...     60   
STID                                                              ...          
ACME  73.0  60.3  52.5  19.7  23.9  25.9   7.2  15.7  21.5  1.27  ...   1.32   
ADAX  61.1  51.1  47.6  22.0  25.4  24.6  16.9  23.5  27.8  1.01  ...   1.25   
ALTU  23.8  17.8  14.3  40.0  45.2  40.9  36.2  37.0  44.8  1.57  ...   1.18   
ALV2  33.3  21.2  19.8  31.4  29.7  29.8  35.3  49.1  50.5  1.66  ...   1.20   
ANT2  55.6  57.5  47.7  34.9  31.1  26.8   9.4  11.3  25.5  1.49  ...   1.29  

因此对于每个 STID(例如 ACME、ADAX、ALTU),在三个深度(5、25、60)定义了一些 属性(例如沙子、淤泥、粘土)。

这种结构使得在每个 STID 处进行按深度计算变得非常容易,例如:

In [12]: (df['sand'] + df['silt']).head()
Out[12]: 
        5     25    60
STID                  
ACME  92.7  84.2  78.4
ADAX  83.1  76.5  72.2
ALTU  63.8  63.0  55.2
ALV2  64.7  50.9  49.6
ANT2  90.5  88.6  74.5

如何巧妙地将计算结果合并回 DataFrame?比如我想调用上面计算的结果'notclay':

In [13]: df['notclay'] = df['sand'] + df['silt']
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-13-a30bd9ba99c3> in <module>()
----> 1 df['notclay'] = df['sand'] + df['silt']

<snip>

ValueError: Wrong number of items passed 3, placement implies 1

预计将为结果中的每一列定义三列,而不仅仅是 'notclay' 列。

我确实有一个使用严格分配的解决方案,但我对此不是很满意:

In [21]: df[[('notclay', 5), ('notclay', 25), ('notclay', 60)]] = df['sand'] + df['silt']

In [22]: df['notclay'].head()
Out[22]: 
        5     25    60
STID                  
ACME  92.7  84.2  78.4
ADAX  83.1  76.5  72.2
ALTU  63.8  63.0  55.2
ALV2  64.7  50.9  49.6
ANT2  90.5  88.6  74.5

我还有许多其他类似的计算要做,每次都使用严格的赋值似乎很乏味。我猜想有更好/"right" 的方法来做到这一点。我认为 add a field in pandas dataframe with MultiIndex columns 可能包含答案,但我不太了解解决方案(甚至不太了解面板是什么以及它是否可以帮助我)。

编辑:我试过的东西,使用 concat:

In [36]: concat([df['sand'] + df['silt']], axis=1, keys=['notclay']).head()
Out[36]: 
     notclay            
          5     25    60
STID                    
ACME    92.7  84.2  78.4
ADAX    83.1  76.5  72.2
ALTU    63.8  63.0  55.2
ALV2    64.7  50.9  49.6
ANT2    90.5  88.6  74.5

In [37]: df['notclay'] = concat([df['sand'] + df['silt']], axis=1, keys=['notclay'])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)

<snip>

ValueError: Wrong number of items passed 3, placement implies 1

与上面提出的 ValueError 相同。

根据您的喜好,这可能是一种更好的方法,仍然使用 concat:

In [53]: df
Out[53]: 
       blah                           foo                    
          1         2         3         1         2         3
a  0.351045  0.044654  0.855627  0.839725  0.675183  0.325324
b  0.610374  0.394499  0.924708  0.924303  0.404475  0.885368
c  0.116418  0.487866  0.190669  0.283535  0.862869  0.346477
d  0.771014  0.204143  0.143449  0.848520  0.887373  0.220083
e  0.103268  0.306820  0.277125  0.627272  0.631019  0.386406

In [54]: newdf
Out[54]: 
          1         2         3
a  0.433377  0.806679  0.976298
b  0.593683  0.217415  0.086565
c  0.716244  0.908777  0.180252
d  0.031942  0.074283  0.745019
e  0.651517  0.393569  0.861616

In [56]: newdf.columns=pd.MultiIndex.from_product([['bar'], newdf.columns])

In [57]: pd.concat([df, newdf], axis=1)
Out[57]: 
       blah                           foo                           bar  \
          1         2         3         1         2         3         1   
a  0.351045  0.044654  0.855627  0.839725  0.675183  0.325324  0.433377   
b  0.610374  0.394499  0.924708  0.924303  0.404475  0.885368  0.593683   
c  0.116418  0.487866  0.190669  0.283535  0.862869  0.346477  0.716244   
d  0.771014  0.204143  0.143449  0.848520  0.887373  0.220083  0.031942   
e  0.103268  0.306820  0.277125  0.627272  0.631019  0.386406  0.651517   


          2         3  
a  0.806679  0.976298  
b  0.217415  0.086565  
c  0.908777  0.180252  
d  0.074283  0.745019  
e  0.393569  0.861616 

为了将其存储到原始数据框中,您可以在最后一行简单地分配给它:

In [58]: df = pd.concat([df, newdf], axis=1)