Panda DataFrame'den kısmi dize ile seçim


448

Ben DataFrame4 sütun 2 olan dize değerleri içeren var. Belirli bir sütuna karşı kısmi bir dize maç dayalı satır seçmek için bir yol olup olmadığını merak ediyordum?

Başka bir deyişle, böyle bir şey yapacak bir işlev veya lambda işlevi

re.search(pattern, cell_in_question) 

bir boole döndürüyor. Ben sözdizimi aşina df[df['A'] == "hello world"]ama kısmi bir dize maç demek ile aynı yapmak için bir yol bulamıyorum 'hello'.

Birisi beni doğru yönde gösterebilir mi?

Yanıtlar:


787

Github sorunu # 620'ye dayanarak, yakında aşağıdakileri yapabileceğiniz anlaşılıyor:

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

Güncelleme: vectorized string yöntemleri (yani Series.str) pandalar 0.8.1 ve üstü için mevcuttur.


1
"VEYA" koşulu ile bulmak istersem "Merhaba" ve "İngiltere" yi nasıl bulabiliriz.
LonelySoul

56
Str. * Yöntemleri girdi düzenini normal bir ifade olarak kullandığından, kullanabilirsinizdf[df['A'].str.contains("Hello|Britain")]
Garrett

7
API.str.contains kullanmak için dönüştürmek mümkün mü ? .query()
zyxue


3
df[df['value'].astype(str).str.contains('1234.+')]dize türü olmayan sütunları filtrelemek için.
François Leblanc

213

Yukarıda önerilen çözümü denedim:

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

ve bir hata aldım:

ValueError: NA / NaN değerleri içeren dizi ile maskeleyemiyor

NA değerlerini şu şekilde dönüştürebilirsiniz False:

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

54
Ya da şunu yapabilirsiniz: df [df ['A']. Str.contains ("Merhaba | İngiltere", na = Yanlış)]
joshlk

2
df[df['A'].astype(str).str.contains("Hello|Britain")]iyi çalıştı
Nagabhushan SN

108

Panda DataFrame'den kısmi dize ile nasıl seçerim?

Bu yayın,

  • dize sütununda bir alt dize arama (en basit durum)
  • birden çok alt dizeyi arama (benzer isin)
  • metinden tüm bir kelimeyi eşleştirin (ör. "mavi", "gökyüzü mavidir" ancak "bluejay" ile eşleşmemelidir)
  • birden çok tam kelimeyi eşleştir
  • "ValueError: NA / NaN değerleri içeren vektörle dizin oluşturulamıyor" nun nedenini anlayın

... ve diğerlerine göre hangi yöntemlerin tercih edilmesi gerektiği hakkında daha fazla bilgi edinmek istiyorum.

(Not: Benzer konularda birçok soru gördüm, bunu burada bırakmanın iyi olacağını düşündüm.)


Temel Alt Dize Arama

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

      col
0     foo
1  foobar
2     bar
3     baz

str.containsalt dize aramaları veya normal ifade tabanlı arama yapmak için kullanılabilir. Açık bir şekilde devre dışı bırakmadığınız sürece arama varsayılan olarak normal ifade tabanlı olur.

Aşağıda, normal ifade tabanlı arama örneği,

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

      col
1  foobar

Bazen normal ifade araması gerekli değildir, bu nedenle regex=Falsedevre dışı bırakmayı belirtin .

#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

Performans açısından, normal ifade araması alt dize aramasından daha yavaştır:

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)

İhtiyacınız yoksa normal ifade tabanlı aramayı kullanmaktan kaçının.

Adresleme ValueErrors
Bazen, bir alt dize araması yapmak ve sonuca filtre uygulamak

ValueError: cannot index with vector containing NA / NaN values

Bunun nedeni genellikle nesne sütununuzdaki karışık veriler veya NaN'lerdir,

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)

Dize olmayan hiçbir şeye dize yöntemi uygulanamaz, bu nedenle sonuç NaN'dir (doğal olarak). Bu durumda, na=Falsedize dışı verileri yoksaymayı belirtin ,

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

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

Çoklu Alt Arama

Bu en kolay şekilde regex OR borusu kullanılarak bir regex aramasıyla elde edilir.

# 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

Ayrıca bir terimler listesi oluşturabilir ve ardından bunlara katılabilirsiniz:

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

          col
0     foo abc
1  foobar xyz
3      baz 45

Bazen, normal ifade metakarakterleri olarak yorumlanabilecek karakterleri olması durumunda terimlerinizden kaçmak akıllıca olur . Terimleriniz aşağıdaki karakterlerden herhangi birini içeriyorsa ...

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

Ardından, kullanmak gerekir re.escapeiçin kaçmak onları:

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

          col
0     foo abc
1  foobar xyz
3      baz 45

re.escape özel karakterlerden kaçma etkisi vardır, böylece tam anlamıyla ele alınırlar.

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

Tüm Kelime (ler) ile Eşleştirme

Varsayılan olarak, alt dize araması, tam sözcük olup olmamasına bakılmaksızın belirtilen alt dizeyi / deseni arar. Yalnızca tam kelimeleri eşleştirmek için, burada düzenli ifadeleri kullanmamız gerekecek - özellikle, modelimizde kelime sınırları ( \b) belirtilmesi gerekecek .

Örneğin,

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

                     col
0        the sky is blue
1  bluejay by the window

Şimdi düşünün,

df3[df3['col'].str.contains('blue')]

                     col
0        the sky is blue
1  bluejay by the window

vs

df3[df3['col'].str.contains(r'\bblue\b')]

               col
0  the sky is blue

Birden Çok Tam Kelime Arama

Yukarıdakine benzer, ancak \bbirleştirilmiş desene bir kelime sınırı ( ) ekleriz .

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

       col
0  foo abc
3   baz 45

Nereye pbenziyor,

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

Harika Bir Alternatif: Liste Anlamalarını Kullanın !

Çünkü yapabilirsin! Ve yapmalısın! Genellikle dize yöntemlerinden biraz daha hızlıdır, çünkü dize yöntemlerini vektörlemek zordur ve genellikle döngüsel uygulamalara sahiptir.

Onun yerine,

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

inOperatör bir liste comp içinde kullanın ,

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

       col
0  foo abc
1   foobar

Onun yerine,

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

re.compile(Regex'inizi önbelleğe almak için) + tuşlarını Pattern.searchbir liste kompozisyonunun içinde kullanın ,

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

      col
1  foobar

"Col" nin NaN'si varsa, bunun yerine

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

Kullanım,

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

Kısmi dize Eşleştirme için Daha Fazla Seçenek: np.char.find, np.vectorize, DataFrame.query.

Anlamalara ek olarak str.containsve listelemenin yanı sıra aşağıdaki alternatifleri de kullanabilirsiniz.

np.char.find
Yalnızca alt dize aramalarını (okuma: normal ifade yok) destekler.

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

          col
0     foo abc
1  foobar xyz

np.vectorize
Bu, bir döngü etrafındaki bir sarıcıdır, ancak çoğu panda stryönteminden daha az ek yüke sahiptir .

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 çözümleri mümkündür:

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 motoru üzerinden dize yöntemlerini destekler. Bu, görünür bir performans avantajı sunmaz, ancak yine de sorgularınızı dinamik olarak oluşturmanız gerekip gerekmediğini bilmek yararlıdır.

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

      col
0     foo
1  foobar

Yöntemler queryve evalyöntemler hakkında daha fazla bilgi pd.eval () kullanılarak pandalarda Dinamik İfade Değerlendirmesi bölümünde bulunabilir .


Önerilen Kullanım Önceliği

  1. (İlk) str.contains, basitliği ve NaN'leri ve karışık verileri kolayca işlemesi için
  2. Performansı için kavrayışları listeleyin (özellikle verileriniz tamamen dizgiyse)
  3. np.vectorize
  4. (Son) df.query

İki veya daha fazla sütunda bir dize ararken kullanılacak doğru yöntemle düzenleyebilir misiniz? Temel olarak: any(needle in haystack for needling in ['foo', 'bar'] and haystack in (df['col'], df['col2']))ve varyasyonları tüm boğmayı denedim (şikayet ediyor any()ve haklı olarak öyle ... Ama doktor böyle bir sorgunun nasıl yapılacağı konusunda mutlu değil.
Denis de Bernardy

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

@ cs95 Pandalar df içinde + işaretinden sonra boşluk içeren alt dize ile satırları çıkarma Yakında yanıtlandı, ancak bir göz atmak isteyebilirsiniz.
ankii

@ankiiiiiii Görünüşe göre regex meta karakterlerinden bahsettiğim cevabımın bir kısmını kaçırdınız: "Bazen regex meta karakterleri olarak yorumlanabilecek karakterleri olması durumunda terimlerinizden kaçmak akıllıca olur".
cs95

1
@ 00schneider r, bu durumda ham dize değişmezini belirtmek için kullanılır. Bunlar düzenli ifade dizeleri yazmayı kolaylaştırır. stackoverflow.com/q/2081640
cs95

53

Birisi ilgili bir sorunun nasıl gerçekleştirileceğini merak ederse: "Sütunu kısmi dizeyle seçin"

kullanın:

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

Kısmi dize eşleşmesine göre satırları seçmek axis=0için filtreye geçin:

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

6
Bu aşağıdakiler için damıtılabilir:df.loc[:, df.columns.str.contains('a')]
elPastor

18
daha fazla damıtılabilirdf.filter(like='a')
Ted Petrou

bu kendi sorusu + cevabı olmalı, zaten 50 kişi aradı ...
PV8

1
@ PV8 sorusu zaten var: stackoverflow.com/questions/31551412/… . Ancak google'da "kısmi dizeyle pandaları seçin sütunu" için arama yaptığımda, bu konu ilk olarak görünür
Philipp Schwarz

28

Hızlı not: Dizinde bulunan kısmi bir dizeye dayalı seçim yapmak istiyorsanız, aşağıdakileri deneyin:

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

5
Sadece df [df.index.to_series (). Str.contains ('LLChit')]
Yury Bayda

21

Aşağıdakilere sahip olduğunuzu varsayalım DataFrame:

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

inFiltrenizi oluşturmak için her zaman bir lambda ifadesinde operatörü kullanabilirsiniz .

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

Burada hüner kullanmaktır axis=1seçeneğini applykolonu ile kolona karşı, arka arkaya, lambda fonksiyonu satıra elemanları geçmek için.


Yukarıda x ['a'] 'nın yalnızca x [' b '] başında olduğunu söylemek için nasıl değişiklik yapabilirim?
ComplexData

1
uygulama burada performans ve bellek açısından kötü bir fikirdir. Bu cevaba bakınız .
cs95

8

İşte kısmi dize maçları için ne yaptım. Bunu yapmanın daha etkili bir yolu varsa lütfen bana bildirin.

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
Döngüden önce regex derlerseniz 2x - 3x daha hızlı olmalıdır: regex = re.compile (regex) ve sonra regex.search (kayıt)
MarkokraM

1
@MarkokraM docs.python.org/3.6/library/re.html#re.compile , en son normal ifadelerin sizin için önbelleğe alındığını söylüyor, bu yüzden kendinizi derlemenize gerek yok.
Teepeemm

DataFrame üzerinde yineleme yapmak için iteritems kullanmayın. Pandorabilite ve performans açısından son sırada
CS95

5

İçeren karakterleri kullanmak benim özel karakterlerle benim dize için işe yaramadı. Yine de çalıştı.

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

2

Daha önce sorulan özelliği yerine getiren cevaplar var, yine de en genel yolu göstermek istiyorum:

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

Bu şekilde, yazdığınız yol ne olursa olsun aradığınız sütunu alalım.

(Açıkçası, her vaka için uygun normal ifade ifadesini yazmanız gerekir)


1
Bu sütun başlıklarını filtreler . Genel değil, yanlış.
cs95

@MicheldeRuiter hala yanlış, bunun yerine dizin etiketlerine filtre uygulayacak!
cs95

Soruya cevap vermiyor. Ama bir şey öğrendim. :)
Michel de Ruiter

2

Belki de, Pandas veri çerçevesinin tüm sütunlarında, yalnızca alt kümesinde değil, bazı metinleri aramak isteyebilirsiniz. Bu durumda, aşağıdaki kod yardımcı olacaktır.

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

Uyarı. Bu yöntem uygun olsa da nispeten yavaştır.


2

Panda veri çerçevesi sütununda bir dize için büyük / küçük harfe duyarlı olmayan bir arama yapmanız gerekirse:

df[df['A'].str.contains("hello", case=False)]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.