Замена строки Pandas аргументом регулярного выражения для замен, не являющихся регулярными выражениями

Предположим, у меня есть фрейм данных, в котором я хочу заменить подстроку, не являющуюся регулярным выражением, состоящую только из символов (т. е. 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)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
50
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Функция 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 — лучший вариант для повышения производительности, когда вам не нужны регулярные выражения.

Это не совсем так. Да, str.replace использует как Python str.replace, так и re.sub, но выбор использования одного из них не делается исключительно на основе ключевого слова regex. Например, pd.Series(['ABC']).str.replace('a', 'x', regex=False, case=False) будет использовать re.sub под капотом.

mozway 20.08.2024 11:29

@mozway: Любопытно, какие еще факторы влияют на выбор функции?

silence_of_the_lambdas 20.08.2024 11:30

@mozway Суть моего ответа заключалась в вызове механизма регулярных выражений вместо простой замены. Если под капотом Pandas становится умнее и игнорирует собственный флаг regex, то это в любом случае выходит из-под нашего контроля.

Tim Biegeleisen 20.08.2024 11:32

@silence_of_the_lambdas передает флаги и использует вызываемый объект в качестве замены / @Tim да, вы можете рассматривать это как «умную» функцию. Series.replace еще более «продвинутый».

mozway 20.08.2024 11:33
Ответ принят как подходящий

Особые персонажи!

Использование регулярных выражений с простыми словами, как правило, нормально (кроме проблем с эффективностью), однако могут возникнуть проблемы, если у вас есть специальные символы. Эту проблему часто упускают из виду, и я видел многих людей, которые не понимали, почему их 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.

Как str.replace решает использовать регулярное выражение или простую строковую операцию?

чистая замена Python будет использоваться, если:

  • regex=False
  • pat — это строка (передача скомпилированного регулярного выражения с regex=False вызовет ValueError)
  • case нет False
  • флаги не установлены
  • repl не вызывается

Во всех остальных случаях будет использоваться 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 20.08.2024 11:27

@silence_of_the_lambdas Я пропустил этот момент, но, по моему мнению, это проблема №1 с параметром regex. Как я уже упоминал в комментарии к другому ответу, regex=False не только определяет, будет ли использоваться регулярное выражение. Если вы используете regex=False, case=False, будет использовано регулярное выражение.

mozway 20.08.2024 11:31

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

mozway 20.08.2024 11:39

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

Другие вопросы по теме