Ich habe ein DataFrame
mit 4 Spalten, von denen 2 Zeichenfolgenwerte enthalten. Ich habe mich gefragt, ob es eine Möglichkeit gibt, Zeilen basierend auf einer partiellen Zeichenfolgenübereinstimmung mit einer bestimmten Spalte auszuwählen.
Mit anderen Worten, eine Funktion oder Lambda-Funktion, die so etwas tun würde
re.search(pattern, cell_in_question)
rückgabe eines Booleschen. Ich bin mit der Syntax von df[df['A'] == "hello world"]
vertraut, kann aber anscheinend keine Möglichkeit finden, dies mit einer partiellen Zeichenfolgenübereinstimmung zu tun, z. B. 'hello'
.
Wäre jemand in der Lage, mich in die richtige Richtung zu weisen?
Basierend auf dem Github-Problem # 62 werden Sie in Kürze Folgendes tun können:
df[df['A'].str.contains("hello")]
Update: vektorisierte Zeichenfolgenmethoden (d. H. Series.str) sind in pandas 0.8.1 und höher verfügbar.
Ich verwende pandas 0.14.1 auf Macs in ipython notebook. Ich habe die vorgeschlagene Zeile oben ausprobiert:
df[df['A'].str.contains("Hello|Britain")]
und habe einen Fehler bekommen:
"cannot index with vector containing NA / NaN values"
aber es funktionierte perfekt, als eine "== True" Bedingung hinzugefügt wurde, wie folgt:
df[df['A'].str.contains("Hello|Britain")==True]
Wenn sich jemand fragt, wie ein verwandtes Problem ausgeführt werden soll: "Spalte nach Teilzeichenfolge auswählen"
Verwenden:
df.filter(like='hello') # select columns which contain the Word hello
Übergeben Sie axis=0
, um Zeilen durch partielle Zeichenfolgenübereinstimmung auszuwählen und zu filtern:
# selects rows which contain the Word hello in their index label
df.filter(like='hello', axis=0)
Wie wähle ich eine Teilzeichenfolge aus einem pandas -Datenrahmen aus?
Dieser Beitrag ist für Leser gedacht, die möchten
isin
)... und möchten mehr darüber erfahren, welche Methoden anderen vorzuziehen sind.
(P .: Ich habe viele Fragen zu ähnlichen Themen gesehen, ich dachte, es wäre gut, das hier zu lassen.)
_# setup
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']})
df1
col
0 foo
1 foobar
2 bar
3 baz
_
str.contains
kann verwendet werden, um entweder Teilstringsuchen oder eine auf Regex basierende Suche durchzuführen. Die Suche basiert standardmäßig auf Regex, es sei denn, Sie deaktivieren sie ausdrücklich.
Hier ist ein Beispiel für eine auf Regex basierende Suche:
_# find rows in `df1` which contain "foo" followed by something
df1[df1['col'].str.contains(r'foo(?!$)')]
col
1 foobar
_
Manchmal ist keine Regex-Suche erforderlich. Geben Sie daher _regex=False
_ an, um die Suche zu deaktivieren.
_#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
_
In Bezug auf die Leistung ist die Regex-Suche langsamer als die Teilstringsuche:
_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)
_
Vermeiden Sie die Verwendung einer auf Regex basierenden Suche, wenn Sie diese nicht benötigen.
Adressierung von ValueError
s
Manchmal führt das Durchführen einer Teilstringsuche und Filtern des Ergebnisses zu
_ValueError: cannot index with vector containing NA / NaN values
_
Dies ist normalerweise auf gemischte Daten oder NaNs in Ihrer Objektspalte zurückzuführen.
_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)
_
Auf alles, was kein String ist, können keine String-Methoden angewendet werden. Das Ergebnis ist also NaN (natürlich). Geben Sie in diesem Fall _na=False
_ an, um Nicht-String-Daten zu ignorieren.
_s.str.contains('foo|bar', na=False)
0 True
1 True
2 False
3 True
4 False
5 False
dtype: bool
_
Dies wird am einfachsten durch eine Regex-Suche mit der Pipe Regex OR erreicht.
_# 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
_
Sie können auch eine Liste von Begriffen erstellen und diese dann verbinden:
_terms = ['foo', 'baz']
df4[df4['col'].str.contains('|'.join(terms))]
col
0 foo abc
1 foobar xyz
3 baz 45
_
Manchmal ist es ratsam, Ihre Begriffe zu umgehen, wenn sie Zeichen enthalten, die als Regex-Metazeichen interpretiert werden können. Wenn Ihre Begriffe eines der folgenden Zeichen enthalten ...
_. ^ $ * + ? { } [ ] \ | ( )
_
Dann müssen Sie re.escape
verwenden, um zu entkommen :
_import re
df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))]
col
0 foo abc
1 foobar xyz
3 baz 45
_
_re.escape
_ hat den Effekt, dass die Sonderzeichen ausgeblendet werden, damit sie buchstäblich behandelt werden.
_re.escape(r'.foo^')
# '\\.foo\\^'
_
Standardmäßig sucht die Teilstringsuche nach dem angegebenen Teilstring/Muster, unabhängig davon, ob es sich um vollständiges Word handelt oder nicht. Um nur vollständige Wörter zu finden, müssen wir hier reguläre Ausdrücke verwenden. Insbesondere müssen in unserem Muster Wortgrenzen (_\b
_) angegeben werden.
Zum Beispiel,
_df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']})
df3
col
0 the sky is blue
1 bluejay by the window
_
Nun überlegen Sie,
_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
_
Ähnlich wie oben, außer dass wir dem verbundenen Muster eine Wortgrenze (_\b
_) hinzufügen.
_p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms)))
df4[df4['col'].str.contains(p)]
col
0 foo abc
3 baz 45
_
Wo p
so aussieht,
_p
# '\\b(?:foo|baz)\\b'
_
Weil du es kannst! nd du solltest! Sie sind normalerweise etwas schneller als String-Methoden, da String-Methoden schwer zu vektorisieren sind und normalerweise Loop-Implementierungen haben.
Anstatt von,
_df1[df1['col'].str.contains('foo', regex=False)]
_
Verwenden Sie den Operator in
in einem Listenkomponenten.
_df1[['foo' in x for x in df1['col']]]
col
0 foo abc
1 foobar
_
Anstatt von,
_regex_pattern = r'foo(?!$)'
df1[df1['col'].str.contains(regex_pattern)]
_
Verwenden Sie re.compile
(zum Zwischenspeichern Ihres regulären Ausdrucks) + Pattern.search
in einer Liste comp,
_p = re.compile(regex_pattern, flags=re.IGNORECASE)
df1[[bool(p.search(x)) for x in df1['col']]]
col
1 foobar
_
Wenn "col" NaNs hat, dann statt
_df1[df1['col'].str.contains(regex_pattern, na=False)]
_
Verwenden,
_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.find
, np.vectorize
, DataFrame.query
.Zusätzlich zu _str.contains
_ und Listenverständnissen können Sie auch die folgenden Alternativen verwenden.
np.char.find
Unterstützt nur Teilstringsuchen (read: no regex).
_df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1]
col
0 foo abc
1 foobar xyz
_
np.vectorize
Dies ist ein Wrapper um eine Schleife, jedoch mit geringerem Overhead als die meisten pandas str
-Methoden.
_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-Lösungen möglich:
_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
Unterstützt String-Methoden über die python -Engine. Dies bietet keine sichtbaren Leistungsvorteile, ist jedoch hilfreich, wenn Sie wissen möchten, ob Sie Ihre Abfragen dynamisch generieren müssen.
_df1.query('col.str.contains("foo")', engine='python')
col
0 foo
1 foobar
_
Weitere Informationen zu den Methodenfamilien query
und eval
finden Sie unter Dynamic Expression Evaluation in pandas using pd.eval () .
str.contains
_, für seine Einfachheit und einfache Handhabung von NaNs und gemischten Datennp.vectorize
_df.query
_Kurzer Hinweis: Wenn Sie eine Auswahl basierend auf einer im Index enthaltenen Teilzeichenfolge vornehmen möchten, versuchen Sie Folgendes:
df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]
Angenommen, Sie haben das folgende DataFrame
:
>>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b'])
>>> df
a b
0 hello hello world
1 abcd defg
Sie können jederzeit den Operator in
in einem Lambda-Ausdruck verwenden, um Ihren Filter zu erstellen.
>>> df.apply(lambda x: x['a'] in x['b'], axis=1)
0 True
1 False
dtype: bool
Der Trick dabei ist, die Option axis=1
in apply
zu verwenden, um Elemente zeilenweise und nicht spaltenweise an die Lambda-Funktion zu übergeben.
Hier ist, was ich für teilweise String-Übereinstimmungen getan habe. Wenn jemand eine effizientere Möglichkeit hat, lassen Sie es mich bitte wissen.
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
Davor gibt es Antworten, die die gewünschte Funktion erfüllen, trotzdem möchte ich den allgemeinsten Weg zeigen:
df.filter(regex=".*STRING_YOU_LOOK_FOR.*")
Auf diese Weise erhalten Sie die Spalte, nach der Sie suchen, wie auch immer geschrieben wird.
(Natürlich müssen Sie für jeden Fall den richtigen regulären Ausdruck schreiben.)