将groupby-apply结果分配给父数据帧

时间:2016-11-14 15:53:07

标签: python pandas

我有以下数据框:

df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar','foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8),
                   'D' : np.random.randn(8)})

    A   B   C   D
0   foo one 0.478183    -1.267588
1   bar one 0.555985    -2.143590
2   foo two -1.592865   1.251546
3   bar three   0.174138    -0.708198
4   foo two 0.302215    -0.219041
5   bar two -0.034550   -0.965414
6   foo one 1.310828    -0.388601
7   foo three   0.357659    -1.610443

我正在尝试添加另一个列,该列将是 C 列的标准化版本,而不是A:

normed = df.groupby('A').apply(lambda x: (x['C']-min(x['C']))/(max(x['C'])-min(x['C'])))

A     
bar  1    0.000000
     3    0.033396
     5    1.000000
foo  0    1.000000
     2    0.413716
     4    0.000000
     6    0.441061
     7    0.357787

最后我想把这个结果加回到df(使用来自similar question的建议):

df.join(normed, on='A', rsuffix='_normed')

然而,我收到错误:

  

ValueError:len(left_on)必须等于索引中的级别数   “正确的”

如何将normed结果添加回数据框df

5 个答案:

答案 0 :(得分:3)

您收到此错误是因为您在第一级中有MultiIndex长度为2的情况。第二级是原始索引。

normed.index

Out[35]:

MultiIndex(levels=[['bar', 'foo'], [0, 1, 2, 3, 4, 5, 6, 7]],
           labels=[[0, 0, 0, 1, 1, 1, 1, 1], [1, 3, 5, 0, 2, 4, 6, 7]],
           names=['A', None])

您可能希望加入原始索引,因此必须删除新索引的第一级

normed.index = normed.index.droplevel()

加入之前:

df.join(normed, rsuffix='_normed')

答案 1 :(得分:2)

最简单的方法是将reset_index应用于normed

normed = df.groupby('A').apply(lambda x: (x['C']-min(x['C']))/(max(x['C'])-min(x['C'])))
normed = normed.reset_index(level=0, drop=True)

现在只需将normed添加为df

的列
df['normed'] = normed

答案 2 :(得分:2)

实际上,有一个非常简单的解决方案。当groupby正在进行一对一操作(而不是减少)时,您可以使用transform并且已经为您处理了索引:

df['c_normed'] = df.groupby('A')['C'].transform( lambda x: (x-min(x))/(max(x)-min(x)))

另请注意,如果您使用df.groupby('A')['C'],代码会更清晰,因为您可以在lambda中使用x而不是x['C']。而且在这种情况下使用x['C']使用apply但不使用transform(我不知道为什么......)。

答案 3 :(得分:1)

您可以做的是:

# Get tuples (index, value) for each level
foo = zip(normed.foo.index, normed.foo.values)
bar = zip(normed.bar.index, normed.bar.values)

# Merge the two lists
foo.extend(bar) # merged lists contained in foo

# Sort the list
new_list = sorted(foo, key=lambda x: x[0])

# Create new column in dataframe
index, values = zip(*new_list) # unzip
df['New_column'] = values

<强>输出

Out[85]: 
 A      B         C         D  New_column
0  foo    one  0.039683 -0.041559    0.638594
1  bar    one -0.090650 -2.316097    0.000000
2  foo    two  0.024210  0.616764    0.629815
3  bar  three  0.142740  0.156198    0.450339
4  foo    two -1.085916 -0.432832    0.000000
5  bar    two  0.427604 -1.154850    1.000000
6  foo    one -0.156424  0.037188    0.527335
7  foo  three  0.676706 -1.336921    1.000000

注意:也许有一种更聪明的方法可以做到这一点。

答案 4 :(得分:1)

你必须首先摆脱groupby创建的多索引的第一级(即'Foo'和'Bar')。

添加以下代码应该有效:

normed = normed.reset_index(level=0)
del normed['A']
normed.rename(columns={'C':'C_normed'}, inplace=True)
pd.concat([df, normed], axis=1)

结果:

A   B   C   D   C_normed
0   foo one 1.697923    0.656727    1.000000
1   bar one -0.626052   -0.466088   0.000000
2   foo two -0.501440   1.080408    0.000000
3   bar three   0.731791    -1.531915   1.000000
4   foo two -0.202666   0.275042    0.135846
5   bar two -0.340455   -0.737039   0.210332
6   foo one 0.506664    1.049853    0.458362
7   foo three   -0.358317   -0.598262   0.065075