Comment puis-je obtenir le NON logique élément par élément d'une série pandas?

229

J'ai un Seriesobjet pandas contenant des valeurs booléennes. Comment puis-je obtenir une série contenant la logique NOTde chaque valeur?

Par exemple, considérons une série contenant:

True
True
True
False

La série que j'aimerais obtenir contiendrait:

False
False
False
True

Cela semble être assez simple, mais apparemment, j'ai égaré mon mojo = (

blz
la source
1
Il est important que les données ne contiennent pas de objecttypes pour que les réponses ci-dessous fonctionnent, alors utilisez:~ df.astype('bool')
LearnOPhile
J'ai écrit sur tous les opérateurs logiques dans ce post . Le poste comprend également des alternatives.
cs95

Réponses:

260

Pour inverser une série booléenne, utilisez~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

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

Depuis Pandas 0.13.0, les séries ne sont plus des sous-classes de numpy.ndarray; ce sont maintenant des sous-classes de pd.NDFrame. Cela pourrait avoir quelque chose à voir avec pourquoi np.invert(s)n'est plus aussi rapide que ~sou -s.

Avertissement: les timeitrésultats peuvent varier en fonction de nombreux facteurs, notamment le matériel, le compilateur, le système d'exploitation, les versions Python, NumPy et Pandas.

unutbu
la source
Dûment noté. En plus d'être beaucoup plus lent, quelle est la différence entre le tilde et -?
blz
Wierd, j'ai en fait testé le tildecomme il était mentionné dans la documentation, mais il n'a pas fonctionné de la même manière que np.invert: S
root
@blz: Au moins sur ma machine Ubuntu, en cours d' exécution NumPy 1.6.2, la performance np.invert(s), ~set -ssont tous les mêmes.
unutbu
@root: Je ne sais pas pourquoi il y a une si grande différence dans nos résultats temporels, mais cela peut certainement se produire. Quel système d'exploitation et quelle version de NumPy utilisez-vous?
unutbu
Également sur Ubuntu, mais en utilisant NumPy 1.7.0 ... ( np.bitwise_not(s)fonctionne de la même manière que np.inverse).
root
32

La réponse de @ unutbu est parfaite, je voulais juste ajouter un avertissement que votre masque doit être dtype bool, pas 'object'. C'est-à-dire que votre masque ne doit jamais avoir de nan. Voyez ici - même si votre masque est maintenant sans nanos, il restera de type «objet».

L'inverse d'une série `` objet '' ne générera pas d'erreur, à la place, vous obtiendrez un masque d'ordures d'entiers qui ne fonctionnera pas comme prévu.

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

Après avoir parlé à mes collègues de celui-ci, j'ai une explication: il semble que les pandas reviennent à l'opérateur au niveau du bit:

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

Comme le dit @geher, vous pouvez le convertir en bool avec astype avant d'inverser avec ~

~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
la source
dans votre exemple, le masque des ints de sortie peut être converti dans la série bool que vous voulez avec .astype(bool)par exemple~df['A'].astype(bool)
geher
Cela fonctionne parce que cela astype(bool)se passe avant le ~ ~df['A'].astype(bool)vs(~df['A']).astype(bool)
JSharm
16

Je lui donne juste un coup de feu:

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
la source
J'ai littéralement essayé tous les opérateurs autres que -! Je garderai cela à l'esprit pour la prochaine fois.
blz
6

Vous pouvez également utiliser 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: La différence de performances apparaît sur Ubuntu 12.04, Python 2.7, NumPy 1.7.0 - ne semble cependant pas exister avec 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
racine
la source
il peut ne pas être correct sur une autre plate-forme. Win 7, python 3.6.3 numpy 1.13.3, pandas 0.20.3, (-s) sera le plus rapide, (~ s) est le deuxième, et np.invert (s) est le plus lent
gaozhidf
0

NumPy est plus lent car il convertit l'entrée en valeurs booléennes (donc None et 0 devient False et tout le reste devient True).

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

vous donne

0    False
1     True
2     True
3    False
dtype: object

alors que ~ s planterait. Dans la plupart des cas, le tilde serait un choix plus sûr que NumPy.

Pandas 0,25, NumPy 1,17

grofte
la source