Как фильтровать строки в пандах по регулярному выражению

169

Я хотел бы чисто отфильтровать фрейм данных с помощью регулярных выражений в одном из столбцов.

Для надуманного примера:

In [210]: foo = pd.DataFrame({'a' : [1,2,3,4], 'b' : ['hi', 'foo', 'fat', 'cat']})
In [211]: foo
Out[211]: 
   a    b
0  1   hi
1  2  foo
2  3  fat
3  4  cat

Я хочу отфильтровать строки с теми, которые начинаются с fиспользованием регулярных выражений. Сначала идите:

In [213]: foo.b.str.match('f.*')
Out[213]: 
0    []
1    ()
2    ()
3    []

Это не слишком ужасно полезно. Однако это даст мне мой логический индекс:

In [226]: foo.b.str.match('(f.*)').str.len() > 0
Out[226]: 
0    False
1     True
2     True
3    False
Name: b

Поэтому я мог бы сделать свое ограничение:

In [229]: foo[foo.b.str.match('(f.*)').str.len() > 0]
Out[229]: 
   a    b
1  2  foo
2  3  fat

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

justinvf
источник
5
Если вы не привязаны к регулярным выражениям, foo[foo.b.str.startswith("f")]будет работать.
DSM
ИМХО, я думаю foo[foo.b.str.match('(f.*)').str.len() > 0], это довольно хорошее решение! Более настраиваемый и полезный, чем начальный с, потому что он содержит в себе универсальность регулярных выражений.
tumultous_rooster
3
это может быть немного поздно, но в более новых версиях панд проблема исправлена. у foo[foo.b.str.match('f.*')]меня линия работает в пандах 0.24.2.
Бехзад Мехрташ

Ответы:

198

Используйте вместо этого содержит :

In [10]: df.b.str.contains('^f')
Out[10]: 
0    False
1     True
2     True
3    False
Name: b, dtype: bool
waitingkuo
источник
11
Как можно перевернуть логическое значение? Нашел его: stackoverflow.com/questions/15998188/…
dmeu
4
Можно ли получить только те строки, которые имеют True?
Shockwave
2
@shockwave вы должны использовать:df.loc[df.b.str.contains('^f'), :]
Рафа
1
@shockwave Также вы можете просто использоватьdf[df.b.str.contains('^f'), :]
Дэвид Юнг
24

Уже есть функция обработки строк Series.str.startswith(). Ты должен попробовать foo[foo.b.str.startswith('f')].

Результат:

    a   b
1   2   foo
2   3   fat

Я думаю, что вы ожидаете.

В качестве альтернативы вы можете использовать параметр «содержит регулярные выражения». Например:

foo[foo.b.str.contains('oo', regex= True, na=False)]

Результат:

    a   b
1   2   foo

na=False предотвратить ошибки в случае, если есть значения nan, null и т. д.

Эркан Жирин
источник
Я изменил это, и это сработало для меняdf[~df.CITY.str.contains('~.*', regex= True, na=False)]
Пэтти Юла
Спасибо! это отличное решение
Кедар Джоши
20

Поиск по нескольким столбцам с фреймом данных:

frame[frame.filename.str.match('*.'+MetaData+'.*') & frame.file_path.str.match('C:\test\test.txt')]
лакшман сенатираджа
источник
2
frame? а 'C:\test\test.txt'? Похоже, вы отвечаете на другой вопрос.
tumultous_rooster
кадр дф. он относится к одному и тому же вопросу, но он отвечает, как отфильтровать несколько столбцов («имя файла» и «путь_файла») в одной строке кода.
Лакшман Сенатираджа
12

Это может быть немного поздно, но теперь это легче сделать в Пандах. Вы можете вызвать матч с, as_indexer=Trueчтобы получить логические результаты. Это задокументировано (вместе с разницей между matchи contains) здесь .

Майкл Силер
источник
11

Спасибо за отличный ответ @ user3136169, вот пример того, как это можно сделать, также удаляя значения NoneType.

def regex_filter(val):
    if val:
        mo = re.search(regex,val)
        if mo:
            return True
        else:
            return False
    else:
        return False

df_filtered = df[df['col'].apply(regex_filter)]

Также вы можете также добавить регулярное выражение в качестве аргумента:

def regex_filter(val,myregex):
    ...

df_filtered = df[df['col'].apply(res_regex_filter,regex=myregex)]
воробей
источник
1
спасибо, из-за этого я нашел способ фильтровать столбец по произвольному предикату.
Jman
9

Напишите булеву функцию, которая проверяет регулярное выражение и используйте команду apply в столбце.

foo[foo['b'].apply(regex_function)]
user3136169
источник
1

Используя str ломтик

foo[foo.b.str[0]=='f']
Out[18]: 
   a    b
1  2  foo
2  3  fat
YOBEN_S
источник