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'
Çü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
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
- (İ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