Ik werk met Booleaanse index in Panda’s.
De vraag is waarom de verklaring:
a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]
Werkt prima, terwijl
a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]
Verlaat met fout?
Voorbeeld:
a=pd.DataFrame({'x':[1,1],'y':[10,20]})
In: a[(a['x']==1)&(a['y']==10)]
Out: x y
0 1 10
In: a[(a['x']==1) and (a['y']==10)]
Out: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Antwoord 1, Autoriteit 100%
Wanneer u
zegt
(a['x']==1) and (a['y']==10)
U vraagt impliciet Python om (a['x']==1)
en (a['y']==10)
naar Boolean-waarden te converteren .
Numpy-arrays (van lengte groter dan 1) en panda-objecten zoals serie hebben geen Booleaanse waarde – met andere woorden, ze verhogen
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
Bij gebruik als een Booleaanse waarde. Dat komt omdat het onduidelijk wanneer het zou moeten wees waar of onwaar . Sommige gebruikers kunnen ervan uitgaan dat ze waar zijn als ze niet-nullengte hebben, zoals een Python-lijst. Anderen zouden willen dat het alleen waar is als alle zijn elementen waar zijn. Anderen willen misschien dat het waar is als elke van zijn elementen waar zijn.
Omdat er zoveel tegenstrijdige verwachtingen zijn, weigeren de ontwerpers van Numpy en Panda’s te raden en brengen in plaats daarvan een waardeerror op.
In plaats daarvan moet u expliciet zijn door de empty()
, all()
of any()
methode te bellen om aan te geven welk gedrag je wenst.
In dit geval lijkt het echter erop dat u geen Booleaanse evaluatie wilt, u wilt element-wise logisch-en. Dat is wat de &
binaire operator uitgevoerd:
(a['x']==1) & (a['y']==10)
Retourneert een Boolean-array.
trouwens, zoals AlexPmil Notes ,
De haakjes zijn verplicht sinds &
heeft een hogere exploitant voorrang dan ==
.
Zonder de haakjes, a['x']==1 & a['y']==10
zou worden geëvalueerd als a['x'] == (1 & a['y']) == 10
die op zijn beurt zou zijn Wees gelijk aan de geketende vergelijking (a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10)
. Dat is een uitdrukking van het formulier Series and Series
.
Het gebruik van and
met twee Series zou opnieuw dezelfde ValueError
activeren als hierboven. Daarom zijn de haakjes verplicht.
Antwoord 2, autoriteit 47%
TLDR; Logische operators in Panda’s zijn &
, |
en ~
, en haakjes (...)
is belangrijk!
Python’s and
, or
en not
logische operators zijn ontworpen om te werken met scalaire waarden. Panda’s moesten het dus beter doen en de bitsgewijze operatoren overschrijven om een gevectoriseerde(elementsgewijs) versie van deze functionaliteit te krijgen.
Dus het volgende in python (exp1
en exp2
zijn uitdrukkingen die een booleaans resultaat opleveren)…
exp1 and exp2 # Logical AND
exp1 or exp2 # Logical OR
not exp1 # Logical NOT
…wordt vertaald naar…
exp1 & exp2 # Element-wise logical AND
exp1 | exp2 # Element-wise logical OR
~exp1 # Element-wise logical NOT
voor panda’s.
Als u tijdens het uitvoeren van een logische bewerking een ValueError
krijgt, dan moet u haakjes gebruiken om te groeperen:
(exp1) op (exp2)
Bijvoorbeeld
(df['col1'] == x) & (df['col2'] == y)
Enzovoort.
Boolean indexeren: Een veelvoorkomende bewerking is het berekenen van booleaanse maskers door middel van logische voorwaarden om de gegevens te filteren. Pandas biedt drieoperators: &
voor logische AND, |
voor logische OR en ~
voor logische NOT.
Overweeg de volgende opstelling:
np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (5, 3)), columns=list('ABC'))
df
A B C
0 5 0 3
1 3 7 9
2 3 5 2
3 4 7 6
4 8 8 1
Logisch EN
Voor df
hierboven, stel dat u alle rijen wilt retourneren waarin A < 5 en B > 5. Dit wordt gedaan door maskers voor elke aandoening afzonderlijk te berekenen en deze te ANDen.
Overbelaste Bitwise &
-operator
Voordat u verder gaat, dient u kennis te nemen van dit specifieke uittreksel van de documenten, waarin staat
Een andere veel voorkomende bewerking is het gebruik van booleaanse vectoren om de
gegevens. De operators zijn:|
vooror
,&
voorand
en~
voornot
. Deze
moet worden gegroepeerd door haakjes te gebruiken, omdat Python standaard
evalueer een uitdrukking zoalsdf.A > 2 & df.B < 3
alsdf.A > (2 &
, terwijl de gewenste evaluatievolgorde
df.B) < 3(df.A > 2) & (df.B <
.
3)
Dus, met dit in gedachten, kan elementgewijs logische AND worden geïmplementeerd met de bitsgewijze operator &
:
df['A'] < 5
0 False
1 True
2 True
3 True
4 False
Name: A, dtype: bool
df['B'] > 5
0 False
1 True
2 False
3 True
4 True
Name: B, dtype: bool
(df['A'] < 5) & (df['B'] > 5)
0 False
1 True
2 False
3 True
4 False
dtype: bool
En de volgende filterstap is eenvoudig,
df[(df['A'] < 5) & (df['B'] > 5)]
A B C
1 3 7 9
3 4 7 6
De haakjes worden gebruikt om de standaardvoorrangsvolgorde van bitsgewijze operatoren te overschrijven, die een hogere prioriteit hebben dan de voorwaardelijke operatoren <
en >
. Zie het gedeelte over Operatorvoorrangin de Python-documenten.
Als u geen haakjes gebruikt, wordt de uitdrukking onjuist geëvalueerd. Als u bijvoorbeeld per ongeluk iets probeert zoals
df['A'] < 5 & df['B'] > 5
Het wordt geparseerd als
df['A'] < (5 & df['B']) > 5
Wat wordt,
df['A'] < something_you_dont_want > 5
Wat wordt (zie de python-documenten op geketende operatorvergelijking) ,
(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)
Wat wordt,
# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2
Welke worpen
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Maak die fout dus niet!1
Groepering tussen haakjes vermijden
De oplossing is eigenlijk vrij eenvoudig. De meeste operators hebben een overeenkomstige gebonden methode voor DataFrames. Als de individuele maskers zijn opgebouwd met behulp van functies in plaats van voorwaardelijke operators, hoeft u niet langer te groeperen op haakjes om de evaluatievolgorde te specificeren:
df['A'].lt(5)
0 True
1 True
2 True
3 True
4 False
Name: A, dtype: bool
df['B'].gt(5)
0 False
1 True
2 False
3 True
4 True
Name: B, dtype: bool
df['A'].lt(5) & df['B'].gt(5)
0 False
1 True
2 False
3 True
4 False
dtype: bool
Zie het gedeelte over Flexibele vergelijkingen.. Samenvattend hebben we
╒════╤════════════╤════════════╕
│ │ Operator │ Function │
╞════╪════════════╪════════════╡
│ 0 │ > │ gt │
├────┼────────────┼────────────┤
│ 1 │ >= │ ge │
├────┼────────────┼────────────┤
│ 2 │ < │ lt │
├────┼────────────┼────────────┤
│ 3 │ <= │ le │
├────┼────────────┼────────────┤
│ 4 │ == │ eq │
├────┼────────────┼────────────┤
│ 5 │ != │ ne │
╘════╧════════════╧════════════╛
Een andere optie om haakjes te vermijden is het gebruik van DataFrame.query
(of eval
):
df.query('A < 5 and B > 5')
A B C
1 3 7 9
3 4 7 6
Ik heb uitgebreidegedocumenteerde query
en eval
in Evaluatie van dynamische expressies in panda’s met pd.eval().
operator.and_
Hiermee kunt u deze bewerking op een functionele manier uitvoeren. Roept intern Series.__and__
aan wat overeenkomt met de bitsgewijze operator.
import operator
operator.and_(df['A'] < 5, df['B'] > 5)
# Same as,
# (df['A'] < 5).__and__(df['B'] > 5)
0 False
1 True
2 False
3 True
4 False
dtype: bool
df[operator.and_(df['A'] < 5, df['B'] > 5)]
A B C
1 3 7 9
3 4 7 6
Dit heb je normaal gesproken niet nodig, maar het is wel handig om te weten.
Algemeen: np.logical_and
(en logical_and.reduce
)
Een ander alternatief is het gebruik van np.logical_and
, waarvoor ook geen groepering tussen haakjes nodig is:
np.logical_and(df['A'] < 5, df['B'] > 5)
0 False
1 True
2 False
3 True
4 False
Name: A, dtype: bool
df[np.logical_and(df['A'] < 5, df['B'] > 5)]
A B C
1 3 7 9
3 4 7 6
np.logical_and
is een ufunc (universele functies), en de meeste ufuncs hebben een reduce
methode. Dit betekent dat het makkelijker is om te generaliseren met logical_and
als je meerdere maskers hebt naar AND. Als u bijvoorbeeld m1
en m2
en m3
wilt maskeren met &
, moet u
m1 & m2 & m3
Een eenvoudigere optie is echter
np.logical_and.reduce([m1, m2, m3])
Dit is krachtig, omdat je hier bovenop kunt bouwen met complexere logica (bijvoorbeeld dynamisch genereren van maskers in een lijstbegrip en ze allemaal toevoegen):
import operator
cols = ['A', 'B']
ops = [np.less, np.greater]
values = [5, 5]
m = np.logical_and.reduce([op(df[c], v) for op, c, v in zip(ops, cols, values)])
m
# array([False, True, False, True, False])
df[m]
A B C
1 3 7 9
3 4 7 6
1 – Ik weet dat ik op dit punt blijf hameren, maar heb alsjeblieft geduld. Dit is een zeer, zeerveelvoorkomende beginnersfout en moet zeer grondig worden uitgelegd.
Logisch OF
Voor de df
hierboven, stel dat je alle rijen wilt retourneren waarbij A == 3 of B == 7.
Overbelast Bitwise |
df['A'] == 3
0 False
1 True
2 True
3 False
4 False
Name: A, dtype: bool
df['B'] == 7
0 False
1 True
2 False
3 True
4 False
Name: B, dtype: bool
(df['A'] == 3) | (df['B'] == 7)
0 False
1 True
2 True
3 True
4 False
dtype: bool
df[(df['A'] == 3) | (df['B'] == 7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
Als je dat nog niet hebt gedaan, lees dan ook het gedeelte over Logisch ENhierboven, alle voorbehouden zijn hier van toepassing.
Als alternatief kan deze bewerking worden gespecificeerd met
df[df['A'].eq(3) | df['B'].eq(7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
operator.or_
Roept Series.__or__
onder de motorkap op.
operator.or_(df['A'] == 3, df['B'] == 7)
# Same as,
# (df['A'] == 3).__or__(df['B'] == 7)
0 False
1 True
2 True
3 True
4 False
dtype: bool
df[operator.or_(df['A'] == 3, df['B'] == 7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
np.logical_or
Gebruik voor twee voorwaarden logical_or
:
np.logical_or(df['A'] == 3, df['B'] == 7)
0 False
1 True
2 True
3 True
4 False
Name: A, dtype: bool
df[np.logical_or(df['A'] == 3, df['B'] == 7)]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
Gebruik voor meerdere maskers logical_or.reduce
:
np.logical_or.reduce([df['A'] == 3, df['B'] == 7])
# array([False, True, True, True, False])
df[np.logical_or.reduce([df['A'] == 3, df['B'] == 7])]
A B C
1 3 7 9
2 3 5 2
3 4 7 6
Logisch NIET
Gegeven een masker, zoals
mask = pd.Series([True, True, False])
Als u elke Booleaanse waarde moet omkeren (zodat het eindresultaat [False, False, True]
is), kunt u een van de onderstaande methoden gebruiken.
Bitwise ~
~mask
0 False
1 False
2 True
dtype: bool
Nogmaals, uitdrukkingen moeten tussen haakjes staan.
~(df['A'] == 3)
0 True
1 False
2 False
3 True
4 True
Name: A, dtype: bool
Dit roept intern
. op
mask.__invert__()
0 False
1 False
2 True
dtype: bool
Maar gebruik het niet rechtstreeks.
operator.inv
Belt intern __invert__
op de Series.
operator.inv(mask)
0 False
1 False
2 True
dtype: bool
np.logical_not
Dit is de numpy variant.
np.logical_not(mask)
0 False
1 False
2 True
dtype: bool
Opmerking, np.logical_and
kan worden vervangen door np.bitwise_and
, logical_or
door bitwise_or
, en logical_not
met invert
.
Antwoord 3, autoriteit 2%
Logische operatoren voor booleaanse indexering in Panda’s
Het is belangrijk om te beseffen dat je geen van de Python logische operatoren(and
, or
of not
) op pandas.Series
of pandas.DataFrame
s (op dezelfde manier kun je ze niet gebruiken op numpy.array
s met meer dan één element). De reden waarom je die niet kunt gebruiken is omdat ze impliciet bool
aanroepen op hun operanden, wat een Exception genereert omdat deze datastructuren besloten dat de boolean van een array dubbelzinnig is:
>>> import numpy as np
>>> import pandas as pd
>>> arr = np.array([1,2,3])
>>> s = pd.Series([1,2,3])
>>> df = pd.DataFrame([1,2,3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> bool(s)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> bool(df)
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Ik heb dit uitgebreider bedekt in mijn antwoord op de “Truth-waarde van een serie is dubbelzinnig. Gebruik A.Egty, a. BOOL (), A.item (), A.Alle () of A.Alle () “Q + A .
Numpys logische functies
Echter numpy biedt element-wise operationele equivalenten aan deze Operators als functies die kunnen worden gebruikt op numpy.array
, pandas.Series
, pandas.DataFrame
of een andere (conforme) numpy.array
subclass:
and
HEEFTnp.logical_and
or
HEEFTnp.logical_or
not
HEEFTnp.logical_not
numpy.logical_xor
die geen Python-equivalent heeft, maar is een logisch “exclusief of” bediening
Dus, in wezen, men moet gebruiken (aannemen van df1
en df2
zijn pandas-dataframes):
np.logical_and(df1, df2)
np.logical_or(df1, df2)
np.logical_not(df1)
np.logical_xor(df1, df2)
Bitise-functies en bitwise-exploitanten voor Booleans
Als u echter een boolean NumPy-array, pandas Series of pandas DataFrames heeft, kunt u ook de elementsgewijze bitsgewijze functies(voor booleans zijn ze – of zouden ze in ieder geval moeten zijn – niet te onderscheiden van de logische functies):
- bitwise en:
np.bitwise_and
of de&
operator - bitwise of:
np.bitwise_or
of de|
operator - bitsgewijs niet:
np.invert
(of de aliasnp.bitwise_not
) of de~
operator - bitwise xor:
np.bitwise_xor
of de^
operator
Normaal gesproken worden de operators gebruikt. In combinatie met vergelijkingsoperatoren moet men er echter aan denken om de vergelijking tussen haakjes te plaatsen, omdat de bitsgewijze operatoren een hogere prioriteit dan de vergelijkingsoperatoren:
(df1 < 10) | (df2 > 10) # instead of the wrong df1 < 10 | df2 > 10
Dit kan irritant zijn omdat de logische operatoren van Python een lagere prioriteit hebben dan de vergelijkingsoperatoren, dus je schrijft normaal gesproken a < 10 and b > 10
(waarbij a
en b
bijvoorbeeld eenvoudige gehele getallen zijn) en hebben geen haakjes nodig.
Verschillen tussen logische en bitsgewijze bewerkingen (op niet-booleans)
Het is erg belangrijk om te benadrukken dat bit- en logische bewerkingen alleen equivalent zijn voor Booleaanse NumPy-arrays (en Booleaanse Series & DataFrames). Als deze geen booleans bevatten, zullen de bewerkingen andere resultaten opleveren. Ik zal voorbeelden toevoegen met behulp van NumPy-arrays, maar de resultaten zullen vergelijkbaar zijn voor de gegevensstructuren van panda’s:
>>> import numpy as np
>>> a1 = np.array([0, 0, 1, 1])
>>> a2 = np.array([0, 1, 0, 1])
>>> np.logical_and(a1, a2)
array([False, False, False, True])
>>> np.bitwise_and(a1, a2)
array([0, 0, 0, 1], dtype=int32)
En aangezien NumPy (en soortgelijke panda’s) verschillende dingen doet voor boolean (Boolean of “mask” index-arrays) en integer (Index arrays) indexen zullen de resultaten van indexering ook anders zijn:
>>> a3 = np.array([1, 2, 3, 4])
>>> a3[np.logical_and(a1, a2)]
array([4])
>>> a3[np.bitwise_and(a1, a2)]
array([1, 1, 1, 2])
Samenvattingstabel
Logical operator | NumPy logical function | NumPy bitwise function | Bitwise operator
-------------------------------------------------------------------------------------
and | np.logical_and | np.bitwise_and | &
-------------------------------------------------------------------------------------
or | np.logical_or | np.bitwise_or | |
-------------------------------------------------------------------------------------
| np.logical_xor | np.bitwise_xor | ^
-------------------------------------------------------------------------------------
not | np.logical_not | np.invert | ~
Waar de logische operator niet werkt voor NumPy-arrays, pandas Series en pandas DataFrames. De anderen werken aan deze datastructuren (en gewone Python-objecten) en werken elementsgewijs.
Wees echter voorzichtig met de bitsgewijze invert op gewone Python bool
s, omdat de bool in deze context zal worden geïnterpreteerd als gehele getallen (bijvoorbeeld ~False
geeft -1
en ~True
retourneert -2
).