对两个数组进行分组和归一化

Posted

技术标签:

【中文标题】对两个数组进行分组和归一化【英文标题】:groupby and normalize over two arrays 【发布时间】:2016-10-07 16:17:51 【问题描述】:

我有一个DataFrame,其中的列是MultiIndex。第一个level 指定'labels',第二个指定'values'df.labels(i, j) 位置中的'label' 对应于df.values(i, j) 位置中的'value'

我想重新调整'values',使它们在相应'labels' 定义的每个组中总和为一个。

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

【问题讨论】:

能否澄清您的预期结果中的总和为 1? @BrenBarn 对应标签为“a”的所有值的总和应为 1。 我明白了。你在下面有一个答案。不过,总的来说,我认为如果您重塑数据以使每一行都是一个单独的观察值,则此类操作会更直接地处理。例如,一行将包含“标签”、“数字”(您的 0-1-2-3-4)和“值”列。然后,对其中任何一个进行分组变得简单。 @BrenBarn 我同意。因此,这成为了重塑、分组和分配的练习。我在下面有我的解决方案(对于那些没有特权的人,它已被删除,因此您看不到它),这就是我生成预期输出的方式。 如果解决方案有效,您为什么要删除它? 【参考方案1】:

要获得标准化值,您可以:

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

也作为一个有点不可读的单行:

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()

tmp = pd.DataFrame(np.hstack((df['labels'].values.reshape(-1, 1), df['values'].values.reshape(-1, 1))))
df.loc[:, 'values'] = tmp.groupby(0).transform(lambda x: x/x.sum()).values.reshape(df['values'].shape)

两者都导致:

  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

【讨论】:

【参考方案2】:

虽然pd.DataFrame.xs 可以方便地检索一些切片:

df.xs('values', axis=1, level=0)

很遗憾,它不允许我们分配。如果我们想使用pd.DataFrame.loc,我们需要能够指定我们想要分配的行和列索引。

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

pd.IndexSlice['values', :]

当我们将它与pd.DataFrame.loc 结合使用时,我们允许自己分配给pd.DataFrame 的非常特定的切片。以下检索并允许无限制地分配给所有行,并将列限制为第一级等于'values'的列

df.loc[:, pd.IndexSlice['values', :]]

为了标准化labels 部分中的值,我将转到stack() df,以便将所有'labels' 展开到与values 对齐的单个列中。这是这个堆叠的head()

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
1234563正确的顺序。

最终答案

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

【讨论】:

以上是关于对两个数组进行分组和归一化的主要内容,如果未能解决你的问题,请参考以下文章

四元数和归一化

归一化和标准化的一些理解

变异位点的归一化(normalization of indel)

数据标准化和数据归一化有啥区别吗?

正则化和归一化

sklearn进行对数据标准化、归一化以及将数据还原的方法