Python 两个数组上的groupby和normalize

Python 两个数组上的groupby和normalize,python,numpy,pandas,group-by,multi-index,Python,Numpy,Pandas,Group By,Multi Index,我有一个DataFrame,其中列是一个多索引。第一个级别指定'labels',第二个指定'values'。(i,j)测向标签位置中的'label'对应于(i,j)测向值位置中的'value' 我想重新缩放“值”,使它们在相应的“标签”定义的每个组中加一 import pandas as pd import numpy as np np.random.seed([3,1415]) df1 = pd.DataFrame(np.random.choice(('a', 'b', 'c', 'd'),

我有一个
DataFrame
,其中列是一个
多索引
。第一个
级别指定
'labels'
,第二个指定
'values'
(i,j)
测向标签位置中的
'label'
对应于
(i,j)
测向值位置中的
'value'

我想重新缩放
“值”
,使它们在相应的
“标签”
定义的每个组中加一

import pandas as pd
import numpy as np

np.random.seed([3,1415])
df1 = pd.DataFrame(np.random.choice(('a', 'b', 'c', 'd'),
                                    (10, 5), p=(.4, .3, .2, .1)))
df2 = pd.DataFrame((np.random.rand(10, 5) * 10).round(0))

df = pd.concat([df1, df2], axis=1, keys=['labels', 'values'])
print df

  labels             values                     
       0  1  2  3  4      0    1     2    3    4
0      b  b  b  b  b    5.0  2.0   7.0  7.0  4.0
1      a  c  c  c  c    6.0  8.0   1.0  5.0  7.0
2      d  c  c  d  c    6.0  3.0  10.0  7.0  4.0
3      a  a  a  b  a    5.0  9.0   9.0  5.0  8.0
4      a  b  a  c  c    0.0  4.0   1.0  8.0  0.0
5      c  b  a  a  b    1.0  6.0   8.0  6.0  1.0
6      c  c  c  a  c    9.0  9.0   4.0  1.0  1.0
7      d  c  a  b  c    7.0  0.0   3.0  6.0  4.0
8      b  a  b  a  a    8.0  6.0   3.0  5.0  4.0
9      c  c  c  b  c    2.0  5.0   3.0  1.0  3.0
我预计结果如下:

  labels                values                                        
       0  1  2  3  4         0         1         2         3         4
0      b  b  b  b  b  0.084746  0.033898  0.118644  0.118644  0.067797
1      a  c  c  c  c  0.084507  0.091954  0.011494  0.057471  0.080460
2      d  c  c  d  c  0.300000  0.034483  0.114943  0.350000  0.045977
3      a  a  a  b  a  0.070423  0.126761  0.126761  0.084746  0.112676
4      a  b  a  c  c  0.000000  0.067797  0.014085  0.091954  0.000000
5      c  b  a  a  b  0.011494  0.101695  0.112676  0.084507  0.016949
6      c  c  c  a  c  0.103448  0.103448  0.045977  0.014085  0.011494
7      d  c  a  b  c  0.350000  0.000000  0.042254  0.101695  0.045977
8      b  a  b  a  a  0.135593  0.084507  0.050847  0.070423  0.056338
9      c  c  c  b  c  0.022989  0.057471  0.034483  0.016949  0.034483
同时,可以方便地检索某些切片:

df.xs('values', axis=1, level=0)
不幸的是,它不允许我们分配。如果我们想使用,我们需要能够指定要分配给的行和列索引

  • 使用
    pd.indexlice
    按不同级别对
    pd.MultiIndex
    进行切片。以下是从第一级访问
    索引的通用表示,第二级没有限制

    pd.IndexSlice['values', :]
    
  • 当我们将其与
    pd.DataFrame.loc
    相结合时,我们允许自己分配给
    pd.DataFrame
    的非常特定的片段。以下内容检索并允许不受限制地分配给所有行,而将列限制给第一级等于“值”的行。

df.loc[:, pd.IndexSlice['values', :]]
  • 为了规范化
    标签
    部分中的值,我将进入
    堆栈()
    df
    ,以便将所有
    标签
    展开到与
    值对齐的单个列中。这是此堆栈的
    头()

    df.stack().head()
    
        labels    values
    0 0      b  0.084746
      1      b  0.033898
      2      b  0.118644
      3      b  0.118644
      4      b  0.067797
    
  • 在这一点上,
    groupby('labels')
    非常简单,唯一的例外是我在末尾使用了
    .values
    ,以避免在我知道已经按正确顺序获得了值数组时必须生成正确的索引


  • 最终答案

    df.loc[:, pd.IndexSlice['values', :]] = \
        df.stack().groupby('labels')['values'].apply(
            lambda x: x / x.sum()).unstack().values
    

    要获得规范化值,可以:

    new_values = pd.DataFrame(data=np.zeros(df['values'].shape))
    for v in np.unique(df['labels']):
        mask = df['values'].where(df['labels'].isin([v]))
        new_values += mask.div(mask.sum().sum()).fillna(0)
    df.loc[:, 'values'] = new_values.values
    
    作为一个有点不可读的oneliner:

    df.loc[:, 'values'] = np.sum([df['values'].where(df['labels'].isin([v])).div(df['values'].where(df['labels'].isin([v])).sum().sum()).fillna(0).values for v in np.unique(df['labels'])], axis=0)
    
    或者,使用
    .groupby()

    两者都会导致:

      labels                values                                        
           0  1  2  3  4         0         1         2         3         4
    0      b  b  b  b  b  0.084746  0.033898  0.118644  0.118644  0.067797
    1      a  c  c  c  c  0.084507  0.091954  0.011494  0.057471  0.080460
    2      d  c  c  d  c  0.300000  0.034483  0.114943  0.350000  0.045977
    3      a  a  a  b  a  0.070423  0.126761  0.126761  0.084746  0.112676
    4      a  b  a  c  c  0.000000  0.067797  0.014085  0.091954  0.000000
    5      c  b  a  a  b  0.011494  0.101695  0.112676  0.084507  0.016949
    6      c  c  c  a  c  0.103448  0.103448  0.045977  0.014085  0.011494
    7      d  c  a  b  c  0.350000  0.000000  0.042254  0.101695  0.045977
    8      b  a  b  a  a  0.135593  0.084507  0.050847  0.070423  0.056338
    9      c  c  c  b  c  0.022989  0.057471  0.034483  0.016949  0.034483
    

    你能解释一下在你的预期结果中什么是和1的总和吗?@BrenBarn所有带有相应标签“a”的值应该和1的总和。我明白了。你有下面的答案。不过,一般来说,我认为,如果您重新调整数据,使每一行都是一个观察值,那么像这样的操作将得到更直接的处理。例如,一行将包含“label”、“number”(您的0-1-2-3-4)和“value”列。然后就可以很容易地对其中任何一个进行分组了。@BrenBarn我同意。因此,这将成为重塑、分组和分配的练习。我在下面有我的解决方案(对于那些没有特权的人,它被删除了,所以你看不到它),这就是我生成预期输出的方式。如果它有效,你为什么要删除你的解决方案?
      labels                values                                        
           0  1  2  3  4         0         1         2         3         4
    0      b  b  b  b  b  0.084746  0.033898  0.118644  0.118644  0.067797
    1      a  c  c  c  c  0.084507  0.091954  0.011494  0.057471  0.080460
    2      d  c  c  d  c  0.300000  0.034483  0.114943  0.350000  0.045977
    3      a  a  a  b  a  0.070423  0.126761  0.126761  0.084746  0.112676
    4      a  b  a  c  c  0.000000  0.067797  0.014085  0.091954  0.000000
    5      c  b  a  a  b  0.011494  0.101695  0.112676  0.084507  0.016949
    6      c  c  c  a  c  0.103448  0.103448  0.045977  0.014085  0.011494
    7      d  c  a  b  c  0.350000  0.000000  0.042254  0.101695  0.045977
    8      b  a  b  a  a  0.135593  0.084507  0.050847  0.070423  0.056338
    9      c  c  c  b  c  0.022989  0.057471  0.034483  0.016949  0.034483