Ambiguità nella definizione di "asse" di Pandas Dataframe / Numpy Array


91

Sono stato molto confuso su come vengono definiti gli assi Python e se si riferiscono a righe o colonne di un DataFrame. Considera il codice seguente:

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

Quindi, se chiamiamo df.mean(axis=1), otterremo una media tra le righe:

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

Tuttavia, se chiamiamo df.drop(name, axis=1), in realtà rilasciamo una colonna , non una riga:

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

Qualcuno può aiutarmi a capire cosa si intende per "asse" in panda / numpy / scipy?

Una nota a margine, DataFrame.meanpotrebbe essere definita sbagliata. Nella documentazione si dice DataFrame.meanche axis=1dovrebbe significare una media sulle colonne, non sulle righe ...


Per una spiegazione dettagliata degli alias, "colonne" e "indice" / "righe", vedere la risposta di seguito .
Ted Petrou

Questo è solo strano. L'asse dovrebbe essere coerente tra i meane i drop. Ci vuole un pensiero non lineare per arrivare al comportamento reale.
StephenBoesch

Risposte:


167

Forse è più semplice ricordarlo come 0 = down e 1 = across .

Questo significa:

  • Utilizzare axis=0per applicare un metodo in ogni colonna o alle etichette delle righe (l'indice).
  • Utilizzare axis=1per applicare un metodo su ogni riga o sulle etichette delle colonne.

Ecco un'immagine per mostrare le parti di un DataFrame a cui si riferisce ogni asse:

È anche utile ricordare che Panda segue l'uso della parola da parte di NumPy axis. L'utilizzo è spiegato nel glossario dei termini di NumPy :

Gli assi sono definiti per array con più di una dimensione. Un array bidimensionale ha due assi corrispondenti: il primo che corre verticalmente verso il basso attraverso le righe (asse 0) e il secondo che corre orizzontalmente tra le colonne (asse 1) . [la mia enfasi ]

Quindi, per quanto riguarda il metodo in questione df.mean(axis=1), sembra essere correttamente definito. Prende la media delle voci orizzontalmente tra le colonne , cioè lungo ogni singola riga. D'altra parte, df.mean(axis=0)sarebbe un'operazione che agisce verticalmente verso il basso attraverso le file .

Allo stesso modo, si df.drop(name, axis=1)riferisce a un'azione sulle etichette delle colonne, perché intuitivamente attraversano l'asse orizzontale. Specificando invece axis=0il metodo agirà sulle righe.


3
Ciò che mi ha fatto soffrire è stato che df.apply (..., axis = 0), non "passava" sull'asse 0 (l'indice), ma correva sulle colonne, recuperando le serie contenenti tutti gli indici. L'indizio è che df.apply (..., axis = 0) restituisce Series così TU puoi applicare un'operazione che corre sull'indice completo.
moritzschaefer

2
Penso che sia utile anche se si considera df.applysimile a un metodo come df.sum. Ad esempio, df.sum(axis=0)somma ogni colonna del DataFrame. Allo stesso modo, puoi scrivere df.apply(sum, axis=0)per fare esattamente la stessa operazione. Mentre l'operazione è effettivamente applicata a ciascuna colonna nel DataFrame, la funzione effettiva corre lungo l'asse 0.
Alex Riley

È un peccato che le convenzioni di denominazione e ordine siano l' opposto della funzione di applicazione di R : in R, il valore inferiore MARGIN(simile a axispanda) di "1" corrisponde a "righe", il che significa che la funzione è applicata a ciascuna riga , mentre il il valore più grande di "2" si riferisce a "colonne", il che significa che la funzione viene applicata a ciascuna colonna .
Keith Hughitt

è un bug distruttivo nei panda
Calcolo

10

Un altro modo per spiegare:

// 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

Circa df.drop(l'asse indica la posizione)

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

Circa df.apply(asse significa direzione)

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

Non pensi che sull'asse 1 e parallelo all'asse 0 significhi lo stesso?
Nuance

9

Ci sono già risposte corrette, ma ti do un altro esempio con> 2 dimensioni.

Il parametro axisindica l' asse da modificare .
Ad esempio, considera che esiste un dataframe con dimensione axbxc .

  • df.mean(axis=1)restituisce un dataframe con dimensione ax 1 xc .
  • df.drop("col4", axis=1)restituisce un dataframe con dimensione ax (b-1) xc .

Qui, axis=1significa il secondo asse che è b, quindi il bvalore verrà modificato in questi esempi.


1
Questa risposta è più intuitiva per me di qualsiasi visualizzazione che ho visto su questo argomento. Tuttavia, xarray è migliore per gli array multidimensionali rispetto ai panda.
alys

2

Dovrebbe essere più ampiamente noto che gli alias di stringa "index" e "colonne" possono essere utilizzati al posto degli interi 0/1. Gli alias sono molto più espliciti e mi aiutano a ricordare come avvengono i calcoli. Un altro alias per "indice" è "righe" .

Quando axis='index'viene utilizzato, i calcoli vengono eseguiti lungo le colonne, il che crea confusione. Ma ricordo che ottengo un risultato della stessa dimensione di un'altra riga.

Otteniamo alcuni dati sullo schermo per vedere di cosa sto parlando:

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

Quando vogliamo prendere la media di tutte le colonne, usiamo axis='index'per ottenere quanto segue:

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

Lo stesso risultato si otterrebbe:

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

Per utilizzare un'operazione da sinistra a destra sulle righe, utilizzare axis = 'columns'. Me lo ricordo pensando che una colonna aggiuntiva può essere aggiunta al mio DataFrame:

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

Lo stesso risultato si otterrebbe:

df.mean(axis=1)

Aggiungi una nuova riga con asse = 0 / indice / righe

Usiamo questi risultati per aggiungere ulteriori righe o colonne per completare la spiegazione. Quindi, ogni volta che si utilizza axis = 0 / index / rows, è come ottenere una nuova riga del DataFrame. Aggiungiamo una riga:

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

Aggiungi una nuova colonna con asse = 1 / colonne

Allo stesso modo, quando l'asse = 1 / colonne creerà dati che possono essere facilmente inseriti nella propria colonna:

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

Sembra che tu possa vedere tutti gli alias con le seguenti variabili private:

df._AXIS_ALIASES
{'rows': 0}

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

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

1

Quando asse = 'righe' o asse = 0, significa accedere agli elementi nella direzione delle righe, dall'alto verso il basso. Se si applica la somma lungo l'asse = 0, ci darà i totali di ogni colonna.

Quando asse = 'colonne' o asse = 1, significa accedere agli elementi nella direzione delle colonne, da sinistra a destra. Se applichiamo la somma lungo l'asse = 1, otterremo i totali di ogni riga.

Ancora confuso! Ma quanto sopra mi rende un po 'più facile.


0

Trovo tutte le altre risposte confuse. Ecco come la penso io:

axis=0: la forma del risultato è orizzontale (una riga)
axis=1: la forma del risultato è verticale (una colonna)

Così

  • df.drop(name, axis=1): rilascia una colonna
  • df.mean(axis=1): calcola una colonna (il risultato può essere aggiunto come una nuova colonna)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.