Chọn theo chuỗi một phần từ DataFrame của gấu trúc


446

Tôi có DataFrame4 cột trong đó có 2 cột chứa giá trị chuỗi. Tôi đã tự hỏi nếu có một cách để chọn các hàng dựa trên một chuỗi khớp một phần với một cột cụ thể?

Nói cách khác, một hàm hoặc hàm lambda sẽ làm một cái gì đó như

re.search(pattern, cell_in_question) 

trả lại một boolean. Tôi quen thuộc với cú pháp của df[df['A'] == "hello world"]nhưng dường như không thể tìm ra cách làm tương tự với một chuỗi khớp một phần 'hello'.

Ai đó sẽ có thể chỉ cho tôi đi đúng hướng?

Câu trả lời:


785

Dựa trên vấn đề github # 620 , có vẻ như bạn sẽ sớm có thể thực hiện các thao tác sau:

df[df['A'].str.contains("hello")]

Cập nhật: các phương thức chuỗi được vector hóa (nghĩa là Series.str) có sẵn trong gấu trúc 0.8.1 trở lên.


1
Làm thế nào để chúng ta đi về "Xin chào" và "Anh" nếu tôi muốn tìm thấy chúng với điều kiện "HOẶC".
LonelySoul

56
Vì các phương thức str. * Xử lý mẫu đầu vào như một biểu thức chính quy, bạn có thể sử dụngdf[df['A'].str.contains("Hello|Britain")]
Garrett

7
Có thể chuyển đổi .str.containsđể sử dụng .query()api ?
zyxue


3
df[df['value'].astype(str).str.contains('1234.+')]để lọc ra các cột không có chuỗi.
François Leblanc

213

Tôi đã thử giải pháp đề xuất ở trên:

df[df["A"].str.contains("Hello|Britain")]

và đã có một lỗi:

ValueError: không thể che dấu với mảng chứa giá trị NA / NaN

bạn có thể chuyển đổi các giá trị NA thành False, như thế này:

df[df["A"].str.contains("Hello|Britain", na=False)]

54
Hoặc bạn có thể làm: df [df ['A']. Str.contains ("Xin chào | Anh", na = Sai)]
joshlk

2
df[df['A'].astype(str).str.contains("Hello|Britain")]làm việc tốt
Nagabhushan SN

108

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.containscó 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ỉ ValueErrors
Đô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=Falsebỏ 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 ptrông như thế này,

p
# '\\b(?:foo|baz)\\b'

Một thay thế tuyệt vời: Sử dụng danh sách toàn diện !

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 intoá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.searchtrong 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

Thêm lựa chọn cho phần Chuỗi Matching: np.char.find, np.vectorize, DataFrame.query.

Ngoài str.containsvà 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 strphươ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ề queryevalhọ 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ị

  1. (Đầu tiên) str.contains, vì đơn giản và dễ dàng xử lý NaN và dữ liệu hỗn hợp
  2. 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)
  3. np.vectorize
  4. (Cuối cùng) df.query

Bạn có thể chỉnh sửa theo phương pháp chính xác để sử dụng khi tìm kiếm một chuỗi trong hai hoặc nhiều cột không? Về cơ bản: any(needle in haystack for needling in ['foo', 'bar'] and haystack in (df['col'], df['col2']))và các biến thể tôi đã thử tất cả các cuộn cảm (nó phàn nàn any()và đúng như vậy ... Nhưng tài liệu không rõ ràng về cách thực hiện một truy vấn như vậy.
Denis de Bernardy

@DenisdeBernardydf[['col1', 'col2']].apply(lambda x: x.str.contains('foo|bar')).any(axis=1)
cs95

@ cs95 Trích xuất các hàng với chuỗi con chứa khoảng trắng sau + trong pandas df Nó đã được trả lời sớm, nhưng bạn có thể muốn xem qua nó.
ankii

@ankiiiiiii Có vẻ như bạn đã bỏ lỡ phần câu trả lời của tôi, nơi tôi đã đề cập đến các siêu ký tự regex: "Đôi khi, thật khôn ngoan khi thoát các thuật ngữ của bạn trong trường hợp chúng có các ký tự có thể được hiểu là các siêu ký tự regex".
cs95

1
@ 00schneider r trong trường hợp này được sử dụng để chỉ một chuỗi ký tự thô. Những điều này làm cho nó dễ dàng hơn để viết các chuỗi biểu thức chính quy. stackoverflow.com/q/2081640
cs95

53

Nếu có ai thắc mắc làm thế nào để thực hiện một vấn đề liên quan: "Chọn cột theo chuỗi một phần"

Sử dụng:

df.filter(like='hello')  # select columns which contain the word hello

Và để chọn các hàng theo khớp chuỗi một phần, chuyển qua axis=0bộ lọc:

# selects rows which contain the word hello in their index label
df.filter(like='hello', axis=0)  

6
Điều này có thể được chưng cất để:df.loc[:, df.columns.str.contains('a')]
elPastor

18
có thể được chưng cất thêm vàodf.filter(like='a')
Ted Petrou

đây phải là một câu hỏi + câu trả lời riêng, đã có 50 người tìm kiếm nó ...
PV8

1
Câu hỏi @ PV8 đã tồn tại: stackoverflow.com/questions/31551412/ . Nhưng khi tôi tìm kiếm trên google cho "gấu trúc Chọn cột theo chuỗi một phần", chuỗi này xuất hiện đầu tiên
Philipp Schwarz

28

Lưu ý nhanh: nếu bạn muốn thực hiện lựa chọn dựa trên chuỗi một phần có trong chỉ mục, hãy thử cách sau:

df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]

5
Bạn chỉ có thể df [df.index.to_series (). Str.contains ('LLChit')]
Yury Bayda

21

Nói rằng bạn có những điều sau đây DataFrame:

>>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b'])
>>> df
       a            b
0  hello  hello world
1   abcd         defg

Bạn luôn có thể sử dụng intoán tử trong biểu thức lambda để tạo bộ lọc của mình.

>>> df.apply(lambda x: x['a'] in x['b'], axis=1)
0     True
1    False
dtype: bool

Mẹo ở đây là sử dụng axis=1tùy chọn trong việc applytruyền các phần tử cho hàng hàm lambda theo hàng, trái ngược với từng cột.


Làm cách nào để sửa đổi ở trên để nói rằng x ['a'] chỉ tồn tại ở đầu x ['b']?
ComplexData

1
áp dụng là một ý tưởng tồi ở đây về hiệu suất và bộ nhớ. Xem câu trả lời này .
cs95

8

Đây là những gì tôi đã làm cho kết hợp chuỗi một phần. Nếu bất cứ ai có một cách hiệu quả hơn để làm điều này xin vui lòng cho tôi biết.

def stringSearchColumn_DataFrame(df, colName, regex):
    newdf = DataFrame()
    for idx, record in df[colName].iteritems():

        if re.search(regex, record):
            newdf = concat([df[df[colName] == record], newdf], ignore_index=True)

    return newdf

3
Nên nhanh hơn gấp 2 đến 3 lần nếu bạn biên dịch regex trước vòng lặp: regex = re.compile (regex) và sau đó nếu regex.search (bản ghi)
MarkokraM

1
@MarkokraM docs.python.org/3.6/library/re.html#re.compile nói rằng hầu hết các regexs gần đây được lưu trữ cho bạn, vì vậy bạn không cần phải biên dịch cho mình.
Teepeemm

Không sử dụng iteritems để lặp qua DataFrame. Nó xếp hạng cuối cùng về độ ổn định và hiệu suất
cs95

5

Sử dụng chứa không hoạt động tốt cho chuỗi của tôi với các ký tự đặc biệt. Tìm làm việc mặc dù.

df[df['A'].str.find("hello") != -1]

2

Có những câu trả lời trước đây để thực hiện tính năng được hỏi, dù sao tôi cũng muốn trình bày một cách chung nhất:

df.filter(regex=".*STRING_YOU_LOOK_FOR.*")

Bằng cách này, hãy để bạn có được cột bạn tìm kiếm bất cứ cách nào được viết.

(Tuyệt vời, bạn phải viết biểu thức regex thích hợp cho từng trường hợp)


1
Bộ lọc này trên các tiêu đề cột . Nó không chung chung, nó không chính xác.
cs95

@MicheldeRuiter vẫn không chính xác, thay vào đó, nó sẽ lọc trên các nhãn chỉ mục!
cs95

Không trả lời câu hỏi. Nhưng tôi đã học được điều gì đó. :)
Michel de Ruiter

2

Có thể bạn muốn tìm kiếm một số văn bản trong tất cả các cột của khung dữ liệu Pandas và không chỉ trong tập hợp con của chúng. Trong trường hợp này, mã sau đây sẽ giúp.

df[df.apply(lambda row: row.astype(str).str.contains('String To Find').any(), axis=1)]

Cảnh báo. Phương pháp này tương đối chậm, mặc dù thuận tiện.


1

Nếu bạn cần thực hiện tìm kiếm không phân biệt chữ hoa chữ thường cho một chuỗi trong cột khung dữ liệu gấu trúc:

df[df['A'].str.contains("hello", case=False)]
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.