Ambigüedad en la definición de "eje" de Pandas Dataframe / Numpy Array

91

He estado muy confundido acerca de cómo se definen los ejes de Python y si se refieren a filas o columnas de un DataFrame. Considere el siguiente código:

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

Entonces, si llamamos df.mean(axis=1), obtendremos una media en las filas:

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

Sin embargo, si llamamos df.drop(name, axis=1), en realidad soltamos una columna , no una fila:

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

¿Alguien puede ayudarme a comprender qué se entiende por "eje" en pandas / numpy / scipy?

Una nota al margen, DataFrame.meanpodría definirse mal. Dice en la documentación DataFrame.meanque axis=1se supone que significa una media sobre las columnas, no las filas ...

hlin117
fuente
Para obtener una explicación detallada de los alias, 'columnas' e 'índice' / 'filas', consulte esta respuesta a continuación .
Ted Petrou
Esto es raro. El eje debe ser coherente entre los meany los drop. Se necesita un pensamiento no lineal para llegar a la conducta real.
StephenBoesch

Respuestas:

167

Quizás sea más simple recordarlo como 0 = hacia abajo y 1 = a lo ancho .

Esto significa:

  • Úselo axis=0para aplicar un método en cada columna o en las etiquetas de las filas (el índice).
  • Úselo axis=1para aplicar un método en cada fila o en las etiquetas de las columnas.

Aquí hay una imagen para mostrar las partes de un DataFrame a las que se refiere cada eje:

También es útil recordar que Pandas sigue el uso que hace NumPy de la palabra axis. El uso se explica en el glosario de términos de NumPy :

Los ejes se definen para matrices con más de una dimensión. Una matriz bidimensional tiene dos ejes correspondientes: el primero se ejecuta verticalmente hacia abajo a través de filas (eje 0) y el segundo se ejecuta horizontalmente a través de columnas (eje 1) . [ mi énfasis ]

Entonces, con respecto al método en la pregunta df.mean(axis=1), parece estar correctamente definido. Toma la media de las entradas horizontalmente en las columnas , es decir, a lo largo de cada fila individual. Por otro lado, df.mean(axis=0)sería una operación que actúe verticalmente hacia abajo a través de filas .

Del mismo modo, se df.drop(name, axis=1)refiere a una acción en las etiquetas de las columnas, ya que atraviesan intuitivamente el eje horizontal. Especificar axis=0haría que el método actúe en filas.

Alex Riley
fuente
3
Lo que me hizo luchar fue que df.apply (..., axis = 0), no "pasó" por encima del eje 0 (el índice), sino que corrió sobre las columnas, volviendo a configurar Series que contienen todos los índices. La pista es que df.apply (..., axis = 0) devuelve Series para que USTED pueda aplicar una ejecución operativa sobre el índice completo.
moritzschaefer
2
Creo que también ayuda si lo ve df.applycomo similar a un método como df.sum. Por ejemplo, df.sum(axis=0)suma cada columna del DataFrame. Del mismo modo, puede escribir df.apply(sum, axis=0)para realizar exactamente la misma operación. Si bien la operación se aplica a cada columna en el DataFrame, la función real se ejecuta en el eje 0.
Alex Riley
Es lamentable que las convenciones de nomenclatura y orden sean opuestas a la función de aplicación de R : en R, el valor más bajo MARGIN(similar a axisen pandas) de "1" corresponde a "filas", lo que significa que la función se aplica a cada fila , mientras que la un valor mayor de "2" se refiere a "columnas", lo que significa que la función se aplica a cada columna .
Keith Hughitt
es un error destructivo en pandas
Cálculo
10

Otra forma de explicar:

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

Acerca de df.drop(el eje significa la posición)

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

Acerca de df.apply(eje significa dirección)

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
fuente
¿No crees que en el eje 1 y paralelo al eje 0 significan lo mismo?
Matiz
9

Ya hay respuestas adecuadas, pero les doy otro ejemplo con> 2 dimensiones.

El parámetro axissignifica el eje que se va a cambiar .
Por ejemplo, considere que hay un marco de datos con dimensión axbxc .

  • df.mean(axis=1)devuelve un marco de datos con dimensión ax 1 xc .
  • df.drop("col4", axis=1)devuelve un marco de datos con dimensión ax (b-1) xc .

Aquí, axis=1significa el segundo eje que es b, por lo que el bvalor se cambiará en estos ejemplos.

jeongmin.cha
fuente
1
Esta respuesta es más intuitiva para mí que cualquier visualización que haya visto sobre este tema. Sin embargo, xarray es mejor para matrices multidimensionales que pandas.
alys
2

Debería ser más conocido que los alias de cadena 'índice' y 'columnas' pueden usarse en lugar de los enteros 0/1. Los alias son mucho más explícitos y me ayudan a recordar cómo se realizan los cálculos. Otro alias para 'índice' es 'filas' .

Cuando axis='index'se usa, los cálculos se realizan en las columnas, lo cual es confuso. Pero, lo recuerdo como obtener un resultado que es del mismo tamaño que otra fila.

Pongamos algunos datos en la pantalla para ver de qué estoy hablando:

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

Cuando queremos tomar la media de todas las columnas, usamos axis='index'para obtener lo siguiente:

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

El mismo resultado se obtendría mediante:

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

Para usar una operación de izquierda a derecha en las filas, use axis = 'columnas'. Lo recuerdo pensando que se puede agregar una columna adicional a mi 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

El mismo resultado se obtendría mediante:

df.mean(axis=1)

Agregue una nueva fila con eje = 0 / índice / filas

Usemos estos resultados para agregar filas o columnas adicionales para completar la explicación. Entonces, siempre que use axis = 0 / index / rows, es como obtener una nueva fila del DataFrame. Agreguemos una fila:

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

Agregue una nueva columna con eje = 1 / columnas

De manera similar, cuando el eje = 1 / columnas, creará datos que se pueden convertir fácilmente en su propia columna:

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

Parece que puede ver todos los alias con las siguientes variables privadas:

df._AXIS_ALIASES
{'rows': 0}

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

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

Cuando eje = 'filas' o eje = 0, significa elementos de acceso en la dirección de las filas, de arriba a abajo. Si aplica la suma a lo largo del eje = 0, nos dará los totales de cada columna.

Cuando eje = 'columnas' o eje = 1, significa elementos de acceso en la dirección de las columnas, de izquierda a derecha. Si aplica la suma a lo largo del eje = 1, obtendremos los totales de cada fila.

¡Todavía confuso! Pero lo anterior me lo pone un poco más fácil.

novato
fuente
0

Encuentro todas las otras respuestas confusas. Así es como lo pienso:

axis=0: la forma del resultado es horizontal (una fila)
axis=1: la forma del resultado es vertical (una columna)

Entonces

  • df.drop(name, axis=1): suelta una columna
  • df.mean(axis=1): calcula una columna (el resultado se puede agregar como una nueva columna)
AXO
fuente