pandas DataFrameから部分文字列で選択


447

私にはDataFrame4つの列があり、そのうち2つには文字列値が含まれています。特定の列に対する部分的な文字列の一致に基づいて行を選択する方法があるかどうか疑問に思いましたか?

言い換えると、次のような関数またはラムダ関数

re.search(pattern, cell_in_question) 

ブール値を返します。私はの構文に精通していますがdf[df['A'] == "hello world"]、部分的な文字列一致sayで同じことを行う方法を見つけることができないようです'hello'

誰かが私を正しい方向に向けることができるでしょうか?

回答:


786

github issue #620に基づいて、次のことをすぐに実行できるようになります:

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

更新:ベクトル化された文字列メソッド(つまり、Series.str)、pandas 0.8.1以降で使用できます。


1
「OR」条件で「ハロー」と「イギリス」を検索するにはどうすればよいですか。
LonelySoul 2013年

56
str。*メソッドは入力パターンを正規表現として扱うため、次を使用できますdf[df['A'].str.contains("Hello|Britain")]
Garrett

7
API.str.containsを使用するように変換することは可能ですか?.query()
zyxue 2017年


3
df[df['value'].astype(str).str.contains('1234.+')]非文字列型の列を除外するため。
フランソワルブラン

213

私は上記の提案された解決策を試しました:

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

エラーが発生しました:

ValueError:NA / NaN値を含む配列でマスクすることはできません

次のFalseように、NA値をに変換できます。

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

54
または、次のようにできます:df [df ['A']。str.contains( "Hello | Britain"、na = False)]
joshlk

2
df[df['A'].astype(str).str.contains("Hello|Britain")]同様に働いた
ナガブシャンSN

108

pandas DataFrameから部分文字列で選択するにはどうすればよいですか?

この投稿は、読者が

  • 文字列列の部分文字列を検索します(最も単純なケース)
  • 複数の部分文字列を検索する(と同様isin
  • テキストの単語全体に一致します(たとえば、「青」は「空は青」に一致しますが、「bluejay」には一致しません)
  • 複数の単語全体に一致
  • 「ValueError:NA / NaN値を含むベクトルでインデックスを作成できない」の背後にある理由を理解する

...そして、他の方法よりも優先される方法について詳しく知りたい。

(追記:同様のトピックについて多くの質問を見てきました。ここに残しておくとよいと思いました。)


基本的な部分文字列検索

# setup
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']})
df1

      col
0     foo
1  foobar
2     bar
3     baz

str.contains部分文字列検索または正規表現ベースの検索を実行するために使用できます。明示的に無効にしない限り、検索はデフォルトで正規表現ベースになります。

これは正規表現ベースの検索の例です、

# find rows in `df1` which contain "foo" followed by something
df1[df1['col'].str.contains(r'foo(?!$)')]

      col
1  foobar

正規表現検索が不要な場合があるためregex=False、無効にするように指定します。

#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

パフォーマンスに関しては、正規表現検索は部分文字列検索よりも低速です。

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)

必要がない場合は、正規表現ベースの検索を使用しないでください。

アドレス指定ValueErrors
部分文字列検索を実行し、結果をフィルタリングすると、

ValueError: cannot index with vector containing NA / NaN values

これは通常、オブジェクト列の混合データまたはNaNが原因です。

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)

文字列ではないものには文字列メソッドを適用できないため、結果は(当然)NaNになります。この場合、na=False文字列以外のデータを無視するように指定し、

s.str.contains('foo|bar', na=False)

0     True
1     True
2    False
3     True
4    False
5    False
dtype: bool

複数の部分文字列検索

これは、正規表現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

用語のリストを作成して、それらに参加することもできます。

terms = ['foo', 'baz']
df4[df4['col'].str.contains('|'.join(terms))]

          col
0     foo abc
1  foobar xyz
3      baz 45

正規表現のメタ文字として解釈できる文字が含まれている場合は、用語をエスケープすることが賢明な場合があります。用語に次のいずれかの文字が含まれている場合...

. ^ $ * + ? { } [ ] \ | ( )

次に、を使用re.escapeそれらをエスケープする必要があります。

import re
df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))]

          col
0     foo abc
1  foobar xyz
3      baz 45

re.escape 特殊文字をエスケープする効果があるため、文字どおりに処理されます。

re.escape(r'.foo^')
# '\\.foo\\^'

単語全体を一致させる

デフォルトでは、部分文字列検索は、指定された部分文字列/パターンがフルワードかどうかに関係なく検索します。完全な単語にのみ一致させるには、ここで正規表現を使用する必要があります\b。特に、パターンでは単語の境界()を指定する必要があります。

例えば、

df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']})
df3

                     col
0        the sky is blue
1  bluejay by the window

今考えてください、

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

複数の全単語検索

上記と同様ですが、\b結合されたパターンに単語境界()を追加します。

p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms)))
df4[df4['col'].str.contains(p)]

       col
0  foo abc
3   baz 45

pこんなところ

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

優れた代替手段:リスト内包表記を使用してください!

できるから!そして、あなたはすべきです!文字列メソッドはベクトル化するのが難しく、通常はルーピーな実装であるため、通常は文字列メソッドよりも少し高速です。

の代わりに、

df1[df1['col'].str.contains('foo', regex=False)]

inリストカンプ内で演算子を使用し、

df1[['foo' in x for x in df1['col']]]

       col
0  foo abc
1   foobar

の代わりに、

regex_pattern = r'foo(?!$)'
df1[df1['col'].str.contains(regex_pattern)]

re.compile(正規表現をキャッシュするために)+ Pattern.searchリストカンプ内で使用します

p = re.compile(regex_pattern, flags=re.IGNORECASE)
df1[[bool(p.search(x)) for x in df1['col']]]

      col
1  foobar

「col」にNaNがある場合、代わりに

df1[df1['col'].str.contains(regex_pattern, na=False)]

使用する、

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

部分文字列照合のためのその他のオプション:np.char.findnp.vectorizeDataFrame.query

str.contains内包表記に加えて、次の選択肢を使用することもできます。

np.char.find
サブストリング検索(読み取り:正規表現なし)のみをサポートします。

df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1]

          col
0     foo abc
1  foobar xyz

np.vectorize
これはループのラッパーですが、ほとんどのパンダstrメソッドよりもオーバーヘッドが少なくなります。

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

可能な正規表現ソリューション:

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
Pythonエンジンを介して文字列メソッドをサポートします。これには目に見えるパフォーマンス上の利点はありませんが、クエリを動的に生成する必要があるかどうかを知るのに役立ちます。

df1.query('col.str.contains("foo")', engine='python')

      col
0     foo
1  foobar

メソッドqueryとそのevalファミリの詳細については、pd.eval()を使用したパンダでの動的式評価をご覧ください


推奨される使用の優先順位

  1. (最初)str.contains、その単純さとNaNと混合データの扱いやすさのため
  2. パフォーマンスのために内包表記を一覧表示する(特に、データが純粋に文字列の場合)
  3. np.vectorize
  4. (最終) df.query

2つ以上の列で文字列を検索するときに使用する正しい方法で編集できますか?基本的には:any(needle in haystack for needling in ['foo', 'bar'] and haystack in (df['col'], df['col2']))および変形私はすべてのチョークを(それは文句を言うてみましたany()。そして当然のように...しかし、このようなクエリを実行する方法についてのドキュメントが穏やか不明である
デニスBernardy・デ・

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

@ cs95はdfをパンダに+の後に空白文字を含むサブストリングを持つ行を抽出することはすぐに答えましたが、あなたはそれを見てすることがあります。
ankii

@ankiiiiiii正規表現のメタ文字について述べた私の回答の一部を逃したように見えます。「正規表現のメタ文字として解釈できる文字がある場合は、用語をエスケープするのが賢明です」。
cs95

1
この場合の@ 00schneider rは、未加工の文字列リテラルを示すために使用されます。これらにより、正規表現文字列を簡単に記述できます。stackoverflow.com/q/2081640
cs95

53

誰かが関連する問題を実行する方法を疑問に思っている場合:「部分的な文字列で列を選択してください」

使用する:

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

そして、部分的な文字列マッチングによって行を選択するにはaxis=0、フィルターに渡します:

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

6
これは、に蒸留することができる:df.loc[:, df.columns.str.contains('a')]
elPastor

18
さらに蒸留することができますdf.filter(like='a')
Ted Petrou 2017年

これは独自の質問と回答でなければなりません。すでに50人が検索しています...
PV8

1
@ PV8の質問は既に存在します:stackoverflow.com/questions/31551412/…。しかし、グーグルで「部分文字列によるパンダ選択列」を検索すると、このスレッドが最初に表示されます
フィリップシュワルツ

28

クイックノート:インデックスに含まれる部分的な文字列に基づいて選択を行う場合は、次のことを試してください。

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

5
あなたはただdf [df.index.to_series()。str.contains( 'LLChit')]
ユーリーベイダ

21

あなたが次のものを持っているとしましょうDataFrame

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

いつでもinラムダ式で演算子を使用してフィルターを作成できます。

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

ここでの秘訣は、のaxis=1オプションを使用して、apply要素を列ごとではなく行ごとにラムダ関数に渡すことです。


上記を変更して、x ['a']がx ['b']の先頭にのみ存在するようにするにはどうすればよいですか?
ComplexData 2016年

1
ここでは、パフォーマンスとメモリの観点から、applyは悪い考えです。この回答を参照してください。
cs95

8

これが、文字列の部分一致に使用した結果です。誰かがこれを行うより効率的な方法を持っている場合は、私に知らせてください。

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
その後、正規表現= re.compile(正規表現)とregex.search(レコード)の場合:あなたコンパイル正規表現ループの前にあれば2倍速く3倍にする必要があります
MarkokraM

1
@MarkokraM docs.python.org/3.6/library/re.html#re.compileは、最新の正規表現がキャッシュされるため、自分でコンパイルする必要がないと述べています。
Teepeemm

DataFrameを反復処理するためにiteritemsを使用しないでください。パンダビリティとパフォーマンスの点で最後にランク付け
cs95

5

特殊文字を含む文字列では、containsを使用してもうまくいきませんでした。でもうまくいきました。

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

2

これの前に、質問された機能を実現する答えがあります。とにかく、最も一般的な方法を示したいと思います。

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

このようにして、書いた方法に関係なく、探している列を取得しましょう。

(当然のことながら、各ケースに適切な正規表現を記述する必要があります)


1
これにより、列ヘッダーがフィルタリングされます。それは一般的ではなく、間違っています。
cs95

@MicheldeRuiterはまだ正しくありません。代わりにインデックスラベルでフィルタリングします。
cs95

質問には答えません。しかし、私は何かを学びました。:)
Michel de Ruiter

2

Pandasデータフレームのすべての列ではなく、それらのサブセットだけでなく、テキストを検索したい場合があります。この場合、次のコードが役立ちます。

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

警告。この方法は便利ですが、比較的低速です。


2

pandasデータフレーム列の文字列を大文字と小文字を区別せずに検索する必要がある場合

df[df['A'].str.contains("hello", case=False)]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.