Niejednoznaczność w definicji „osi” Pandas Dataframe / Numpy Array

92

Byłem bardzo zdezorientowany, jak definiowane są osie Pythona i czy odnoszą się do wierszy czy kolumn DataFrame. Rozważ poniższy kod:

>>> df = pd.DataFrame([[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]], columns=["col1", "col2", "col3", "col4"])
>>> df
   col1  col2  col3  col4
0     1     1     1     1
1     2     2     2     2
2     3     3     3     3

Więc jeśli zadzwonimy df.mean(axis=1), otrzymamy średnią między wierszami:

>>> df.mean(axis=1)
0    1
1    2
2    3

Jeśli jednak zadzwonimy df.drop(name, axis=1), faktycznie upuścimy kolumnę , a nie wiersz:

>>> df.drop("col4", axis=1)
   col1  col2  col3
0     1     1     1
1     2     2     2
2     3     3     3

Czy ktoś może mi pomóc zrozumieć, co oznacza „oś” w pandach / numpy / scipy?

Na marginesie, DataFrame.meanmoże być źle zdefiniowane. W dokumentacji jest napisane, DataFrame.meanże axis=1ma to oznaczać średnią nad kolumnami, a nie wierszami ...

hlin117
źródło
Szczegółowe wyjaśnienie aliasów, „kolumn” i „indeksu” / „wierszy” zawiera poniższa odpowiedź .
Ted Petrou
To jest po prostu dziwne. Oś powinna być spójna w poprzek meani drop. Potrzeba nieliniowego myślenia, aby dojść do rzeczywistego zachowania.
StephenBoesch

Odpowiedzi:

168

Być może najłatwiej jest zapamiętać to jako 0 = w dół i 1 = w poprzek .

To znaczy:

  • Służy axis=0do stosowania metody w każdej kolumnie lub do etykiet wierszy (indeksu).
  • Służy axis=1do stosowania metody do każdego wiersza lub do etykiet kolumn.

Oto rysunek przedstawiający części DataFrame, do których odwołuje się każda oś:

Warto również pamiętać, że Pandy podążają za użyciem tego słowa przez NumPy axis. Użycie jest wyjaśnione w słowniczku terminów NumPy :

Osie są definiowane dla tablic z więcej niż jednym wymiarem. Dwuwymiarowa tablica ma dwie odpowiadające sobie osie: pierwsza biegnąca pionowo w dół przez rzędy (oś 0) , a druga biegnąca poziomo w poprzek kolumn (oś 1) . [ moje podkreślenie ]

Tak więc, jeśli chodzi o metodę, o której mowa df.mean(axis=1), wydaje się, że jest ona poprawnie zdefiniowana. Przyjmuje średnią wpisów poziomo w kolumnach , to znaczy wzdłuż każdego pojedynczego wiersza. Z drugiej strony df.mean(axis=0)byłaby to operacja działająca pionowo w dół w poprzek rzędów .

Podobnie df.drop(name, axis=1)odnosi się do akcji na etykietach kolumn, ponieważ intuicyjnie przechodzą one przez oś poziomą. Określenie axis=0spowoduje, że metoda będzie działać na wierszach.

Alex Riley
źródło
3
To, co mnie zmusiło, to fakt, że df.apply (..., axis = 0), nie "przejechał" przez oś 0 (indeks), ale przeszedł przez kolumny, przywracając serię zawierającą wszystkie indeksy. Chodzi o to, że df.apply (..., axis = 0) zwraca Series, więc TY możesz zastosować operację przebiegającą po całym indeksie.
moritzschaefer
2
Myślę, że pomaga również, jeśli postrzegasz ją df.applyjako podobną do metody takiej jak df.sum. Na przykład df.sum(axis=0)sumuje każdą kolumnę DataFrame. Podobnie możesz napisać, df.apply(sum, axis=0)aby wykonać dokładnie tę samą operację. Chociaż operacja jest rzeczywiście stosowana do każdej kolumny w DataFrame, rzeczywista funkcja biegnie w dół osi 0.
Alex Riley
Jest to niefortunne, że nazwy i porządek konwencje są naprzeciwko R tych funkcji zastosowania - w R, dolna MARGIN(podobną do axisw pand) wartość „1” odpowiada „rzędów”, co oznacza, że funkcja jest stosowana do każdego rzędu , przy czym większa wartość „2” odnosi się do „kolumn”, co oznacza, że ​​funkcja jest stosowana do każdej kolumny .
Keith Hughitt
to destrukcyjny błąd w pandach
Calculus
10

Inny sposób wyjaśnienia:

// Not realistic but ideal for understanding the axis parameter 
df = pd.DataFrame([[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]],
                  columns=["idx1", "idx2", "idx3", "idx4"],
                  index=["idx1", "idx2", "idx3"]
                 )

---------------------------------------1
|          idx1  idx2  idx3  idx4
|    idx1     1     1     1     1
|    idx2     2     2     2     2
|    idx3     3     3     3     3
0

O df.drop(oś oznacza pozycję)

A: I wanna remove idx3.
B: **Which one**? // typing while waiting response: df.drop("idx3",
A: The one which is on axis 1
B: OK then it is >> df.drop("idx3", axis=1)

// Result
---------------------------------------1
|          idx1  idx2     idx4
|    idx1     1     1     1
|    idx2     2     2     2
|    idx3     3     3     3
0

O df.apply(oś oznacza kierunek)

A: I wanna apply sum.
B: Which direction? // typing while waiting response: df.apply(lambda x: x.sum(),
A: The one which is on *parallel to axis 0*
B: OK then it is >> df.apply(lambda x: x.sum(), axis=0)

// Result
idx1    6
idx2    6
idx3    6
idx4    6
o0omycomputero0o
źródło
Czy nie sądzisz, że na osi 1 i równolegle do osi 0 oznacza to samo?
Nuance
9

Są już prawidłowe odpowiedzi, ale podam inny przykład z> 2 wymiarami.

Parametr axisoznacza oś do zmiany .
Na przykład weźmy pod uwagę, że istnieje ramka danych o wymiarze axbxc .

  • df.mean(axis=1)zwraca ramkę danych o wymiarach ax 1 xc .
  • df.drop("col4", axis=1)zwraca ramkę danych o wymiarze ax (b-1) xc .

Tutaj axis=1oznacza drugą oś, która jest b, więc bwartość zostanie zmieniona w tych przykładach.

jeongmin.cha
źródło
1
Ta odpowiedź jest dla mnie bardziej intuicyjna niż jakakolwiek wizualizacja, którą widziałem na ten temat. Jednak xarray jest lepszy w przypadku tablic wielowymiarowych niż pandy.
alys
2

Powinno być szerzej znane, że aliasy ciągów „indeks” i „kolumny” mogą być używane zamiast liczb całkowitych 0/1. Aliasy są znacznie bardziej wyraźne i pomagają mi zapamiętać, jak przebiegają obliczenia. Innym aliasem dla „indeksu” są „wiersze” .

Kiedy axis='index'jest używane, obliczenia odbywają się w kolumnach, co jest mylące. Ale pamiętam to jako otrzymanie wyniku, który jest tego samego rozmiaru co inny wiersz.

Wyświetlmy trochę danych na ekranie, aby zobaczyć, o czym mówię:

df = pd.DataFrame(np.random.rand(10, 4), columns=list('abcd'))
          a         b         c         d
0  0.990730  0.567822  0.318174  0.122410
1  0.144962  0.718574  0.580569  0.582278
2  0.477151  0.907692  0.186276  0.342724
3  0.561043  0.122771  0.206819  0.904330
4  0.427413  0.186807  0.870504  0.878632
5  0.795392  0.658958  0.666026  0.262191
6  0.831404  0.011082  0.299811  0.906880
7  0.749729  0.564900  0.181627  0.211961
8  0.528308  0.394107  0.734904  0.961356
9  0.120508  0.656848  0.055749  0.290897

Kiedy chcemy wziąć średnią ze wszystkich kolumn, używamy, axis='index'aby uzyskać:

df.mean(axis='index')
a    0.562664
b    0.478956
c    0.410046
d    0.546366
dtype: float64

Ten sam wynik dałoby:

df.mean() # default is axis=0
df.mean(axis=0)
df.mean(axis='rows')

Aby uzyskać operację od lewej do prawej na wierszach, użyj axis = 'columns'. Pamiętam to myśląc, że do mojej DataFrame można dodać dodatkową kolumnę:

df.mean(axis='columns')
0    0.499784
1    0.506596
2    0.478461
3    0.448741
4    0.590839
5    0.595642
6    0.512294
7    0.427054
8    0.654669
9    0.281000
dtype: float64

Ten sam wynik dałoby:

df.mean(axis=1)

Dodaj nowy wiersz z osią = 0 / indeks / wiersze

Wykorzystajmy te wyniki, aby dodać dodatkowe wiersze lub kolumny w celu uzupełnienia wyjaśnienia. Tak więc za każdym razem, gdy używasz axis = 0 / index / rows, jest to jak pobranie nowego wiersza DataFrame. Dodajmy wiersz:

df.append(df.mean(axis='rows'), ignore_index=True)

           a         b         c         d
0   0.990730  0.567822  0.318174  0.122410
1   0.144962  0.718574  0.580569  0.582278
2   0.477151  0.907692  0.186276  0.342724
3   0.561043  0.122771  0.206819  0.904330
4   0.427413  0.186807  0.870504  0.878632
5   0.795392  0.658958  0.666026  0.262191
6   0.831404  0.011082  0.299811  0.906880
7   0.749729  0.564900  0.181627  0.211961
8   0.528308  0.394107  0.734904  0.961356
9   0.120508  0.656848  0.055749  0.290897
10  0.562664  0.478956  0.410046  0.546366

Dodaj nową kolumnę o osi = 1 / kolumny

Podobnie, gdy oś = 1 / kolumny utworzy dane, które można łatwo wprowadzić do własnej kolumny:

df.assign(e=df.mean(axis='columns'))

          a         b         c         d         e
0  0.990730  0.567822  0.318174  0.122410  0.499784
1  0.144962  0.718574  0.580569  0.582278  0.506596
2  0.477151  0.907692  0.186276  0.342724  0.478461
3  0.561043  0.122771  0.206819  0.904330  0.448741
4  0.427413  0.186807  0.870504  0.878632  0.590839
5  0.795392  0.658958  0.666026  0.262191  0.595642
6  0.831404  0.011082  0.299811  0.906880  0.512294
7  0.749729  0.564900  0.181627  0.211961  0.427054
8  0.528308  0.394107  0.734904  0.961356  0.654669
9  0.120508  0.656848  0.055749  0.290897  0.281000

Wygląda na to, że możesz zobaczyć wszystkie aliasy z następującymi zmiennymi prywatnymi:

df._AXIS_ALIASES
{'rows': 0}

df._AXIS_NUMBERS
{'columns': 1, 'index': 0}

df._AXIS_NAMES
{0: 'index', 1: 'columns'}
Ted Petrou
źródło
1

Gdy oś = „rzędy” lub oś = 0, oznacza to dostęp do elementów w kierunku wierszy, od góry do dołu. Jeśli zastosujesz sumę wzdłuż osi = 0, da nam to sumy każdej kolumny.

Gdy oś = 'kolumny' lub oś = 1, oznacza to dostęp do elementów w kierunku kolumn, od lewej do prawej. Jeśli zastosujemy sumę wzdłuż osi = 1, otrzymamy sumy z każdego wiersza.

Wciąż mylące! Ale powyższe sprawia, że ​​jest mi trochę łatwiej.

Nowicjusz
źródło
0

Wszystkie inne odpowiedzi są mylące. Oto jak o tym myślę:

axis=0: kształt wyniku jest poziomy (wiersz)
axis=1: kształt wyniku jest pionowy (kolumna)

Więc

  • df.drop(name, axis=1): upuszcza kolumnę
  • df.mean(axis=1): oblicza kolumnę (wynik można dodać jako nową kolumnę)
AXO
źródło