¿Cómo puedo obtener el elemento lógico NO de una serie de pandas?

229

Tengo un Seriesobjeto pandas que contiene valores booleanos. ¿Cómo puedo obtener una serie que contenga la lógica NOTde cada valor?

Por ejemplo, considere una serie que contiene:

True
True
True
False

La serie que me gustaría obtener contendría:

False
False
False
True

Parece que debería ser razonablemente simple, pero aparentemente he perdido mi mojo = (

blz
fuente
1
Es importante que los datos no contengan objecttipos para que las respuestas a continuación funcionen, así que use:~ df.astype('bool')
LearnOPhile
He escrito sobre todos los operadores lógicos en esta publicación . La publicación también incluye alternativas.
cs95

Respuestas:

260

Para invertir una serie 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 de Pandas 0.13.0, las series ya no son subclases de numpy.ndarray; ahora son subclases de pd.NDFrame. Esto podría tener algo que ver con por qué np.invert(s)ya no es tan rápido como ~so -s.

Advertencia: los timeitresultados pueden variar según muchos factores, incluidas las versiones de hardware, compilador, sistema operativo, Python, NumPy y Pandas.

unutbu
fuente
Debidamente anotado. Además de ser mucho más lento, ¿cuál es la diferencia entre tilde y -?
blz
Wierd, realmente probé el tildecomo se menciona en la documentación, pero no funcionó igual que np.invert: S
root
@blz: Al menos en mi máquina Ubuntu, ejecutando NumPy 1.6.2, el rendimiento de np.invert(s), ~sy -sson todos iguales.
unutbu
@root: No estoy seguro de por qué existe una gran discrepancia en nuestros resultados de tiempo, pero ciertamente puede suceder. ¿Qué sistema operativo y versión de NumPy estás usando?
unutbu
También en Ubuntu, pero usando NumPy 1.7.0 ... ( np.bitwise_not(s)funciona igual que np.inverse).
raíz
32

La respuesta de @unutbu es acertada, solo quería agregar una advertencia de que su máscara debe ser dtype bool, no 'object'. Es decir, su máscara no puede haber nunca tenido ninguna de Nan. Vea aquí , incluso si su máscara no tiene nan ahora, seguirá siendo del tipo 'objeto'.

El inverso de una serie de 'objetos' no arrojará un error, sino que obtendrá una máscara de basura que no funcionará como espera.

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

Después de hablar con colegas sobre este tema, tengo una explicación: parece que los pandas están volviendo al operador bit a bit:

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

Como dice @geher, puede convertirlo en bool con un tipo antes de invertir con ~

~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
fuente
en su ejemplo, la máscara de entradas de salida se puede convertir a la serie bool que desee, por .astype(bool)ejemplo~df['A'].astype(bool)
desde
Esto está funcionando porque astype(bool)está sucediendo antes del ~ ~df['A'].astype(bool)vs(~df['A']).astype(bool)
JSharm
16

Solo le doy una oportunidad:

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
fuente
Literalmente probé todos los operadores que no sean -! Lo tendré en cuenta para la próxima vez.
blz
6

También puedes 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

EDITAR: La diferencia en el rendimiento aparece en Ubuntu 12.04, Python 2.7, NumPy 1.7.0, aunque no parece existir usando 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
raíz
fuente
Puede que no sea correcto en una plataforma diferente. Win 7, python 3.6.3 numpy 1.13.3, pandas 0.20.3, (-s) será el más rápido, (~ s) es el segundo y np.invert (s) es el más lento
gaozhidf
0

NumPy es más lento porque convierte la entrada en valores booleanos (por lo que None y 0 se convierten en False y todo lo demás se convierte en True).

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

te dio

0    False
1     True
2     True
3    False
dtype: object

mientras que ~ s se estrellaría. En la mayoría de los casos, tilde sería una opción más segura que NumPy.

Pandas 0.25, NumPy 1.17

Grofte
fuente