Сообщить об ошибке.

Использование регулярных выражений в Pandas

Существует несколько методов pandas для работы с регулярными выражениями, которые ищут шаблон в строке, внутри объекта Series.

В материале дается полное описание с примерами использования строковых методов Series.str, которые работают с регулярными выражениями.

Содержание:


Series.str.count(pat, flags=0):

Метод Series.str.count(pat, flags=0) подсчитывает вхождения шаблона pat в каждой строке pandas.Series/pandas.Index.

Аргумент flags принимает флаги модуля re, например. re.I.

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

Некоторые символы необходимо экранировать при передаче шаблона pat. Например, символ '$' имеет особое значение в регулярном выражении, и при поиске этого буквального символа его необходимо экранировать.

Примеры использования Series.str.count():

>>> import pandas as pd
>>> s = pd.Series(['A', 'B', 'Aaba', 'Baca', None, 'CABA', 'cat'])
>>> s.str.count('a')
# 0    0.0
# 1    0.0
# 2    2.0
# 3    2.0
# 4    NaN
# 5    0.0
# 6    1.0
# dtype: float64

Пример экранирования '$', для нахождения буквального знака доллара.

>>> s = pd.Series(['$', 'B', 'Aab$', '$$ca', 'C$B$', 'cat'])
>>> s.str.count('\\$')
# 0    1
# 1    0
# 2    1
# 3    2
# 4    2
# 5    0
# dtype: int64

Метод также доступен в pandas.Index, если индекс использует в качестве меток строки:

>>> pd.Index(['A', 'A', 'Aaba', 'cat']).str.count('a')
# Index([0, 0, 2, 1], dtype='int64')

Series.str.replace(pat, repl, n=-1, case=None, flags=0, regex=False):

Метод Series.str.replace() заменит каждое вхождение шаблона регулярного выражения pat в pandas.Series/pandas.Index.

Эквивалентно str.replace() или re.sub(), в зависимости от значения регулярного выражения.

Принимаемые аргументы:

  • pat - строка может быть последовательностью символов или регулярным выражением/скомпилированным регулярным выражением.
  • repl - строка замены или вызываемый объект. Вызываемому объекту передается объект соответствия регулярному выражению, и он должен возвращать строку замены, которая будет использоваться. Подробнее смотрите re.sub().
  • n - количество замен, которые необходимо сделать с самого начала.
  • case - Определяет, чувствительна ли замена к регистру. Если True, то учитывается регистр. False без учета регистра символов. Невозможно установить, если pat является скомпилированным регулярным выражением.
  • flags - флаги модуля re, например. re.I. Невозможно установить, если pat является скомпилированным регулярным выражением.
  • regex - определяет, является ли переданный шаблон регулярным выражением. Если True, предполагается, что переданный шаблон является регулярным выражением. Если False, то шаблон рассматривается как литеральная строка. Невозможно установить значение False, если pat - скомпилированное регулярное выражение или repl - вызываемый объект.

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

Примеры использования Series.str.replace():

Когда pat является строкой и regex=True, то данный pat компилируется как регулярное выражение. Когда repl является строкой, он заменяет соответствующие шаблоны регулярных выражений, как с помощью re.sub(). Значения NaN в серии оставляются как есть:

>>> pd.Series(['foo', 'fuz', np.nan]).str.replace('f.', 'ba', regex=True)
# 0    bao
# 1    baz
# 2    NaN
# dtype: object

Если pat является строкой, а regex=False, то каждый pat заменяется на repl, как в случае с str.replace().

>>> pd.Series(['f.o', 'fuz', np.nan]).str.replace('f.', 'ba', regex=False)
# 0    bao
# 1    fuz
# 2    NaN
# dtype: object

Когда repl является вызываемым объектом, то он вызывается при каждом pat с помощью re.sub(). Вызываемый объект должен ожидать один позиционный аргумент (объект регулярного выражения) и возвращать строку.

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

>>> pd.Series(['foo', 'fuz', None]).str.replace('f', repr, regex=True)
# 0    <re.Match object; span=(0, 1), match='f'>oo
# 1    <re.Match object; span=(0, 1), match='f'>uz
# 2                                           None
# dtype: object

Перевернем каждое строчное буквенное слово:

>>> repl = lambda m: m.group(0)[::-1]
>>> ser = pd.Series(['foo 123', 'bar baz', np.nan])
>>> ser.str.replace(r'[a-z]+', repl, regex=True)
# 0    oof 123
# 1    rab zab
# 2        NaN
# dtype: object

Использование групп регулярных выражений (извлечем вторую группу и поменяем регистр):

>>> pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"
>>> repl = lambda m: m.group('two').swapcase()
>>> ser = pd.Series(['One Two Three', 'Foo Bar Baz'])
>>> ser.str.replace(pat, repl, regex=True)
# 0    tWO
# 1    bAR
# dtype: object

Использование скомпилированного регулярного выражения с флагами

>>> import re
>>> regex_pat = re.compile(r'FUZ', flags=re.IGNORECASE)
>>> pd.Series(['foo', 'fuz', np.nan]).str.replace(regex_pat, 'bar', regex=True)
# 0    foo
# 1    bar
# 2    NaN
# dtype: object

Series.str.contains(pat, case=True, flags=0, na=None, regex=True):

Метод Series.str.contains() проверяет, содержится ли шаблон pat или регулярное выражение pat в каждой строке pandas.Series/pandas.Index.

Возвращает логический Series или Index на основе того, содержится ли данный шаблон или регулярное выражение в строке Series или Index.

Принимаемые аргументы:

  • pat - строка может быть последовательностью символов или регулярным выражением.
  • case - определяет, чувствительность к регистру символов. Если True, то учитывается регистр. False без учета регистра символов.
  • flags - флаги модуля re, например. re.I.
  • na - заполнитель пропущенных значений. Значение по умолчанию зависит от dtype массива. Для object-dtype используется numpy.nan. Для StringDtype используется pandas.NA.
  • regex - определяет, является ли переданный шаблон регулярным выражением. Если True, предполагается, что переданный шаблон является регулярным выражением. Если False, то шаблон рассматривается как литеральная строка.

Примеры использования Series.str.contains():

Возврат Series логических значений с использованием только символьного шаблона.

>>> s1 = pd.Series(['Mouse', 'dog', 'house and parrot', '23', np.nan])
>>> s1.str.contains('og', regex=False)
# 0    False
# 1     True
# 2    False
# 3    False
# 4      NaN
# dtype: object

Используя "логическую индексацию" можно посмотреть результаты. Логическая маска не должна содержать значения NA. Для замены таких значений используем метод Series.fillna(False):

>>> s1[s1.str.contains('og', regex=False).fillna(False)]
# 1    dog
# dtype: object

Для инверсии результатов можно применить оператор отрицания ~ (логической маски), к тому же, из результатов можно исключить значение NA, присвоив ему True:

>>> s1[~s1.str.contains('og', regex=False).fillna(True)]
# 0               Mouse
# 2    house and parrot
# 3                  23
# dtype: object

Вернемся к примерам использования Series.str.contains(). Чувствительность к регистру указывается с помощью case.

>>> s1.str.contains('oG', case=True, regex=True)
# 0    False
# 1    False
# 2    False
# 3    False
# 4      NaN
# dtype: object

Возвращает ‘house’ или ‘dog’, при помощи регулярного выражения

>>> s1.str.contains('house|dog', regex=True)
# 0    False
# 1     True
# 2     True
# 3    False
# 4      NaN
# dtype: object

Игнорирование чувствительности к регистру с использованием флагов с регулярным выражением.

>>> import re
>>> s1.str.contains('PARROT', flags=re.IGNORECASE, regex=True)
# 0    False
# 1    False
# 2     True
# 3    False
# 4      NaN
# dtype: object

Series.str.extract(pat, flags=0, expand=True):

Метод Series.str.extract() извлекает группы захвата из шаблона регулярного выражения pat в виде столбцов в DataFrame.

Принимаемые аргументы:

  • pat - шаблон регулярного выражения с [группами захвата][].
  • flags - флаги модуля re, например. re.I.
  • expand - если True, возвращает DataFrame с одним столбцом для каждой группы захвата. Если False, возвращает pandas.Series/pandas.Index, если есть одна группа захвата, или DataFrame, если есть несколько групп захвата.

Для каждой строки в Series извлекает группы из первого совпадения регулярного выражения pat.

Примеры использования Series.str.extract():

Шаблон с двумя группами вернет DataFrame с двумя столбцами. Несовпадения будут NaN.

>>> s = pd.Series(['a1', 'b2', 'c3'])
>>> s.str.extract(r'([ab])(\d)')
#     0    1
# 0    a    1
# 1    b    2
# 2  NaN  NaN

Шаблон может содержать необязательные группы.

>>> s.str.extract(r'([ab])?(\d)')
#     0  1
# 0    a  1
# 1    b  2
# 2  NaN  3

Именованные группы в результате станут именами столбцов

>>> s.str.extract(r'(?P<letter>[ab])(?P<digit>\d)')
# letter digit
# 0      a     1
# 1      b     2
# 2    NaN   NaN

Шаблон с одной группой вернет DataFrame с одним столбцом, если expand=True.

>>> s.str.extract(r'[ab](\d)', expand=True)
#     0
# 0    1
# 1    2
# 2  NaN

Шаблон с одной группой вернет серию, если expand=False.

>>> s.str.extract(r'[ab](\d)', expand=False)
# 0      1
# 1      2
# 2    NaN
# dtype: object

Series.str.findall(pat, flags=0):

Метод Series.str.findall() найдет все вхождения шаблона или регулярного выражения pat в pandas.Series/pandas.Index. Эквивалентно применению re.findall() ко всем элементам в Series/Index.

Аргумент flags - представляет собой флаги модуля re, например. re.I.

Примеры использования Series.str.findall():

Для примеров создадим серию:

s = pd.Series(['Lion', 'Monkey', 'Rabbit'])

Поиск по шаблону 'Monkey' возвращает одно совпадение:

>>> s.str.findall('Monkey')
# 0          []
# 1    [Monkey]
# 2          []
# dtype: object

С другой стороны, поиск по шаблону ‘MONKEY’ не возвращает никакого совпадения

>>> s.str.findall('MONKEY')
# 0    []
# 1    []
# 2    []
# dtype: object

Флаги могут быть добавлены к шаблону или регулярному выражению. Например, чтобы найти шаблон 'MONKEY', игнорирующий регистр

>>> import re
>>> s.str.findall('MONKEY', flags=re.I)
# 0          []
# 1    [Monkey]
# 2          []
# dtype: object

Когда шаблон соответствует более чем одной строке в серии, возвращаются все совпадения

>>> s.str.findall('on')
# 0    [on]
# 1    [on]
# 2      []
# dtype: object

Регулярные выражения также поддерживаются. Например, далее показан поиск всех строк, заканчивающихся словом "on":

>>> s.str.findall('on$')
# 0    [on]
# 1      []
# 2      []
# dtype: object

Если шаблон найден более одного раза в одной и той же строке, то возвращается список из нескольких строк:

>>> s.str.findall('b')
# 0        []
# 1        []
# 2    [b, b]
# dtype: object

Series.str.match(pat, case=True, flags=0, na=None):

Метод Series.str.match() определяет, начинается ли каждая строка с совпадения с регулярным выражением.

  • pat - строка может быть последовательностью символов или регулярным выражением.
  • case - определяет, чувствительность к регистру символов. Если True, то учитывается регистр. False без учета регистра символов.
  • flags - флаги модуля re, например. re.I.
  • na - заполнитель пропущенных значений. Значение по умолчанию зависит от dtype массива. Для object-dtype используется numpy.nan. Для StringDtype используется pandas.NA.

Примеры использования Series.str.match():

>>> ser = pd.Series(["horse", "eagle", "donkey"])
>>> ser.str.match("e")
# 0   False
# 1   True
# 2   False
# dtype: bool

Используя "логическую индексацию" можно посмотреть собственно результаты:

>>> ser[ser.str.match("e")]
# 1    eagle
# dtype: object

Для инверсии результатов можно применить оператор отрицания ~ (логической маски)

>>> ser[~ser.str.match("e")]
# 0     horse
# 2    donkey
# dtype: object

Дополнительно смотрите описание функции стандартной библиотеки re.match().

Series.str.split(pat=None, *, n=-1, expand=False, regex=None)
Series.str.rsplit(pat=None, *, n=-1, expand=False, regex=None)
:

Метод Series.str.split() разбивает строки по заданному разделителю pat начиная начала n количество раз в pandas.Series/pandas.Index.

Метод эквивалентен использованию str.split() или re.split().

Принимаемые аргументы:

  • pat - строка или регулярное выражение/скомпилированным регулярным выражением. Если не указано, то делит по пробелам.
  • n - ограничивает количество делений. None, 0 и -1 будут интерпретироваться как делить по всем вхождениям.
  • expand - если True, возвращает DataFrame/MultiIndex, расширяющий размерность. Если False, возвращает Series/Index, содержащий списки строк.
  • regex - определяет, является ли переданный шаблон регулярным выражением:
    • Если True, предполагается, что переданный шаблон является регулярным выражением
    • Если False, обрабатывает шаблон как буквальную строку.
    • Если None и длина pat равна 1, то обрабатывает pat как буквальную строку.
    • Если None и длина pat не равна 1, то pat рассматривается как регулярное выражение.
    • Не может быть установлено значение False, если pat является скомпилированным регулярным выражением

Обработка ключевого аргумента n зависит от количества найденных разбиений:

  • Если найденные разбиения > n, то выполнит только первые n разбиений;
  • Если найденные разбиения <= n, то выполнит все разбиения;
  • Если для определенной строки количество найденных разделений < n, то добавит None для заполнения до n, если expand=True;
  • При использовании expand=True вызывающие Series и Index возвращают объекты DataFrame и MultiIndex соответственно.
  • Использование regex=False с pat в качестве скомпилированного регулярного выражения вызовет ошибку.

Примеры использования Series.str.split():

>>> s = pd.Series(
...     [
...         "this is a regular sentence",
...         "https://docs.python.org/3/tutorial/index.html",
...         np.nan
...     ]
... )
>>> s
# 0                       this is a regular sentence
# 1    https://docs.python.org/3/tutorial/index.html
# 2                                              NaN
# dtype: object

По умолчанию строка делится по пробелам.

>>> s.str.split()
# 0                   [this, is, a, regular, sentence]
# 1    [https://docs.python.org/3/tutorial/index.html]
# 2                                                NaN
# dtype: object

Без аргумента n выходные данные Series.str.rsplit() и Series.str.split() идентичны.

>>> s.str.rsplit()
# 0                   [this, is, a, regular, sentence]
# 1    [https://docs.python.org/3/tutorial/index.html]
# 2                                                NaN
# dtype: object

Аргумент n можно использовать для ограничения количества разделений.

# МЕТОД `Series.str.split()`
>>> s.str.split(n=2)
# 0                     [this, is, a regular sentence]
# 1    [https://docs.python.org/3/tutorial/index.html]
# 2                                                NaN
# dtype: object

# МЕТОД `Series.str.rsplit()`
>>> s.str.rsplit(n=2)
# 0                     [this is a, regular, sentence]
# 1    [https://docs.python.org/3/tutorial/index.html]
# 2                                                NaN
# dtype: object

Аргумент pat можно использовать для разделения на пользовательские символы.

>>> s.str.split(pat="/")
# 0                         [this is a regular sentence]
# 1    [https:, , docs.python.org, 3, tutorial, index...
# 2                                                  NaN
# dtype: object

При использовании expand=True разделенные элементы будут расширяться в отдельные столбцы. Если присутствует значение NaN, оно распространяется по столбцам во время разделения.

>>> s.str.split(expand=True)
#                                                0     1     2        3         4
# 0                                           this    is     a  regular  sentence
# 1  https://docs.python.org/3/tutorial/index.html  None  None     None      None
# 2                                            NaN   NaN   NaN      NaN       NaN

Для более сложных случаев, таких как отделение имени html-документа от URL-адреса, можно использовать комбинацию настроек.

>>> s.str.rsplit("/", n=1, expand=True)
#                                     0           1
# 0          this is a regular sentence        None
# 1  https://docs.python.org/3/tutorial  index.html
# 2                                 NaN         NaN

Не забывайте экранировать специальные символы при явном использовании регулярных выражений

>>> s = pd.Series(["foo and bar plus baz"])
>>> s.str.split(r"and|plus", expand=True)
#     0   1   2
# 0 foo bar baz

Регулярные выражения могут использоваться для обработки URL-адресов или имен файлов. Когда pat является строкой, а regex=None (по умолчанию), данный pat компилируется как регулярное выражение, только если len(pat) != 1.

>>> s = pd.Series(['foojpgbar.jpg'])
>>> s.str.split(r".", expand=True)
#            0    1
# 0  foojpgbar  jpg

>>> s.str.split(r"\.jpg", expand=True)
#            0 1
# 0  foojpgbar

Когда regex=True, то pat интерпретируется как регулярное выражение.

>>> s.str.split(r"\.jpg", regex=True, expand=True)
#            0 1
# 0  foojpgbar

Скомпилированное регулярное выражение можно передать как pat:

>>> import re
>>> s.str.split(re.compile(r"\.jpg"), expand=True)
#            0 1
# 0  foojpgbar

Когда regex=False, то pat интерпретируется как сама строка.

>>> s.str.split(r"\.jpg", regex=False, expand=True)
#                0
# 0  foojpgbar.jpg