2016-06-07 1 views
2

У меня есть DataFrame, где находятся столбцы MultiIndex. Первый level указывает 'labels', второй указывает 'values'. A 'label' в положении (i, j)df.labels соответствует 'value' в положении (i, j)df.values.groupby и нормализовать более двух массивов

Я хочу перемасштабировать '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. Это 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 
+0

Не могли бы вы объяснить свой код? –