web-dev-qa-db-ja.com

パンダから部分文字列で選択する

私はDataFrameを持っています。そのうちの2つは文字列値を含みます。特定の列に対する部分文字列の一致に基づいて行を選択する方法があるかどうか私は思っていませんでしたか。

言い換えれば、関数またはラムダ関数は次のようになります。

re.search(pattern, cell_in_question) 

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

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

276
euforia

Github issue #620 に基づくと、すぐに次のことができるようになります。

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

更新: ベクトル化された文字列メソッド(すなわちSeries.str) はパンダ0.8.1以降で利用可能です。

557
Garrett

私はipythonのノートブックのmacosでパンダ0.14.1を使っています。私は上記の提案された行を試してみました:

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

そしてエラーが発生しました:

"cannot index with vector containing NA / NaN values"

しかし、次のように "== True"条件が追加されていれば完璧に機能します。

df[df['A'].str.contains("Hello|Britain")==True]
140
sharon

誰かが関連する問題をどのように実行するか疑問に思うなら: "部分的な文字列で列を選択する"

つかいます:

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

そして部分的な文字列マッチングで行を選択するには、axis=0をfilterに渡します:

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

クイックノート:インデックスに含まれる部分文字列に基づいて選択したい場合は、以下を試してください。

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

次のような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

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

19
Mike

どのように私はパンダDataFrameから部分的なストリングによって選択するのですか?

この記事は、読みたい読者を対象としています。

  • 文字列の列で部分文字列を検索する(最も単純な場合)
  • 複数の部分文字列を検索する( isin に似ています)
  • テキストから単語全体を一致させる(たとえば、「青」は「空は青」に一致するが「bluejay」には一致しない)
  • 複数の単語全体に一致

...そして、どの方法が他の方法よりも優先されるべきかについてもっと知りたいのです。

(P:。私は同様のトピックについて多くの質問を見ました、私はこれをここに残すのが良いだろうと思いました。)


基本サブストリング検索

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

      col
0     foo
1  foobar
2     bar
3     baz

"foo"を含むすべての行を選択するには、 str.contains を使用します。

df1[df1['col'].str.contains('foo')]

      col
0     foo
1  foobar

これは純粋な部分文字列検索なので、正規表現ベースのマッチングを無効にしても問題ありません。

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

      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)

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


文字列の先頭または末尾に固定されている部分的なサブストリング検索は、それぞれ str.startswith または str.endswith を使用して実行できます。

さらに、先頭に固定された正規表現ベースの検索には、 str.match を使用します。

正規表現ベースの検索
ほとんどのstrメソッドは正規表現をサポートしています。たとえば、df1の中に "foo"の後に他のものが続く行を見つけるには、次のようにします。

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

      col
1  foobar

複数部分文字列検索

これはregex OR pipeを使った正規表現検索によって最も簡単に実現できます。

# 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.escapeescapeに使う必要があります。

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\\^'

単語全体の一致

既定では、部分文字列検索は、完全なWordであるかどうかにかかわらず、指定された部分文字列/パターンを検索します。完全な単語のみを一致させるには、ここで正規表現を使用する必要があります。特に、パターンではWordの境界を指定する必要があります(\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

複数単語検索

上と同じですが、結合パターンにWordの境界(\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'

素晴らしい代替案:使用 List Comprehensions

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

の代わりに、

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

リストcompの中で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 list compの中で

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
部分文字列検索(read:no regex)のみをサポートします。

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、その単純さのために
  2. その性能のためのリスト内包表記
  3. np.vectorize
  4. (最後)df.query
14
cs95

これが部分的な文字列の一致のためにやったことです。誰かがこれを行うより効率的な方法を持っているなら私に知らせてください。

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

とにかく私が最も一般的な方法を示したいと思います、これの前に尋ねられた機能を達成する答えがあります:

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

このようにして、どのように書かれたものであれ、あなたが探すコラムを手に入れましょう。

(もちろん、それぞれの場合に適切な正規表現を書く必要があります)

0
xpeiro