Toán tử logic để lập chỉ mục boolean trong Pandas


152

Tôi đang làm việc với chỉ số boolean ở Pandas. Câu hỏi là tại sao tuyên bố:

a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]

hoạt động tốt trong khi

a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]

thoát với lỗi?

Thí dụ:

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()

6
Điều này là do các mảng và chuỗi gấu trúc numpy sử dụng các toán tử bitwise thay vì logic khi bạn so sánh mọi phần tử trong mảng / chuỗi với nhau. Do đó, không có nghĩa gì khi sử dụng toán tử logic trong tình huống này. xem có liên quan: stackoverflow.com/questions/8632033/
Mạnh

9
Trong Python and != &. Các andnhà điều hành trong Python không thể ghi đè, trong khi các &nhà điều hành ( __and__) có thể. Do đó sự lựa chọn sử dụng &trong numpy và gấu trúc.
Steven Rumbalski

Câu trả lời:


208

Khi bạn nói

(a['x']==1) and (a['y']==10)

Bạn đang ngầm yêu cầu Python chuyển đổi (a['x']==1)và chuyển (a['y']==10)sang các giá trị boolean.

Các mảng NumPy (có độ dài lớn hơn 1) và các đối tượng Pandas như Sê-ri không có giá trị boolean - nói cách khác, chúng nâng lên

ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().

khi được sử dụng như một giá trị boolean. Đó là bởi vì nó không rõ ràng khi nào nên đúng hay sai . Một số người dùng có thể cho rằng chúng là True nếu chúng có độ dài khác không, như danh sách Python. Những người khác có thể mong muốn nó là True chỉ khi tất cả các yếu tố của nó là True. Những người khác có thể muốn nó là True nếu bất kỳ yếu tố nào của nó là True.

Bởi vì có rất nhiều kỳ vọng mâu thuẫn, các nhà thiết kế NumPy và Pandas từ chối đoán, và thay vào đó đưa ra một ValueError.

Thay vào đó, bạn phải rõ ràng, bằng cách gọi empty(), all()hoặc any()phương thức để chỉ ra hành vi nào bạn mong muốn.

Tuy nhiên, trong trường hợp này, có vẻ như bạn không muốn đánh giá boolean, bạn muốn logic- phần tử -và. Đó là những gì &toán tử nhị phân thực hiện:

(a['x']==1) & (a['y']==10)

trả về một mảng boolean.


Nhân tiện, như alexpmil lưu ý , dấu ngoặc đơn là bắt buộc vì &quyền ưu tiên toán tử cao hơn ==. Nếu không có dấu ngoặc đơn, a['x']==1 & a['y']==10sẽ được đánh giá a['x'] == (1 & a['y']) == 10lần lượt tương đương với so sánh chuỗi (a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10). Đó là một biểu hiện của hình thức Series and Series. Việc sử dụng andvới hai Series sẽ lại kích hoạt ValueErrornhư trên. Đó là lý do tại sao dấu ngoặc đơn là bắt buộc.


3
mảng numpy có thuộc tính này nếu chúng có độ dài một. Chỉ có gấu trúc dev (bướng bỉnh) từ chối đoán: p
Andy Hayden

4
Không '&' mang đường cong mơ hồ như 'và'? Làm thế nào khi nói đến '&', đột nhiên tất cả người dùng đều đồng ý rằng nó phải là yếu tố khôn ngoan, trong khi khi họ thấy 'và', kỳ vọng của họ khác nhau?
Indominus

16
@Indominus: Bản thân ngôn ngữ Python yêu cầu biểu thức x and ykích hoạt việc đánh giá bool(x)bool(y). Python "đầu tiên đánh giá x; nếu xsai, giá trị của nó được trả về; nếu không, yđược ước tính và giá trị kết quả được trả về." Vì vậy, cú pháp x and ykhông thể được sử dụng cho logic có phần tử - và chỉ xhoặc ycó thể được trả về. Ngược lại, x & ykích hoạt x.__and__(y)__and__phương thức có thể được xác định để trả về bất cứ thứ gì chúng ta muốn.
unutbu

2
Điều quan trọng cần lưu ý: các dấu ngoặc đơn xung quanh ==mệnh đề là bắt buộc . a['x']==1 & a['y']==10trả về lỗi tương tự như trong câu hỏi
Alex P. Miller

1
"|" Để làm gì?
Euler_Salter

62

TLDR; Toán tử logic trong Pandas là &, |~, dấu ngoặc đơn (...)là quan trọng!

Python là and, ornotkhai thác hợp lý được thiết kế để làm việc với vô hướng. Vì vậy, Pandas phải làm tốt hơn một lần nữa và ghi đè các toán tử bitwise để đạt được phiên bản véc tơ (yếu tố khôn ngoan) của chức năng này.

Vì vậy, sau đây trong python ( exp1exp2là các biểu thức đánh giá kết quả boolean) ...

exp1 and exp2              # Logical AND
exp1 or exp2               # Logical OR
not exp1                   # Logical NOT

... sẽ dịch sang ...

exp1 & exp2                # Element-wise logical AND
exp1 | exp2                # Element-wise logical OR
~exp1                      # Element-wise logical NOT

cho gấu trúc.

Nếu trong quá trình thực hiện thao tác logic bạn nhận được a ValueError, thì bạn cần sử dụng dấu ngoặc đơn để nhóm:

(exp1) op (exp2)

Ví dụ,

(df['col1'] == x) & (df['col2'] == y) 

Và như thế.


Lập chỉ mục Boolean : Một hoạt động phổ biến là tính toán các mặt nạ boolean thông qua các điều kiện logic để lọc dữ liệu. Pandas cung cấp ba toán tử:&cho logic AND,|cho logic OR và~cho logic KHÔNG.

Hãy xem xét các thiết lập sau:

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

Logic VÀ

Đối với dfở trên, giả sử bạn muốn trả về tất cả các hàng trong đó A <5 và B> 5. Điều này được thực hiện bằng cách tính mặt nạ cho từng điều kiện riêng biệt và ANDing chúng.

&Toán tử bitwise quá tải
Trước khi tiếp tục, vui lòng lưu ý đoạn trích cụ thể này của tài liệu, trạng thái

Một hoạt động phổ biến khác là sử dụng các vectơ boolean để lọc dữ liệu. Các toán tử là: |for or, &for and~for not. Chúng phải được nhóm lại bằng cách sử dụng dấu ngoặc đơn , vì theo mặc định Python sẽ đánh giá một biểu thức df.A > 2 & df.B < 3như df.A > (2 & df.B) < 3, trong khi thứ tự đánh giá mong muốn là (df.A > 2) & (df.B < 3).

Vì vậy, với ý nghĩ này, phần tử logic logic VÀ có thể được thực hiện với toán tử bitwise &:

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

Và bước lọc tiếp theo chỉ đơn giản là,

df[(df['A'] < 5) & (df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

Các dấu ngoặc đơn được sử dụng để ghi đè thứ tự ưu tiên mặc định của các toán tử bitwise, có độ ưu tiên cao hơn các toán tử có điều kiện <>. Xem phần Ưu tiên của Người vận hành trong tài liệu python.

Nếu bạn không sử dụng dấu ngoặc đơn, biểu thức được đánh giá không chính xác. Ví dụ: nếu bạn vô tình thử thứ gì đó như

df['A'] < 5 & df['B'] > 5

Nó được phân tích thành

df['A'] < (5 & df['B']) > 5

Trở thành,

df['A'] < something_you_dont_want > 5

Mà trở thành (xem tài liệu python về so sánh toán tử chuỗi ),

(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)

Trở thành,

# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2

Mà ném

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Vì vậy, đừng phạm sai lầm! 1

Tránh dấu ngoặc đơn Nhóm cách
khắc phục thực sự khá đơn giản. Hầu hết các toán tử có một phương thức ràng buộc tương ứng cho DataFrames. Nếu các mặt nạ riêng lẻ được xây dựng bằng cách sử dụng các hàm thay vì các toán tử có điều kiện, bạn sẽ không còn cần phải nhóm theo parens để chỉ định thứ tự đánh giá:

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

Xem phần về So sánh linh hoạt. . Tóm lại, chúng ta có

╒════╤════════════╤════════════╕
     Operator    Function   
╞════╪════════════╪════════════╡
  0  >           gt         
├────┼────────────┼────────────┤
  1  >=          ge         
├────┼────────────┼────────────┤
  2  <           lt         
├────┼────────────┼────────────┤
  3  <=          le         
├────┼────────────┼────────────┤
  4  ==          eq         
├────┼────────────┼────────────┤
  5  !=          ne         
╘════╧════════════╧════════════╛

Một tùy chọn khác để tránh dấu ngoặc đơn là sử dụng DataFrame.query(hoặc eval):

df.query('A < 5 and B > 5')

   A  B  C
1  3  7  9
3  4  7  6

Tôi đã ghi chép rộng rãiqueryevaltrong Đánh giá biểu hiện động trong gấu trúc bằng cách sử dụng pd.eval () .

operator.and_
Cho phép bạn thực hiện thao tác này một cách có chức năng. Các cuộc gọi nội bộ Series.__and__tương ứng với toán tử bitwise.

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

Bạn sẽ không cần điều này, nhưng nó rất hữu ích để biết.

Tổng quát hóa: np.logical_and(và logical_and.reduce)
Một cách khác là sử dụng np.logical_and, cũng không cần nhóm dấu ngoặc đơn:

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_andlà một ufunc (Hàm đa năng) và hầu hết các ufunc đều có một reducephương thức. Điều này có nghĩa là việc khái quát hóa sẽ dễ dàng hơn logical_andnếu bạn có nhiều mặt nạ để AND. Ví dụ, để VÀ mặt nạ m1m2m3với &, bạn sẽ phải làm

m1 & m2 & m3

Tuy nhiên, một lựa chọn dễ dàng hơn là

np.logical_and.reduce([m1, m2, m3])

Điều này rất mạnh mẽ, bởi vì nó cho phép bạn xây dựng trên đầu trang này với logic phức tạp hơn (ví dụ: tạo mặt nạ động trong phần hiểu danh sách và thêm tất cả chúng):

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 - Tôi biết tôi đang làm phiền về điểm này, nhưng xin hãy đồng ý với tôi. Đây là một sai lầm rất , rất phổ biến của người mới bắt đầu, và phải được giải thích rất kỹ lưỡng.


Hợp lý HOẶC

Đối với phần dftrên, giả sử bạn muốn trả về tất cả các hàng trong đó A == 3 hoặc B == 7.

Quá tải 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

Nếu bạn chưa có, xin vui lòng đọc phần trên Logic VÀ ở trên, tất cả các cảnh báo áp dụng ở đây.

Ngoài ra, hoạt động này có thể được chỉ định với

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_
Các cuộc gọi Series.__or__dưới mui xe.

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
Đối với hai điều kiện, sử dụng 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

Đối với nhiều mặt nạ, sử dụng 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

Logic KHÔNG

Đưa ra một mặt nạ, chẳng hạn như

mask = pd.Series([True, True, False])

Nếu bạn cần đảo ngược mọi giá trị boolean (để kết quả cuối cùng là [False, False, True]), thì bạn có thể sử dụng bất kỳ phương thức nào dưới đây.

Bitwise ~

~mask

0    False
1    False
2     True
dtype: bool

Một lần nữa, các biểu thức cần phải được ngoặc.

~(df['A'] == 3)

0     True
1    False
2    False
3     True
4     True
Name: A, dtype: bool

Điều này gọi nội bộ

mask.__invert__()

0    False
1    False
2     True
dtype: bool

Nhưng đừng sử dụng nó trực tiếp.

operator.inv
Gọi nội bộ __invert__trên Series.

operator.inv(mask)

0    False
1    False
2     True
dtype: bool

np.logical_not
Đây là biến thể numpy.

np.logical_not(mask)

0    False
1    False
2     True
dtype: bool

Lưu ý, np.logical_andcó thể được thay thế cho np.bitwise_and, logical_orvới bitwise_orlogical_notvới invert.


@ cs95 trong TLDR, đối với boolean phần tử khôn ngoan HOẶC, bạn ủng hộ việc sử dụng |, tương đương với numpy.bitwise_or, thay vì numpy.logical_or. Tôi có thể hỏi tại sao không? Không numpy.logical_orđược thiết kế cho nhiệm vụ này cụ thể? Tại sao thêm gánh nặng của việc thực hiện nó theo từng bit cho từng cặp phần tử?
Flow2k

@ Flow2k bạn có thể trích dẫn văn bản liên quan không? Tôi không thể tìm thấy những gì bạn đang đề cập đến. FWIW Tôi duy trì logic_ * là tương đương chức năng chính xác của các toán tử.
cs95

@ cs95 Tôi đang đề cập đến dòng đầu tiên của Câu trả lời: "TLDR; Toán tử logic trong Pandas là &, | và ~".
Flow2k

@ Flow2k Nghĩa đen là trong tài liệu : "Một thao tác phổ biến khác là sử dụng các vectơ boolean để lọc dữ liệu. Các toán tử là: | for hoặc, & for và, và ~ for."
cs95

@ cs95, ok, tôi chỉ đọc phần này và nó sử dụng |cho hoạt động boolean phần tử. Nhưng đối với tôi, tài liệu đó giống như một "hướng dẫn", và ngược lại, tôi cảm thấy các tài liệu tham khảo API này gần với nguồn gốc của sự thật: numpy.bitwise_ornumpy.logical_or - vì vậy tôi đang cố gắng hiểu ý nghĩa của nó mô tả ở đây.
Flow2k

4

Toán tử logic để lập chỉ mục boolean trong Pandas

Điều quan trọng là nhận ra rằng bạn không thể sử dụng bất kỳ Python toán tử logic ( and, orhoặc not) trên pandas.Serieshoặc pandas.DataFrames (tương tự như bạn không thể sử dụng chúng trên numpy.arrays với nhiều hơn một phần tử). Lý do tại sao bạn không thể sử dụng chúng là vì chúng ngầm gọi boolcác toán hạng của chúng, ném ra một ngoại lệ vì các cấu trúc dữ liệu này quyết định rằng boolean của một mảng là mơ hồ:

>>> 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().

Tôi đã trình bày rộng rãi hơn trong câu trả lời của mình cho "Giá trị thật của một chuỗi là mơ hồ. Sử dụng a.empty, a.bool (), a.item (), a.any () hoặc a.all ()" Q + A .

Hàm logic NumPys

Tuy nhiên NumPy cung cấp tương đương điều hành yếu tố khôn ngoan để các nhà khai thác như các chức năng có thể được sử dụng trên numpy.array, pandas.Series, pandas.DataFrame, hoặc bất kỳ khác (phù hợp) numpy.arraylớp con:

Vì vậy, về cơ bản, người ta nên sử dụng (giả sử df1df2là gấu trúc DataFrames):

np.logical_and(df1, df2)
np.logical_or(df1, df2)
np.logical_not(df1)
np.logical_xor(df1, df2)

Hàm bitwise và toán tử bitwise cho booleans

Tuy nhiên, trong trường hợp bạn có mảng NumPy boolean, sê-ri gấu trúc hoặc gấu trúc DataFrames, bạn cũng có thể sử dụng các hàm bitwise khôn ngoan của phần tử (đối với booleans chúng - hoặc ít nhất nên - không thể phân biệt được với các hàm logic):

Thông thường các toán tử được sử dụng. Tuy nhiên, khi kết hợp với các toán tử so sánh, người ta phải nhớ bọc so sánh trong ngoặc đơn vì các toán tử bitwise có độ ưu tiên cao hơn các toán tử so sánh :

(df1 < 10) | (df2 > 10)  # instead of the wrong df1 < 10 | df2 > 10

Điều này có thể khó chịu vì các toán tử logic Python có một precendence thấp hơn so với các nhà khai thác so sánh, do đó bạn thường ghi a < 10 and b > 10(nơi abđược cho số nguyên ví dụ đơn giản) và không cần dấu ngoặc.

Sự khác nhau giữa các phép toán logic và bitwise (trên các giá trị không phải là booleans)

Điều thực sự quan trọng là nhấn mạnh rằng các hoạt động logic và bit chỉ tương đương với các mảng NumPy boolean (và Chuỗi boolean & DataFrames). Nếu những cái này không chứa booleans thì các hoạt động sẽ cho kết quả khác nhau. Tôi sẽ bao gồm các ví dụ sử dụng mảng NumPy nhưng kết quả sẽ tương tự đối với cấu trúc dữ liệu của gấu trúc:

>>> 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)

Và vì NumPy (và tương tự gấu trúc) thực hiện những điều khác nhau đối với boolean ( mảng chỉ số Boolean hoặc mặt nạ đường mật ) và số nguyên ( mảng chỉ mục ), kết quả của việc lập chỉ mục cũng sẽ khác nhau:

>>> 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])

Bảng tóm tắt

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              |        ~

Trường hợp toán tử logic không hoạt động cho mảng NumPy , Sê-ri gấu trúc và DataFrames của gấu trúc. Những cái khác hoạt động trên các cấu trúc dữ liệu này (và các đối tượng Python đơn giản) và làm việc theo yếu tố. Tuy nhiên, hãy cẩn thận với nghịch đảo bitwise trên Python đơn giản boolvì bool sẽ được hiểu là số nguyên trong ngữ cảnh này (ví dụ ~Falsetrả về -1~Truetrả về -2).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.