Предположим, у меня есть фрейм данных, в котором я хочу заменить подстроку, не являющуюся регулярным выражением, состоящую только из символов (т. е. a-z, A-Z) и/или цифр (т. е. 0-9) через pd.Series.str.replace. В документации указано, что эта функция эквивалентна str.replace или re.sub(), в зависимости от аргумента regex (по умолчанию False).
Помимо, скорее всего, излишеств, есть ли какие-либо недостатки, которые следует учитывать, если функция вызывалась с regex=True для замен, не связанных с регулярными выражениями (например, производительность)? Если да, то какие? Конечно, я не предлагаю использовать функцию таким образом.
Пример: замените «Слон» в приведенном ниже фрейме данных.
import pandas as pd
data = {'Animal_Name': ['Elephant African', 'Elephant Asian', 'Elephant Indian', 'Elephant Borneo', 'Elephant Sumatran']}
df = pd.DataFrame(data)
df = df['Animal_Name'].str.replace('Elephant', 'Tiger', regex=True)






Функция Pandas str.replace() использует re.sub() под капотом, когда установлен флаг regex=True. В противном случае для regex=False используется базовая строковая функция Python replace().
Реализации re.sub и replace не одинаковы. В общем, мы ожидаем более высоких накладных расходов при замене подстроки с помощью re.sub по сравнению с replace. Одним из таких накладных расходов может быть то, что при использовании re.sub (str.replace с regex=True) первый параметр сначала необходимо проанализировать как регулярное выражение, прежде чем его можно будет использовать.
В общем, вам следует избегать использования регулярных выражений, если они вам не нужны, поэтому использование regex=False — лучший вариант для повышения производительности, когда вам не нужны регулярные выражения.
@mozway: Любопытно, какие еще факторы влияют на выбор функции?
@mozway Суть моего ответа заключалась в вызове механизма регулярных выражений вместо простой замены. Если под капотом Pandas становится умнее и игнорирует собственный флаг regex, то это в любом случае выходит из-под нашего контроля.
@silence_of_the_lambdas передает флаги и использует вызываемый объект в качестве замены / @Tim да, вы можете рассматривать это как «умную» функцию. Series.replace еще более «продвинутый».
Использование регулярных выражений с простыми словами, как правило, нормально (кроме проблем с эффективностью), однако могут возникнуть проблемы, если у вас есть специальные символы. Эту проблему часто упускают из виду, и я видел многих людей, которые не понимали, почему их str.replace не удалось.
Панды даже изменили стандартное регулярное выражение=True на regex=False , и первоначальная причина этого ( #GH24804) заключалась в том, что str.replace('.', '') удалит все символы, что ожидается, если вы знаете регулярное выражение, но не совсем, если ты нет.
Например, давайте попробуем заменить 1.5 на 2.3, а валюту $ на £:
df = pd.DataFrame({'input': ['This item costs $1.5.', 'We need 125 units.']})
df['out1'] = df['input'].str.replace('1.5', '2.3', regex=False)
df['out1_regex'] = df['input'].str.replace('1.5', '2.3', regex=True)
df['out2'] = df['input'].str.replace('$', '£', regex=False)
df['out2_regex'] = df['input'].str.replace('$', '£', regex=True)
Выход:
input out1 out1_regex \
0 This item costs $1.5. This item costs $2.3. This item costs $2.3.
1 We need 125 units. We need 125 units. We need 2.3 units.
out2 out2_regex
0 This item costs £1.5. This item costs $1.5.£
1 We need 125 units. We need 125 units.£
Поскольку . и $ имеют особое значение в регулярном выражении, их нельзя использовать как есть, и их следует экранировать (1\.5/\$), что можно сделать программно с помощью re.escape.
чистая замена Python будет использоваться, если:
regex=Falsepat — это строка (передача скомпилированного регулярного выражения с regex=False вызовет ValueError)case нет Falserepl не вызываетсяВо всех остальных случаях будет использоваться re.sub.
Код, который это делает: core/strings/object_array.py:
def _str_replace(
self,
pat: str | re.Pattern,
repl: str | Callable,
n: int = -1,
case: bool = True,
flags: int = 0,
regex: bool = True,
):
if case is False:
# add case flag, if provided
flags |= re.IGNORECASE
if regex or flags or callable(repl):
if not isinstance(pat, re.Pattern):
if regex is False:
pat = re.escape(pat)
pat = re.compile(pat, flags=flags)
n = n if n >= 0 else 0
f = lambda x: pat.sub(repl=repl, string=x, count=n)
else:
f = lambda x: x.replace(pat, repl, n)
Учитывая шаблон без специальных символов, regex=True примерно в 6 раз медленнее, чем regex=False в линейном режиме:
Конечно, это правда, поэтому я написал, что подстрока должна содержать только символы (a-z, A-Z) и цифры (0-9) :)
@silence_of_the_lambdas Я пропустил этот момент, но, по моему мнению, это проблема №1 с параметром regex. Как я уже упоминал в комментарии к другому ответу, regex=False не только определяет, будет ли использоваться регулярное выражение. Если вы используете regex=False, case=False, будет использовано регулярное выражение.
@silence_of_the_lambdas, чтобы ответить на ваш другой комментарий, я добавил более подробную информацию о том, как осуществляется выбор использования механизма регулярных выражений.
re.sub существенно медленнее, чем str.replace, даже для таких простых строк. Вы можете проверить это с помощью модуля timeit:
from timeit import timeit
setup = "import pandas as pd; series = pd.Series(['Elephant']).repeat(10000)"
print(timeit(setup=setup, stmt = "series.str.replace('Elephant', 'Tiger', regex=False)",
number=10000))
print(timeit(setup=setup, stmt = "series.str.replace('Elephant', 'Tiger', regex=True)",
number=10000))
Для меня результаты были заметно другими.
11.037849086000051
18.286654022999983
Это не совсем так. Да,
str.replaceиспользует как Pythonstr.replace, так иre.sub, но выбор использования одного из них не делается исключительно на основе ключевого словаregex. Например,pd.Series(['ABC']).str.replace('a', 'x', regex=False, case=False)будет использоватьre.subпод капотом.