Существует несколько методов pandas
для работы с регулярными выражениями, которые ищут шаблон в строке, внутри объекта Series
.
В материале дается полное описание с примерами использования строковых методов
Series.str
, которые работают с регулярными выражениями.
Series.str.count()
подсчитывает вхождения шаблона в строке;Series.str.replace()
заменит каждое вхождение шаблона регулярного выражения;Series.str.contains()
проверяет, содержится ли регулярное выражение в каждой строке;Series.str.extract()
извлекает группы захвата из шаблона регулярного выражения;Series.str.findall()
найдет все вхождения регулярного выражения;Series.str.match()
определяет, начинается ли каждая строка с совпадения с регулярным выражением;Series.str.split()
разбивает строки по заданному разделителю;Series.str.rsplit()
разбивает строки по заданному разделителю начиная справа.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