2016-06-07 72 views
2

我有一個DataFrame其中的列是MultiIndex。第一個level指定'labels',第二個指定'values'。 A 'label'在的位置df.labels對應於'value'(i, j)位置df.valuesgroupby和歸一化兩個陣列

我想重新調整'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 
+0

能闡明什麼是總和爲1在你預期的結果? – BrenBarn

+0

@BrenBarn對應的標籤'a'的所有值應該總和爲1. – piRSquared

+1

我明白了。你有一個答案在下面。不過,一般來說,如果您重新整理數據以便每行都是單一觀察結果,那麼我認爲像這樣的操作可以更直接地處理。例如,一行將包含「label」,「number」(您的0-1-2-3-4)和「value」的列。然後在任何這些組上進行分組變得很簡單。 – BrenBarn

回答

2

要獲得標準化的值,你可以:

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

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 
1

雖然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一致的單個列。這是該堆疊

    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末,以避免產生正確的指數時,我知道我已經有異常的head()按正確順序排列值。


最後的答案

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

你能解釋一下你的代碼嗎? –