Como posso obter o NOT lógico por elementos de uma série de pandas?

229

Eu tenho um Seriesobjeto pandas contendo valores booleanos. Como posso obter uma série contendo a lógica NOTde cada valor?

Por exemplo, considere uma série contendo:

True
True
True
False

A série que eu gostaria de obter conteria:

False
False
False
True

Parece que deve ser razoavelmente simples, mas aparentemente eu perdi meu mojo = (

blz
fonte
1
É importante que os dados não contenham objecttipos para as respostas abaixo funcionarem, portanto, use:~ df.astype('bool')
LearnOPhile
Eu escrevi sobre todos os operadores lógicos neste post . O post também inclui alternativas.
cs95

Respostas:

260

Para inverter uma série booleana, use~s :

In [7]: s = pd.Series([True, True, False, True])

In [8]: ~s
Out[8]: 
0    False
1    False
2     True
3    False
dtype: bool

Usando Python2.7, NumPy 1.8.0, Pandas 0.13.1:

In [119]: s = pd.Series([True, True, False, True]*10000)

In [10]:  %timeit np.invert(s)
10000 loops, best of 3: 91.8 µs per loop

In [11]: %timeit ~s
10000 loops, best of 3: 73.5 µs per loop

In [12]: %timeit (-s)
10000 loops, best of 3: 73.5 µs per loop

A partir do Pandas 0.13.0, as séries não são mais subclasses de numpy.ndarray; agora são subclasses de pd.NDFrame. Isso pode ter algo a ver com o porquê np.invert(s)não é mais tão rápido quanto ~sou -s.

Advertência: os timeitresultados podem variar dependendo de muitos fatores, incluindo versões de hardware, compilador, SO, Python, NumPy e Pandas.

unutbu
fonte
Devidamente anotado. Além de ser muito mais lento, qual é a diferença entre o til e -?
blz
Estranho, eu realmente testei o tildeque foi mencionado na documentação, mas ele não executou o mesmo que np.invert: S
root
@blz: Pelo menos na minha máquina Ubuntu, correndo NumPy 1.6.2, o desempenho de np.invert(s), ~se -ssão todos iguais.
unutbu
@root: Não sei ao certo por que há uma discrepância tão grande em nossos resultados de timeit, mas isso certamente pode acontecer. Qual sistema operacional e versão do NumPy você está usando?
unutbu
Também no Ubuntu, mas usando o NumPy 1.7.0 ... ( np.bitwise_not(s)executa o mesmo que np.inverse).
root
32

A resposta do @ unutbu é imediata, só queria adicionar um aviso de que sua máscara precisa ser do tipo bool, não 'objeto'. Ou seja, sua máscara nunca pode ter nan. Veja aqui - mesmo que sua máscara não esteja nan agora, ela permanecerá do tipo 'objeto'.

O inverso de uma série de 'objetos' não gera um erro; em vez disso, você recebe uma máscara de lixo de ints que não funcionará conforme o esperado.

In[1]: df = pd.DataFrame({'A':[True, False, np.nan], 'B':[True, False, True]})
In[2]: df.dropna(inplace=True)
In[3]: df['A']
Out[3]:
0    True
1   False
Name: A, dtype object
In[4]: ~df['A']
Out[4]:
0   -2
0   -1
Name: A, dtype object

Depois de conversar com colegas sobre este, tenho uma explicação: Parece que os pandas estão revertendo para o operador bit a bit:

In [1]: ~True
Out[1]: -2

Como @geher diz, você pode convertê-lo em booleano com astype antes de inverter com ~

~df['A'].astype(bool)
0    False
1     True
Name: A, dtype: bool
(~df['A']).astype(bool)
0    True
1    True
Name: A, dtype: bool
JSharm
fonte
no seu exemplo, a máscara ints de saída pode ser convertida na série bool que você deseja, por .astype(bool)exemplo~df['A'].astype(bool)
geher
Isso está funcionando porque astype(bool)está acontecendo antes do ~ ~df['A'].astype(bool)vs(~df['A']).astype(bool)
JSharm 06/02
16

Eu apenas dou uma chance:

In [9]: s = Series([True, True, True, False])

In [10]: s
Out[10]: 
0     True
1     True
2     True
3    False

In [11]: -s
Out[11]: 
0    False
1    False
2    False
3     True
herrfz
fonte
Eu literalmente tentei todos os operadores que não -! Vou manter isso em mente para a próxima vez.
blz
6

Você também pode usar numpy.invert:

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: s = pd.Series([True, True, False, True])

In [4]: np.invert(s)
Out[4]: 
0    False
1    False
2     True
3    False

EDIT: A diferença de desempenho aparece no Ubuntu 12.04, Python 2.7, NumPy 1.7.0 - parece não existir usando o NumPy 1.6.2:

In [5]: %timeit (-s)
10000 loops, best of 3: 26.8 us per loop

In [6]: %timeit np.invert(s)
100000 loops, best of 3: 7.85 us per loop

In [7]: %timeit ~s
10000 loops, best of 3: 27.3 us per loop
raiz
fonte
pode não estar correto em uma plataforma diferente. Win 7, python 3.6.3 numpy 1.13.3, pandas 0.20.3, (-s) será o mais rápido, (~ s) é o segundo e np.invert (s) é o mais lento
gaozhidf
0

O NumPy é mais lento porque converte a entrada em valores booleanos (portanto, Nenhum e 0 se tornam Falso e todo o resto se torna Verdadeiro).

import pandas as pd
import numpy as np
s = pd.Series([True, None, False, True])
np.logical_not(s)

da-te

0    False
1     True
2     True
3    False
dtype: object

enquanto ~ s iria falhar. Na maioria dos casos, til seria uma escolha mais segura que o NumPy.

Pandas 0,25, NumPy 1,17

grofte
fonte