webentwicklung-frage-antwort-db.com.de

Wählen Sie eine Teilzeichenfolge aus einem pandas -Datenrahmen aus

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?

316
euforia

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.

615
Garrett

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]
152
sharon

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)  
44
Philipp Schwarz

Wie wähle ich eine Teilzeichenfolge aus einem pandas -Datenrahmen aus?

Dieser Beitrag ist für Leser gedacht, die möchten

  • suche nach einem Teilstring in einer String-Spalte (der einfachste Fall)
  • suche nach mehreren Teilzeichenfolgen (ähnlich wie isin )
  • einem ganzen Wort aus dem Text entsprechen (z. B. sollte "blau" mit "der Himmel ist blau", aber nicht mit "bluejay" übereinstimmen)
  • stimmen mit mehreren ganzen Wörtern überein
  • Verstehen Sie den Grund für "ValueError: Kann nicht mit Vektoren indizieren, die NA/NaN-Werte enthalten"

... 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.)


Grundlegende Teilstringsuche

_# 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 ValueErrors
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
_

Mehrere Teilstrings suchen

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

Übereinstimmende ganze Wörter

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
_

Suche nach mehreren ganzen Wörtern

Ä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'
_

Eine großartige Alternative: Verwenden Sie List Comprehensions !

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
_

Weitere Optionen für die teilweise Zeichenfolgenübereinstimmung: 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 () .


Empfohlene Verwendungsrangfolge

  1. (First) _str.contains_, für seine Einfachheit und einfache Handhabung von NaNs und gemischten Daten
  2. Listen Sie das Verständnis für seine Leistung auf (insbesondere, wenn es sich bei Ihren Daten um reine Zeichenfolgen handelt).
  3. _np.vectorize_
  4. (Last) _df.query_
33
cs95

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")]
27
Christian

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.

20
Mike

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

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.)

0
xpeiro