Pandas de substituição condicional


123

Eu tenho um DataFrame e desejo substituir os valores em uma coluna específica que excedem um valor com zero. Eu pensei que essa era uma maneira de conseguir isso:

df[df.my_channel > 20000].my_channel = 0

Se eu copiar o canal em um novo quadro de dados, é simples:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Isso faz exatamente o que eu quero, mas parece não funcionar com o canal como parte do DataFrame original.


Encontrei o que acho que você estava procurando aqui .
fevereiro

Respostas:


181

.ixO indexador funciona bem para a versão do pandas anterior a 0.20.0, mas desde o pandas 0.20.0, o .ixindexador está obsoleto , portanto, você deve evitar usá-lo. Em vez disso, você pode usar .locou ilocindexadores. Você pode resolver esse problema:

mask = df.my_channel > 20000
column_name = 'my_channel'
df.loc[mask, column_name] = 0

Ou, em uma linha,

df.loc[df.my_channel > 20000, 'my_channel'] = 0

maskajuda a selecionar as linhas em que df.my_channel > 20000é True, enquanto df.loc[mask, column_name] = 0define o valor 0 para as linhas selecionadas onde se maskmantém na coluna cujo nome é column_name.

Update: Neste caso, você deve usar locporque, se usar iloc, receberá um aviso deNotImplementedError que a indexação booleana baseada em iLocation em um tipo inteiro não está disponível .


81

Experimentar

df.loc[df.my_channel > 20000, 'my_channel'] = 0

Observação: desde a v0.20.0,ix foi preterido em favor de loc/ iloc.


8
Obrigado. Também encontrei minha própria solução, que foi: df.my_channel [df.my_channel> 20000] = 0
BMichell

2
@BMichell Eu acho que sua solução pode começar a dar avisos em 0,13, não tive a chance de tentar ainda
lowtech

erro de rendimento: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: Um valor está tentando ser definido em uma cópia de uma fatia de um DataFrame Veja as advertências em a documentação: pandas.pydata.org/pandas-docs/stable/… "" "Ponto de entrada para lançar um kernel IPython.
Rutger Hofste

@RutgerHofste obrigado por mencionar que, ainda outro argumento nunca use Python3
lowtech

34

np.where função funciona da seguinte maneira:

df['X'] = np.where(df['Y']>=50, 'yes', 'no')

No seu caso, você gostaria de:

import numpy as np
df['my_channel'] = np.where(df.my_channel > 20000, 0, df.my_channel)

19

O motivo pelo qual seu dataframe original não é atualizado é porque a indexação encadeada pode fazer com que você modifique uma cópia em vez de uma exibição de seu dataframe. Os documentos dão este conselho:

Ao definir valores em um objeto pandas, deve-se tomar cuidado para evitar o que é chamado de indexação em cadeia.

Você tem algumas alternativas: -

loc + Indexação booleana

loc pode ser usado para definir valores e oferece suporte a máscaras booleanas:

df.loc[df['my_channel'] > 20000, 'my_channel'] = 0

mask + Indexação booleana

Você pode atribuir à sua série:

df['my_channel'] = df['my_channel'].mask(df['my_channel'] > 20000, 0)

Ou você pode atualizar sua série no local:

df['my_channel'].mask(df['my_channel'] > 20000, 0, inplace=True)

np.where + Indexação booleana

Você pode usar o NumPy atribuindo sua série original quando sua condição não for satisfeita; entretanto, as duas primeiras soluções são mais limpas, pois alteram explicitamente apenas os valores especificados.

df['my_channel'] = np.where(df['my_channel'] > 20000, 0, df['my_channel'])

0

Eu usaria a lambdafunção em um Seriesou DataFrameassim:

f = lambda x: 0 if x>100 else 1
df['my_column'] = df['my_column'].map(f)

Não afirmo que seja uma forma eficiente, mas funciona bem.


3
Isso é ineficiente e não recomendado, pois envolve um loop no nível do Python em uma operação em linha.
jpp de

Obrigado, acho que podemos usar locaqui, como df.loc[: , 'my_column'] = df['my_column'].map(f). Não sei se é rápido como os que você adicionou abaixo.
Ozkan Serttas

2
Não, ainda é lento porque você ainda está operando em linha ao invés de coluna.
jpp de

0

Experimente isto:

df.my_channel = df.my_channel.where(df.my_channel <= 20000, other= 0)

ou

df.my_channel = df.my_channel.mask(df.my_channel > 20000, other= 0)

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.