Mam ramkę danych, która używa MultiIndex zarówno dla indeksu, jak i kolumn. Na przykład:

df = pd.DataFrame(index=pd.MultiIndex.from_product([[1,2], [1,2,3], [4,5]], names=['i','j', 'k']), columns=pd.MultiIndex.from_product([[1,2], [1,2]], names=['x', 'y']))
for c in df.columns:
    df[c] = np.random.randint(100, size=(12,1))

x       1       2
y       1   2   1   2
i j k
1 1 4  10  13   0  76
    5  92  37  52  40
  2 4  88  77  50  22
    5  75  31  19   1
  3 4  61  23   5  47
    5  43  68  10  21
2 1 4  23  15  17   5
    5  47  68   6  94
  2 4   0  12  24  54
    5  83  27  46  19
  3 4   7  22   5  15
    5   7  10  89  79

Chcę pogrupować wartości według nazwy w indeksie i nazwy w kolumnach. Dla każdej takiej grupy będziemy mieć dwuwymiarową tablicę liczb (zamiast serii). Chcę zagregować std() wszystkich wpisów w tej tablicy 2D.

Na przykład, powiedzmy, że grupuję według ['i', 'x'], jedna grupa będzie miała wartości i=1 i x=1. Chcę obliczyć std dla każdej z tych tablic 2D i utworzyć DataFrame z i wartościami jako indeksem i x wartościami jako kolumnami.

Jaki jest najlepszy sposób, aby to osiągnąć?

Jeśli zrobię stack(), aby uzyskać x jako indeks, nadal będę obliczać kilka std() zamiast jednej, ponieważ nadal będzie wiele kolumn.

1
Suresh 3 kwiecień 2020, 13:29

3 odpowiedzi

Najlepsza odpowiedź

Możesz używać zagnieżdżonych list składanych. Na przykład z danym rodzajem DataFrame (nie tym samym, ponieważ wartości są losowe; możesz chcieć ustalić wartość początkową, aby wyniki były porównywalne) oraz i i x jako indeksy interesujące, to działałoby tak:

# get values of the top level row index
rows = set(df.index.get_level_values(0))
# get values of the top level column index
columns = set(df.columns.get_level_values(0))

# for every sub-dataframe (every combination of top-level indices) 
# compute sampling standard deviation (1 degree of freedom) across all values
df_groupSD = pd.DataFrame([[df.loc[(row, )][(col, )].values.std(ddof=1)  
                            for col in columns] for row in rows], 
                          index = rows, columns = columns)

# show result
display(df_groupSD)  

Wynik:

    1           2
1   31.455115   25.433812
2   29.421699   33.748962

Oczywiście mogą istnieć lepsze sposoby.

1
Arne 3 kwiecień 2020, 13:28

Możesz użyć stack, aby umieścić poziom „y” kolumny jako indeks, a następnie groupby tylko i, aby uzyskać:

print (df.stack(level='y').groupby(['i']).std())
x          1          2
i                      
1  32.966811  23.933462
2  28.668825  28.541835
1
Ben.T 3 kwiecień 2020, 11:12

Wypróbuj poniższy kod:

df.groupby(level=0).apply(lambda grp: grp.stack().std())
1
Valdi_Bo 3 kwiecień 2020, 13:00