Modificando um subconjunto de linhas em um dataframe do pandas

143

Suponha que eu tenho um DataFrame do pandas com duas colunas, A e B. Eu gostaria de modificar esse DataFrame (ou criar uma cópia) para que B seja sempre NaN sempre que A for 0. Como eu conseguiria isso?

Eu tentei o seguinte

df['A'==0]['B'] = np.nan

e

df['A'==0]['B'].values.fill(np.nan)

sem sucesso.

Arthur B.
fonte
Se você está procurando um uso solução muito rápida NumPy está wherecomo visto em esta solução abaixo
Ted Petrou

Respostas:

243

Use .locpara indexação baseada em etiqueta:

df.loc[df.A==0, 'B'] = np.nan

A df.A==0expressão cria uma série booleana que indexa as linhas, 'B'seleciona a coluna. Você também pode usar isso para transformar um subconjunto de uma coluna, por exemplo:

df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2

Não sei o suficiente sobre os internos do pandas para saber exatamente por que isso funciona, mas o problema básico é que, às vezes, a indexação em um DataFrame retorna uma cópia do resultado e, às vezes, retorna uma exibição no objeto original. De acordo com a documentação aqui , esse comportamento depende do comportamento numpy subjacente. Descobri que acessar tudo em uma operação (em vez de [uma] [duas]) é mais provável que funcione na configuração.

BrenBarn
fonte
A segunda parte disso é uma boa resposta a uma pergunta que nem sequer foi feita ;-) Gostaria de saber se essa ainda é a resposta dos pandas canônicos, em particular porque é uma violação óbvia de DRY, embora eu assuma que ela esteja em fato necessário para violar o DRY, dadas as restrições dos internos dos pandas? (I pode postar exatamente esse tipo de pergunta, em mais detalhes, mas queria ver se tinha uma resposta rápida antes de eu fiz isso)
Johne
Como subconjunto de um Dataframe que não possui nomes de coluna, como subconjunto df apenas pelo índice? df.loc [df [0] == 0] não funciona ... Qual é a alternativa? Obrigado
amipro
89

Aqui está dos documentos do pandas sobre indexação avançada:

A seção explicará exatamente o que você precisa! Acontece que df.loc(como .ix foi descontinuado - como muitos apontaram abaixo) pode ser usado para fatiar / cortar dados em um quadro de dados. E. Também pode ser usado para definir as coisas.

df.loc[selection criteria, columns I want] = value

Então a resposta de Bren está dizendo 'encontre-me todos os lugares onde df.A == 0, selecione a coluna Be defina-a como np.nan'

badgley
fonte
2
Você fez meu dia. Explicação clara.
TwinPenguins
1
Sim, de alguma forma loc[selection criteria, columns I want]adere perfeitamente à sua mente ...
EmEs
29

A partir de pandas, 0,20 ix está obsoleto . O caminho certo é usar o df.loc

aqui está um exemplo de trabalho

>>> import pandas as pd 
>>> import numpy as np 
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN
>>> 

Explicação:

Conforme explicado no documento aqui , .loc é principalmente baseado em rótulo, mas também pode ser usado com uma matriz booleana .

Portanto, o que estamos fazendo acima é a aplicação df.loc[row_index, column_index]de:

  • Explorando o fato de que locpodemos usar uma matriz booleana como uma máscara que diz aos pandas em que subconjunto de linhas queremos alterarrow_index
  • A exploração do fato loctambém é baseada em rótulo para selecionar a coluna usando o rótulo 'B'nocolumn_index

Podemos usar lógica, condição ou qualquer operação que retorne uma série de booleanos para construir a matriz de booleanos. No exemplo acima, queremos rowsque contenha um 0, para que possamos usar df.A == 0, como você pode ver no exemplo abaixo, isso retorna uma série de booleanos.

>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df 
   A  B
0  0  2
1  1  0
2  0  5
>>> df.A == 0 
0     True
1    False
2     True
Name: A, dtype: bool
>>> 

Em seguida, usamos a matriz de booleanos acima para selecionar e modificar as linhas necessárias:

>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN

Para mais informações, consulte a documentação avançada de indexação aqui .

Mohamed Ali JAMAOUI
fonte
11

Para um grande aumento de velocidade, use a função where do NumPy.

Configuração

Crie um DataFrame de duas colunas com 100.000 linhas com alguns zeros.

df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))

Solução rápida com numpy.where

df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)

Horários

%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy whereé cerca de 4x mais rápido

Ted Petrou
fonte
Eu estava curioso sobre isso, então testei eu mesmo e a diferença foi ainda maior usando outros parâmetros. Numpy foi quase 10 vezes mais rápido na substituição de 0s por um número inteiro em vez de np.nan. Eu me pergunto o que leva o tempo extra.
Alexander
É necessário o uso .valuesem np.where(df.a.values == 0, np.nan, df.b.values)? Parece que np.where(df.a == 0, np.nan, df.b)também funciona?
hsl 02/02
4

Para substituir várias colunas, converta em matriz numpy usando .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Adrien Renaud
fonte