Как ускорить это регулярное выражение в функции .Replace? - PullRequest
0 голосов
/ 27 января 2020

У меня есть 4,5 миллиона строк для обработки, поэтому мне нужно так сильно ускорить это. Я не понимаю, как регулярное выражение работает довольно хорошо, поэтому мне сложно понять другие ответы.

У меня есть столбец, содержащий ID с, например:

ELI-123456789

Эта цифра c часть этого идентификатора содержится в этой строке в столбце m bk_name , начинающемся с "@":

AAAAA@123456789_BBBBB;CCCCC;

Теперь моя цель - изменить эту строку на эту строку, добавить идентификатор в конце, начать с «#», сохранить в new_name :

AAAAA_BBBBB; CCCCC; # 123456789

Вот что я пробовал:

  1. Возьмите ID и замените «ELI-» на «@», сохраните как ID2 :

df["ID2"] = df["ID"].str.replace("ELI-", "@")

Найдите ID2 в строке bk_name и замените его на "":

df["new_name"] = df["bk_name"].replace(regex = r'(?i)' + df["ID2"].astype(str), value = "")

Снова возьмите ID , замените «ELI-» на «#», добавьте его в конец строки new_name :

df["new_name"] = df["new_name"] + df["ID"].str.replace("ELI-", "#")

Теперь проблема в том, что шаг 2 - это строка, которая занимает большую часть времени, для обработки 6700 строк потребовалось 6,5 секунды. Но теперь мне нужно обработать 4,6 миллиона строк, прошло 7 часов, и он все еще работает, и я понятия не имею, почему.

По моему мнению, регулярное выражение замедляет мой код. Но у меня нет более глубокого понимания. Так что спасибо за вашу помощь заранее, любые предложения будут оценены:)

Ответы [ 3 ]

0 голосов
/ 27 января 2020

Итак, ваш подход не так уж и плох.

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

Что касается шага 2, не используйте регулярное выражение только для замены строки другой строкой (в данном случае "").

df = {};
df['ID'] = 'ELI-123456789'; df['bk_name'] = 'AAAAA@123456789_BBBBB;CCCCC;';
print(df)

# Take the ID and replace "ELI-" with "@", save as ID2:

# df["ID2"] = df["ID"].str.replace("ELI-", "@")
df["ID2"] = df["ID"].replace("ELI-", "@")
print(df)

# Find ID2 in the string of bk_name and replace it with "":

# df["new_name"] = df["bk_name"].replace(regex = r'(?i)' + df["ID2"].astype(str), value = "")
df["new_name"] = df["bk_name"].replace(df["ID2"], "")
print(df)

# Take the ID again, replace "ELI-" with "#", add it at the end of the string of new_name:

# df["new_name"] = df["new_name"] + df["ID"].str.replace("ELI-", "#") 
df["new_name"] = df["new_name"] + df["ID"].replace("ELI-", "#") 
print(df)

Надеюсь, это поможет

РЕДАКТИРОВАТЬ:

Вы уверены, что этот кусок кода замедляет ваш проект? Вы пытались изолировать его и посмотреть, сколько времени на самом деле требуется, чтобы выполнить?

Попробуйте это:

import timeit
code_to_test = """
df = {};
df['ID'] = 'ELI-123456789'; df['bk_name'] = 'AAAAA@123456789_BBBBB;CCCCC;';
df["new_name"] = df['bk_name'].replace('@' + df['ID'][4:],'') + '#' + df['ID'][4:] # <----- ONE LINER
# print(df['new_name'])
"""
elapsed_time = timeit.timeit(code_to_test, number=4600000)
print(elapsed_time)

Я сократил его до одной строки, которая работает ТОЛЬКО ЕСЛИ:

  • ELI- (или 4-символьная строка) - это всегда строка, подлежащая замене
  • @ - это всегда удаляемый символ и # всегда нужно добавить

По крайней мере, в ноутбуке, который я кодирую, он всегда выполняет код 4600000 раз менее чем за 4 секунды

0 голосов
/ 28 января 2020

Я не понимаю, что вызвало проблему, но я решил ее.

Единственное, что я изменил, - это шаг 2. Я использовал функцию apply / lambda, и она неожиданно работает, и я понятия не имею, почему.

  1. Возьмите ID и замените "ELI" на "@", сохраните как ID2 :

df["ID2"] = df["ID"].str.replace("ELI-", "@")

Найдите ID2 в строке bk_name и замените его на "":

df["new_name"] = df[["bk_name", "ID2"]].astype(str).apply(lambda x: x["bk_name"].replace(x["ID2"], ""), axis=1)

Снова возьмите ID , замените «ELI-» на «#», добавьте его в конец строки new_name :

df["new_name"] = df["new_name"] + df["ID"].str.replace("ELI-", "#")

0 голосов
/ 27 января 2020

Ну, я бы сказал, что если вы знаете, что за частью идентификатора всегда стоит ELI-, за которой следует число и что это число всегда стоит за @ в начале, то я бы не считывал идентификатор на шаге 1 Непосредственно работайте на шаге 2 с этим регулярным выражением и заменой:

https://regex101.com/r/QBQB7E/1

Вы сможете создавать содержимое своего нового поля только с одним присваивание с результатом подстановки старого поля. Идея ускорить процесс состоит в том, чтобы избежать использования 4-5 строк кода для выполнения операции получения идентификатора, его поиска и последующей перекомпоновки результата. Регулярное выражение и шаблон замещения могут делать все это за одну операцию.

Вы можете сгенерировать некоторый код Python непосредственно из Regex101, чтобы интегрировать его для создания содержимого поля new_name.

Пояснение

^(.*?)@(\d+)(.*)$ - это регулярное выражение

  • ^ означает «начиная с».
  • $ означает «окончание».
  • () - захват группы и создание переменной, которую мы затем можем использовать в шаблоне замены. \1 для первой подходящей группы, \2 для второй и т. Д. c.
  • .*? соответствует любому символу от нуля до неограниченного числа раз, как можно меньше раз, расширяясь по мере необходимости (ленивый ).
  • @ соответствует символу @ буквально (с учетом регистра).
  • \d+ соответствует значению di git (равно [0-9]) один или несколько раз.
  • .* соответствует любому символу от нуля до неограниченного числа раз, столько раз, сколько возможно, возвращая при необходимости (жадность).

Строка замены \1\3#\2, поэтому он берет первую группу соответствия, за которой следует последняя, ​​а затем добавляет #, за которым следует вторая группа соответствия, которая является вашим идентификатором.

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

Вторая версия:

  • ^([^@]+)@(\d+)(.*)$, где я заменил .*? на [^@]+, что означает найти любой символ, который не @ один или несколько раз.
  • Решение ее e: https://regex101.com/r/QBQB7E/3

Протестировал код, и я получил его примерно за 4 секунды ...

import timeit

code_to_test = """
import re

regex = r"^([^@]+)@(\d+)(.*)$"
subst = "\\1\\3#\\2"

df = {
    "ID": "ELI-123456789",
    "bk_name": "AAAAA@123456789_BBBBB;CCCCC;"
}

df["new_name"] = re.sub(regex, subst, df["bk_name"])
"""

elapsed_time = timeit.timeit(code_to_test, number=4600000)
print(elapsed_time)

Я действительно думаю, что если вы кодируете работает через 7 часов, это, вероятно, где-то еще, где проблема. Движок регулярных выражений выглядит не намного медленнее, чем операции ручного поиска / замены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...