Выбор по частичной строке в панде DataFrame

У меня есть DataFrame с 4 столбцами, из которых 2 содержат строковые значения. Мне было интересно, есть ли способ выбора строк на основе частичного совпадения строк с конкретным столбцом?

Другими словами, функция или лямбда-функция, которая сделает что-то вроде

re.search(pattern, cell_in_question) 

возвращает логическое значение. Я знаком с синтаксисом df[df['A'] == "hello world"], но не может найти способ сделать то же самое с частичным совпадением строк say 'hello'.

Кто-нибудь сможет указать мне в правильном направлении?

Ответ 2

Я использую pandas 0.14.1 на macos в ноутбуке ipython. Я попробовал предложенную строку выше:

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

и получил сообщение об ошибке:

"cannot index with vector containing NA / NaN values"

но он отлично работал, когда было добавлено условие "== Истина", например:

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

Ответ 3

Если кто-то задается вопросом, как выполнить связанную проблему: "Выберите столбец частичной строкой"

Использование:

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

И для выбора строк путем частичного совпадения строк, пропустите axis=0 для фильтрации:

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

Ответ 4

Быстрое примечание: если вы хотите сделать выбор на основе частичной строки, содержащейся в индексе, попробуйте следующее:

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

Ответ 5

Скажите, что у вас есть следующие DataFrame:

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

Вы всегда можете использовать оператор in в выражении лямбда для создания вашего фильтра.

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

Трюк здесь заключается в использовании опции axis=1 в apply для передачи элементов в функцию лямбда по строке, в отличие от столбца по столбцу.

Ответ 6

Как выбрать частичную строку в панде DataFrame?

Этот пост предназначен для читателей, которые хотят

  • поиск подстроки в столбце строки (самый простой случай)
  • поиск нескольких подстрок (аналогично isin)
  • соответствует целому слову из текста (например, "синий" должен соответствовать "голубое небо", а не "синий")
  • сопоставить несколько целых слов
  • Понять причину "ValueError: невозможно индексировать с вектором, содержащим значения NA/NaN"

... и хотел бы узнать больше о том, какие методы предпочтительнее других.

(PS: я видел много вопросов на подобные темы, я думал, что было бы хорошо оставить это здесь.)


Основной поиск подстроки

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

      col
0     foo
1  foobar
2     bar
3     baz

str.contains можно использовать для поиска по подстроке или по регулярному выражению. По умолчанию поиск выполняется на основе регулярных выражений, если вы явно не отключили его.

Вот пример поиска на основе регулярных выражений,

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

      col
1  foobar

Иногда поиск по регулярному выражению не требуется, поэтому укажите regex=False чтобы отключить его.

#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

По производительности, поиск по регулярным выражениям медленнее, чем поиск по подстроке:

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)

Избегайте использования поиска на основе регулярных выражений, если он вам не нужен.

Обращение к ValueError s
Иногда выполнение поиска и фильтрации подстроки по результату приводит к

ValueError: cannot index with vector containing NA / NaN values

Обычно это происходит из-за смешанных данных или NaN в столбце вашего объекта,

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)

На все, что не является строкой, не могут быть применены строковые методы, поэтому результатом будет NaN (естественно). В этом случае укажите na=False чтобы игнорировать нестроковые данные,

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

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

Поиск нескольких подстрок

Этого легче всего достичь с помощью поиска регулярных выражений с использованием регулярного выражения OR.

# 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

Вы также можете создать список терминов, а затем присоединиться к ним:

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

          col
0     foo abc
1  foobar xyz
3      baz 45

Иногда разумно избегать ваших терминов, если в них есть символы, которые можно интерпретировать как метасимволы регулярных выражений. Если ваши термины содержат любой из следующих символов...

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

Затем вам нужно использовать re.escape чтобы избежать их:

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

          col
0     foo abc
1  foobar xyz
3      baz 45

re.escape имеет эффект экранирования специальных символов, поэтому они трактуются буквально.

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

Совпадение всего слова

По умолчанию поиск подстроки выполняет поиск указанной подстроки/шаблона независимо от того, является ли это полным словом или нет. Чтобы сопоставлять только полные слова, нам нужно будет использовать здесь регулярные выражения - в частности, наш шаблон должен будет указать границы слов (\b).

Например,

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

                     col
0        the sky is blue
1  bluejay by the window

Теперь рассмотрим,

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

                     col
0        the sky is blue
1  bluejay by the window

в/с

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

               col
0  the sky is blue

Множественный поиск по всему слову

Как и выше, за исключением того, что мы добавляем границу слова (\b) к объединенному шаблону.

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

       col
0  foo abc
3   baz 45

Где p выглядит так,

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

Отличная альтернатива: используйте списки !

Потому что ты можешь! И ты должен! Они обычно немного быстрее строковых методов, потому что строковые методы трудно векторизовать и обычно имеют зацикленные реализации.

Вместо,

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

Используйте оператор in внутри списка comp,

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

       col
0  foo abc
1   foobar

Вместо,

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

Используйте re.compile (для кэширования своего регулярного выражения) + Pattern.search внутри списка comp,

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

      col
1  foobar

Если "col" имеет NaNs, то вместо

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

Использование,

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.

В дополнение к str.contains и списку представлений вы также можете использовать следующие альтернативы.

np.char.find
Поддерживает только поиск по подстроке (читай: без регулярных выражений).

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

          col
0     foo abc
1  foobar xyz

np.vectorize
Это обертка вокруг цикла, но с меньшими издержками, чем у большинства методов pandas str.

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:

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. Это не дает видимых преимуществ в производительности, но, тем не менее, полезно знать, нужно ли вам динамически генерировать ваши запросы.

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

      col
0     foo
1  foobar

Более подробную информацию о query и семействе методов eval можно найти в разделе "Оценка динамических выражений" в pandas, используя pd.eval().


Рекомендуемое приоритетность использования

  1. (Первый) str.contains, за его простоту и удобство обработки NaN и смешанных данных
  2. Список представлений, для его производительности (особенно если ваши данные являются чисто строками)
  3. np.vectorize
  4. (Последнее) df.query

Ответ 7

Вот что я сделал для частичных совпадений строк. Если у кого-то есть более эффективный способ сделать это, пожалуйста, дайте мне знать.

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

Ответ 8

import pandas as pd
k=pd.DataFrame(['hello','doubt','hero','help'])
k.columns=['some_thing']
t=k[k['some_thing'].str.contains("hel")]
d=k.replace(t,'CS')

:ВЫХОД:

k
Out[95]: 
   some_thing
0  hello
1  doubt
2  hero
3   help

t
Out[99]: 
   some_thing
0  hello
3   help

d
Out[96]: 
    some_thing
0     CS
1  doubt
2  hero
3     CS

Ответ 9

Почему бы вам не попробовать df[df["COLUMN_ID"].str.contains("SUBSTRING")]?

Просто замените COLUMN_ID на строку, относящуюся к столбцу, в котором вы хотите найти свою подстроку, и замените SUBSTRING текстом, который вы хотите найти ("Hello" в вашем случае).

Ответ 10

Перед этим есть ответы, которые выполняют заданную функцию, в любом случае я хотел бы показать наиболее общий способ:

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

Таким образом, вы можете получить столбец, который вы ищете, независимо от того, как написано.

(Очевидно, вы должны написать правильное выражение регулярного выражения для каждого случая)

Ответ 11

Как бы вы отфильтровали "свободу", кроме как с помощью большего количества критериев, таких как "наследие", "ulic" и т.д.?

   df_Fixed[~df_Fixed["Busler Group"].map(lambda x: x.startswith('Liberty'))]