2016-11-14 57 views
2

,我有以下的數據幀:分配GROUPBY申請結果於母公司的數據幀

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) must equal the number of levels in the index of "right"

如何添加normed結果傳回數據幀df

+0

請注意,如果使用'transform'而不是'apply',則問題基本消失。你也可以使用groupby('A')['C']'而不是'groupby('A')'來獲得更乾淨的代碼。有關完整的語法,請參閱下面的答案。 – JohnE

回答

3

由於在第一級中有一個長度爲2的MultiIndex,所以出現此錯誤。第二層是原始索引。

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') 
+0

不錯的一個,我不知道'.droplevel()'方法 – MMF

+0

這是一個很好的解決方案。我以爲你需要在''normed''上''.reset_index()'然後做一些奇特的佈局變化。這是重用原始索引的一個很好的簡單方法。 –

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 

注:也許有一個聰明的方式來做到這一點。

1

你必須擺脫這是由groupby first(即'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 
2

最簡單的方法是應用reset_indexnormed

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

實際上,有一個非常簡單的解決方案。當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']適用,但不適用(我不知道爲什麼......)。

+0

羣不會扭曲命令嗎?爲了安全起見,您可以添加a.'sort_index(level = 1)' –

+0

@MaartenFabré謝謝,您是正確的。我已經更新了我的答案,以便不再成爲問題。 – JohnE