Làm cách nào để tôi chọn theo chuỗi một phần từ DataFrame của gấu trúc?
Bài viết này là dành cho độc giả muốn
- tìm kiếm một chuỗi con trong một cột chuỗi (trường hợp đơn giản nhất)
- tìm kiếm nhiều chuỗi con (tương tự
isin
)
- khớp toàn bộ từ trong văn bản (ví dụ: "màu xanh" phải khớp với "bầu trời là màu xanh" nhưng không phải là "màu xanh da trời")
- nối nhiều từ
- Hiểu lý do đằng sau "ValueError: không thể lập chỉ mục với vectơ chứa giá trị NA / NaN"
... Và muốn biết thêm về phương pháp nào nên được ưu tiên hơn các phương pháp khác.
(PS: Tôi đã thấy rất nhiều câu hỏi về các chủ đề tương tự, tôi nghĩ sẽ tốt hơn nếu để nó ở đây.)
Tìm kiếm chuỗi con cơ bản
# setup
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']})
df1
col
0 foo
1 foobar
2 bar
3 baz
str.contains
có thể được sử dụng để thực hiện tìm kiếm chuỗi con hoặc tìm kiếm dựa trên regex. Tìm kiếm mặc định dựa trên regex trừ khi bạn vô hiệu hóa nó một cách rõ ràng.
Dưới đây là một ví dụ về tìm kiếm dựa trên regex,
# find rows in `df1` which contain "foo" followed by something
df1[df1['col'].str.contains(r'foo(?!$)')]
col
1 foobar
Đôi khi tìm kiếm regex là không cần thiết, vì vậy chỉ định regex=False
để vô hiệu hóa nó.
#select all rows containing "foo"
df1[df1['col'].str.contains('foo', regex=False)]
# same as df1[df1['col'].str.contains('foo')] but faster.
col
0 foo
1 foobar
Hiệu suất khôn ngoan, tìm kiếm regex chậm hơn tìm kiếm chuỗi con:
df2 = pd.concat([df1] * 1000, ignore_index=True)
%timeit df2[df2['col'].str.contains('foo')]
%timeit df2[df2['col'].str.contains('foo', regex=False)]
6.31 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.8 ms ± 241 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Tránh sử dụng tìm kiếm dựa trên regex nếu bạn không cần nó.
Địa chỉ ValueError
s
Đôi khi, thực hiện tìm kiếm chuỗi con và lọc kết quả sẽ dẫn đến
ValueError: cannot index with vector containing NA / NaN values
Điều này thường là do dữ liệu hỗn hợp hoặc NaN trong cột đối tượng của bạn,
s = pd.Series(['foo', 'foobar', np.nan, 'bar', 'baz', 123])
s.str.contains('foo|bar')
0 True
1 True
2 NaN
3 True
4 False
5 NaN
dtype: object
s[s.str.contains('foo|bar')]
# ---------------------------------------------------------------------------
# ValueError Traceback (most recent call last)
Bất cứ điều gì không phải là một chuỗi đều không thể áp dụng các phương thức chuỗi, vì vậy kết quả là NaN (tự nhiên). Trong trường hợp này, chỉ định na=False
bỏ qua dữ liệu không phải chuỗi,
s.str.contains('foo|bar', na=False)
0 True
1 True
2 False
3 True
4 False
5 False
dtype: bool
Tìm kiếm nhiều chuỗi con
Điều này dễ dàng đạt được nhất thông qua tìm kiếm regex bằng cách sử dụng ống regex OR.
# Slightly modified example.
df4 = pd.DataFrame({'col': ['foo abc', 'foobar xyz', 'bar32', 'baz 45']})
df4
col
0 foo abc
1 foobar xyz
2 bar32
3 baz 45
df4[df4['col'].str.contains(r'foo|baz')]
col
0 foo abc
1 foobar xyz
3 baz 45
Bạn cũng có thể tạo một danh sách các điều khoản, sau đó tham gia chúng:
terms = ['foo', 'baz']
df4[df4['col'].str.contains('|'.join(terms))]
col
0 foo abc
1 foobar xyz
3 baz 45
Đôi khi, thật khôn ngoan khi thoát khỏi các điều khoản của bạn trong trường hợp họ có các ký tự có thể được hiểu là các siêu ký tự regex . Nếu các điều khoản của bạn có chứa bất kỳ ký tự nào sau đây ...
. ^ $ * + ? { } [ ] \ | ( )
Sau đó, bạn sẽ cần sử dụng re.escape
để thoát khỏi chúng:
import re
df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))]
col
0 foo abc
1 foobar xyz
3 baz 45
re.escape
có tác dụng thoát khỏi các ký tự đặc biệt để chúng được xử lý theo nghĩa đen.
re.escape(r'.foo^')
# '\\.foo\\^'
Kết hợp toàn bộ từ (s)
Theo mặc định, tìm kiếm chuỗi con tìm kiếm cho chuỗi / mẫu con được chỉ định bất kể đó có phải là từ đầy đủ hay không. Để chỉ khớp các từ đầy đủ, chúng ta sẽ cần sử dụng các cụm từ thông dụng ở đây, đặc biệt, mẫu của chúng ta sẽ cần chỉ định ranh giới từ ( \b
).
Ví dụ,
df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']})
df3
col
0 the sky is blue
1 bluejay by the window
Bây giờ hãy xem xét,
df3[df3['col'].str.contains('blue')]
col
0 the sky is blue
1 bluejay by the window
v / s
df3[df3['col'].str.contains(r'\bblue\b')]
col
0 the sky is blue
Nhiều tìm kiếm toàn bộ từ
Tương tự như trên, ngoại trừ chúng ta thêm một ranh giới từ ( \b
) vào mẫu đã nối.
p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms)))
df4[df4['col'].str.contains(p)]
col
0 foo abc
3 baz 45
Nơi này p
trông như thế này,
p
# '\\b(?:foo|baz)\\b'
Bởi vì bạn có thể! Và bạn nên! Chúng thường nhanh hơn một chút so với các phương thức chuỗi, bởi vì các phương thức chuỗi rất khó để vectorise và thường có các triển khai loopy.
Thay vì,
df1[df1['col'].str.contains('foo', regex=False)]
Sử dụng in
toán tử bên trong một danh sách comp,
df1[['foo' in x for x in df1['col']]]
col
0 foo abc
1 foobar
Thay vì,
regex_pattern = r'foo(?!$)'
df1[df1['col'].str.contains(regex_pattern)]
Sử dụng re.compile
(để lưu trữ regex của bạn) + Pattern.search
trong danh sách comp,
p = re.compile(regex_pattern, flags=re.IGNORECASE)
df1[[bool(p.search(x)) for x in df1['col']]]
col
1 foobar
Nếu "col" có NaN, thì thay vì
df1[df1['col'].str.contains(regex_pattern, na=False)]
Sử dụng,
def try_search(p, x):
try:
return bool(p.search(x))
except TypeError:
return False
p = re.compile(regex_pattern)
df1[[try_search(p, x) for x in df1['col']]]
col
1 foobar
Ngoài str.contains
và liệt kê những hiểu biết, bạn cũng có thể sử dụng các lựa chọn thay thế sau đây.
np.char.find
Chỉ hỗ trợ tìm kiếm chuỗi con (đọc: không có regex).
df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1]
col
0 foo abc
1 foobar xyz
np.vectorize
Đây là một trình bao bọc xung quanh một vòng lặp, nhưng với chi phí thấp hơn so với hầu hết các str
phương pháp gấu trúc .
f = np.vectorize(lambda haystack, needle: needle in haystack)
f(df1['col'], 'foo')
# array([ True, True, False, False])
df1[f(df1['col'], 'foo')]
col
0 foo abc
1 foobar
Giải pháp Regex có thể:
regex_pattern = r'foo(?!$)'
p = re.compile(regex_pattern)
f = np.vectorize(lambda x: pd.notna(x) and bool(p.search(x)))
df1[f(df1['col'])]
col
1 foobar
DataFrame.query
Hỗ trợ các phương thức chuỗi thông qua công cụ python. Điều này cung cấp không có lợi ích hiệu suất có thể nhìn thấy, nhưng dù sao cũng hữu ích để biết nếu bạn cần tự động tạo các truy vấn của mình.
df1.query('col.str.contains("foo")', engine='python')
col
0 foo
1 foobar
Thông tin thêm về query
và eval
họ phương pháp có thể được tìm thấy tại Đánh giá biểu hiện động trong gấu trúc bằng cách sử dụng pd.eval () .
Ưu tiên sử dụng được khuyến nghị
- (Đầu tiên)
str.contains
, vì đơn giản và dễ dàng xử lý NaN và dữ liệu hỗn hợp
- Liệt kê mức độ hiểu, về hiệu suất của nó (đặc biệt nếu dữ liệu của bạn hoàn toàn là chuỗi)
np.vectorize
- (Cuối cùng)
df.query