2016-06-07 2 views
2

Ich habe eine DataFrame, wo die Spalten ein MultiIndex sind. Die erste level spezifiziert 'labels', die zweite spezifiziert 'values'. Eine 'label' in der (i, j) Position von df.labels entspricht der 'value' in der (i, j) Position von df.values.groupby und normalisieren über zwei Arrays

Ich möchte die 'values' so skalieren, dass sie in jeder durch die entsprechende 'labels' definierten Gruppe zu eins addiert werden.

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 

Ich erwarte, dass die Ergebnisse wie folgt aussehen:

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

Kann klären, was in Ihrem erwarteten Ergebnis 1 Summieren? – BrenBarn

+0

@BrenBarn alle Werte mit einem entsprechenden Label von 'a' sollte zu 1 summieren. – piRSquared

+1

Ich sehe. Sie haben eine Antwort unten. Im Allgemeinen denke ich, dass Operationen wie diese einfacher gehandhabt werden, wenn Sie Ihre Daten so umformen, dass jede Zeile eine einzige Beobachtung ist. Zum Beispiel würde eine Zeile Spalten für "Label", "Nummer" (Ihre 0-1-2-3-4) und "Wert" enthalten. Es wird dann einfach, auf irgendwelchen von diesen zu gruppieren. – BrenBarn

Antwort

2

die normierten Werte zu erhalten, könnten Sie:

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 

auch als etwas unleserlich 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) 

oder unter Verwendung von .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) 

sowohl Ergebnis in:

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

Während pd.DataFrame.xs es bequem macht einige Scheiben abzurufen:

df.xs('values', axis=1, level=0) 

Es leider nicht uns abzutreten erlaubt. Wenn wir pd.DataFrame.loc verwenden möchten, müssen wir in der Lage sein, die Zeilen- und Spaltenindizes anzugeben, die wir zuordnen möchten.

  • Verwenden pd.IndexSlice eine pd.MultiIndex durch seine verschiedenen Ebenen zu schneiden. Das Folgende ist eine generische Darstellung des Zugriffs auf die values Indizes von der ersten Ebene und ohne Einschränkungen auf der zweiten Ebene.

    pd.IndexSlice['values', :] 
    
  • Wenn wir kombinieren diese mit pd.DataFrame.loc, lassen wir uns auf ganz bestimmte Schichten des pd.DataFrame zuzuweisen. Die folgenden abruft und ermöglicht die Zuordnung zu allen Zeilen ohne Einschränkung und Spalten auf diejenigen beschränkt, deren erste Stufe gleich 'values'

    df.loc[:, pd.IndexSlice['values', :]] 
    
  • Um über den Werten in labels Abschnitt zu normalisieren, ich werde stack()df so, dass ich Wickeln Sie alle 'labels' in eine einzige Spalte, die mit values ausgerichtet ist. Dies ist die head() dieser

    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 
    
  • An diesem Punkt Stapeln der groupby('labels') mit Ausnahme ziemlich geradlinig ist, dass ich .values am Ende verwenden, um zu vermeiden, die richtigen Indizes zu erzeugen, wenn ich weiß, ich habe schon bekam das Array von Werten in der richtigen Reihenfolge.


endgültige Antwort

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

Könnten Sie bitte Ihren Code erklären? –