Ambiguidade na definição do “eixo” do Pandas Dataframe / Numpy Array


96

Estou muito confuso sobre como os eixos Python são definidos e se eles se referem às linhas ou colunas de um DataFrame. Considere o código abaixo:

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

Portanto, se chamarmos df.mean(axis=1), obteremos uma média nas linhas:

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

No entanto, se chamarmos df.drop(name, axis=1), na verdade eliminamos uma coluna , não uma linha:

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

Alguém pode me ajudar a entender o que significa "eixo" em pandas / numpy / scipy?

Uma nota lateral, DataFrame.meansó pode ser definida incorretamente. Diz na documentação DataFrame.meanque axis=1isso significa uma média sobre as colunas, não as linhas ...


Para obter uma explicação detalhada dos aliases, 'colunas' e 'índice' / 'linhas', consulte a resposta abaixo .
Ted Petrou

Isso é estranho. O eixo deve ser consistente entre o meane o drop. É necessário pensamento não linear para chegar ao comportamento real.
StephenBoesch

Respostas:


172

Talvez seja mais simples lembrá-lo como 0 = para baixo e 1 = transversalmente .

Isso significa:

  • Use axis=0para aplicar um método a cada coluna ou aos rótulos de linha (o índice).
  • Use axis=1para aplicar um método em cada linha ou nos rótulos das colunas.

Aqui está uma imagem para mostrar as partes de um DataFrame a que cada eixo se refere:

Também é útil lembrar que o Pandas segue o uso da palavra por NumPy axis. O uso é explicado no glossário de termos do NumPy :

Os eixos são definidos para matrizes com mais de uma dimensão. Uma matriz bidimensional tem dois eixos correspondentes: o primeiro executando verticalmente para baixo nas linhas (eixo 0) e o segundo executando horizontalmente nas colunas (eixo 1) . [ ênfase minha ]

Então, no que diz respeito ao método em questão df.mean(axis=1),, parece estar corretamente definido. Ele pega a média das entradas horizontalmente nas colunas , ou seja, ao longo de cada linha individual. Por outro lado, df.mean(axis=0)seria uma operação atuando verticalmente para baixo nas linhas .

Da mesma forma, df.drop(name, axis=1)refere-se a uma ação nos rótulos das colunas, porque eles passam intuitivamente pelo eixo horizontal. Especificar axis=0faria o método agir nas linhas.


3
O que me fez lutar foi que df.apply (..., axis = 0), não "atropelou" o eixo 0 (o índice), mas atropelou as colunas, retendo Series contendo todos os índices. A dica é que df.apply (..., axis = 0) retorna Series para que VOCÊ possa aplicar uma execução operacional sobre o índice completo.
moritzschaefer de

2
Acho que também ajuda se você considerar df.applyum método semelhante a df.sum. Por exemplo, df.sum(axis=0)soma cada coluna do DataFrame. Da mesma forma, você pode escrever df.apply(sum, axis=0)para fazer exatamente a mesma operação. Embora a operação seja de fato aplicada a cada coluna no DataFrame, a função real é executada no eixo 0.
Alex Riley

É lamentável que as convenções de nomenclatura e ordem sejam o oposto da função de aplicação de R - em R, o valor mais baixo MARGIN(semelhante a axisnos pandas) de "1" corresponde a "linhas", o que significa que a função é aplicada a cada linha , enquanto o o valor maior de "2" refere-se a "colunas", o que significa que a função é aplicada a cada coluna .
Keith Hughitt

é um bug destrutivo em pandas
Cálculo

10

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

Sobre df.drop(eixo significa a posição)

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

Sobre df.apply(eixo significa direção)

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

Você não acha que no eixo 1 e paralelo ao eixo 0 significam o mesmo?
Nuance

9

Já existem respostas adequadas, mas dou outro exemplo com> 2 dimensões.

O parâmetro axissignifica eixo a ser alterado .
Por exemplo, considere que existe um dataframe com dimensão axbxc .

  • df.mean(axis=1)retorna um dataframe com dimensão ax 1 xc .
  • df.drop("col4", axis=1)retorna um dataframe com dimensão ax (b-1) xc .

Aqui, axis=1significa o segundo eixo, que é b, portanto, o bvalor será alterado nestes exemplos.


1
Essa resposta é mais intuitiva para mim do que qualquer visualização que já vi sobre este tópico. No entanto, xarray é melhor para arrays multidimensionais do que para pandas.
becos

3

Deve ser mais amplamente conhecido que os aliases de string 'índice' e 'colunas' podem ser usados ​​no lugar dos inteiros 0/1. Os apelidos são muito mais explícitos e me ajudam a lembrar como os cálculos ocorrem. Outro alias para 'índice' é 'linhas' .

Quando axis='index'é usado, os cálculos acontecem nas colunas, o que é confuso. Mas, lembro-me de obter um resultado que é do mesmo tamanho que outra linha.

Vamos colocar alguns dados na tela para ver do que estou falando:

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 queremos obter a média de todas as colunas, usamos axis='index'para obter o seguinte:

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

O mesmo resultado seria obtido por:

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

Para usar uma operação da esquerda para a direita nas linhas, use axis = 'colunas'. Lembro-me disso pensando que uma coluna adicional pode ser adicionada ao meu 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

O mesmo resultado seria obtido por:

df.mean(axis=1)

Adicionar uma nova linha com eixo = 0 / índice / linhas

Vamos usar esses resultados para adicionar linhas ou colunas adicionais para completar a explicação. Portanto, sempre que usar axis = 0 / index / rows, é como obter uma nova linha do DataFrame. Vamos adicionar uma linha:

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

Adicione uma nova coluna com eixo = 1 / colunas

Da mesma forma, quando eixo = 1 / colunas, ele criará dados que podem ser facilmente transformados em sua própria coluna:

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 você pode ver todos os aliases com as seguintes variáveis ​​privadas:

df._AXIS_ALIASES
{'rows': 0}

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

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

É interessante saber que existem apelidos para os números do primeiro eixo e eles não são usados ​​com tanta frequência, embora sejam mais explícitos. Quem pode imaginar ouvir na escola "isto é uma tabela, tente calcular somas ao longo do eixo 0" em vez de "tente obter a soma por colunas" (ou ao longo do 'índice' no caso dos pandas, infelizmente).
minutos

1

Quando eixo = 'linhas' ou eixo = 0, significa acessar os elementos na direção das linhas, de cima para baixo. Se aplicar soma ao longo do eixo = 0, obteremos os totais de cada coluna.

Quando eixo = 'colunas' ou eixo = 1, significa acessar elementos na direção das colunas, da esquerda para a direita. Se aplicar soma ao longo do eixo = 1, obteremos os totais de cada linha.

Ainda confuso! Mas o exposto acima torna tudo um pouco mais fácil para mim.


0

Acho todas as outras respostas confusas. Aqui está o que penso sobre isso:

axis=0: a forma do resultado é horizontal (uma linha)
axis=1: a forma do resultado é vertical (uma coluna)

então

  • df.drop(name, axis=1): solta uma coluna
  • df.mean(axis=1): calcula uma coluna (o resultado pode ser adicionado como uma nova coluna)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.