groupby 并规范化两个数组
groupby and normalize over two arrays
我有一个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
虽然 pd.DataFrame.xs
可以方便地检索一些切片:
df.xs('values', axis=1, level=0)
不幸的是,它不允许我们分配。如果我们想使用 pd.DataFrame.loc
,我们需要能够指定我们想要分配给的行和列索引。
使用 pd.IndexSlice
将 pd.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
此时 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
还有一个有点难读的单行本:
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
我有一个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
虽然 pd.DataFrame.xs
可以方便地检索一些切片:
df.xs('values', axis=1, level=0)
不幸的是,它不允许我们分配。如果我们想使用 pd.DataFrame.loc
,我们需要能够指定我们想要分配给的行和列索引。
使用
pd.IndexSlice
将pd.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
此时
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
还有一个有点难读的单行本:
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