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.contains
alt 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=False
devre 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 ValueError
s
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=False
dize 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.escape
iç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 \b
birleş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 p
benziyor,
p
# '\\b(?:foo|baz)\\b'
Çü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)]
in
Operatö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.search
bir 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
Anlamalara ek olarak str.contains
ve 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 str
yö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 query
ve eval
yö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
- (İlk)
str.contains
, basitliği ve NaN'leri ve karışık verileri kolayca işlemesi için
- Performansı için kavrayışları listeleyin (özellikle verileriniz tamamen dizgiyse)
np.vectorize
- (Son)
df.query