Ответ 1
Основываясь на проблеме github # 620, похоже, что вы скоро сможете сделать следующее:
df[df['A'].str.contains("hello")]
Обновление: векторизованные методы строк (например, Series.str) доступны в pandas 0.8.1 и выше.
У меня есть DataFrame
с 4 столбцами, из которых 2 содержат строковые значения. Мне было интересно, есть ли способ выбора строк на основе частичного совпадения строк с конкретным столбцом?
Другими словами, функция или лямбда-функция, которая сделает что-то вроде
re.search(pattern, cell_in_question)
возвращает логическое значение. Я знаком с синтаксисом df[df['A'] == "hello world"]
, но не может найти способ сделать то же самое с частичным совпадением строк say 'hello'
.
Кто-нибудь сможет указать мне в правильном направлении?
Основываясь на проблеме github # 620, похоже, что вы скоро сможете сделать следующее:
df[df['A'].str.contains("hello")]
Обновление: векторизованные методы строк (например, Series.str) доступны в pandas 0.8.1 и выше.
Я использую 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]
Если кто-то задается вопросом, как выполнить связанную проблему: "Выберите столбец частичной строкой"
Использование:
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)
Быстрое примечание: если вы хотите сделать выбор на основе частичной строки, содержащейся в индексе, попробуйте следующее:
df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]
Скажите, что у вас есть следующие 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
для передачи элементов в функцию лямбда по строке, в отличие от столбца по столбцу.
Как выбрать частичную строку в панде DataFrame?
Этот пост предназначен для читателей, которые хотят
isin
)... и хотел бы узнать больше о том, какие методы предпочтительнее других.
(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().
str.contains
, за его простоту и удобство обработки NaN и смешанных данныхnp.vectorize
df.query
Вот что я сделал для частичных совпадений строк. Если у кого-то есть более эффективный способ сделать это, пожалуйста, дайте мне знать.
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
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
Почему бы вам не попробовать df[df["COLUMN_ID"].str.contains("SUBSTRING")]
?
Просто замените COLUMN_ID
на строку, относящуюся к столбцу, в котором вы хотите найти свою подстроку, и замените SUBSTRING
текстом, который вы хотите найти ("Hello" в вашем случае).
Перед этим есть ответы, которые выполняют заданную функцию, в любом случае я хотел бы показать наиболее общий способ:
df.filter(regex=".*STRING_YOU_LOOK_FOR.*")
Таким образом, вы можете получить столбец, который вы ищете, независимо от того, как написано.
(Очевидно, вы должны написать правильное выражение регулярного выражения для каждого случая)
Как бы вы отфильтровали "свободу", кроме как с помощью большего количества критериев, таких как "наследие", "ulic" и т.д.?
df_Fixed[~df_Fixed["Busler Group"].map(lambda x: x.startswith('Liberty'))]