Добавьте случайно нарисованную подстроку к каждому вхождению слова (эффективность, Python) - PullRequest
0 голосов
/ 16 апреля 2020

У меня длинная строка, и я хочу добавить одну из двух случайно определенных подстрок в каждое вхождение указанного c слова. Пример: случайным образом добавить '_1' или '_2' к каждому вхождению 'c':

s = 'a b c d c r'
to_add = ['_1', '_2']

Желаемый результат:

a b c_2 d c_1 r

Я нашел несколько способов сделать это. Например:

#Method 1
import random
import numpy as np
s = 'a b c d c r'
to_add = ['_1', '_2']
s_new = ' '+s+' ' # Spaces needed because 'c' could be the first or last element
while ' c ' in s_new:
    s_new = s_new.replace(' c ', ' '+'c'+np.random.choice(to_add)+' ', 1)
s_new = s_new[1:-1]

Еще один пример:

# Method 2
s_new = s.split(' ')
for n, i in enumerate(s_new):
    if i == 'c':
        subscript = np.random.choice(to_add)
        s_new[n] = 'c'+subscript
s_new = (' ').join(s_new)

Однако я работаю с очень большим корпусом, и поэтому эффективность является ключевым фактором. Переназначение в методе 1 очень медленное. Перебор всех элементов в методе 2 также кажется излишне медленным. Мне интересно, есть ли лучший подход. Вот кое-что, о чем я думаю, но я не уверен, что это может быть эффективно реализовано:

n = s.count('c')
to_replace = [np.random.choice(to_add) for x in range(n)]

На этом этапе мне потребуется какой-то способ последовательной замены 'c' элементами в to_replace.

У кого-нибудь есть идеи, как реализовать ту или иную мысль по повышению эффективности?

Редактировать: Просто подумал о другом:

# Method 3
s_new = s.split(' ')
indices = [i for i, x in enumerate(s_new) if x == 'c']
for ind in indices:
    s_new[ind] = 'c'+np.random.choice(to_add)
s_new = (' ').join(s_new)

Однако, все они в значительной степени идентичны по скорости.

1 Ответ

1 голос
/ 16 апреля 2020

Более эффективный подход может быть следующим:

  • Начните с подсчета количества раз, которое появляется c
  • Генерация k случайных выборок из to_add
  • Заменить каждое вхождение c элементом указанной последовательности

Вот один из способов использования re.sub:

import re

c_counts = s.count('c')
replace_with = iter(random.choices(to_add, k=c_counts))
# ['_2', '_1']
re.sub(r'(c)', lambda _: 'c'+next(replace_with), s)
# 'a b c_2 d c_1 r'

Синхронизация в большем массиве:

def yatu(s, to_add):
    c_counts = s.count('c')
    replace_with = iter(random.choices(to_add, k=c_counts))
    out = re.sub(r'(c)', lambda _: 'c'+next(replace_with), s)

def op1(s, to_add):
    s_new = ' '+s+' ' # Spaces needed because 'c' could be the first or last element
    while ' c ' in s_new:
        s_new = s_new.replace(' c ', ' '+'c'+np.random.choice(to_add)+' ', 1)
    s_new = s_new[1:-1]

def op2(s, to_add):
    s_new = s.split(' ')
    for n, i in enumerate(s_new):
        if i == 'c':
            subscript = np.random.choice(to_add)
            s_new[n] = 'c'+subscript
    s_new = (' ').join(s_new)

def op3(s, to_add):
    s_new = s.split(' ')
    indices = [i for i, x in enumerate(s_new) if x == 'c']
    for ind in indices:
        s_new[ind] = 'c'+np.random.choice(to_add)
    s_new = (' ').join(s_new)

s_large = f'{s} '*10000

%timeit yatu(s_large, to_add)
# 12.7 ms ± 730 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit op1(s_large, to_add)
# 2.64 s ± 19.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit op2(s_large, to_add)
# 218 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit op3(s_large, to_add)
# 213 ms ± 4.89 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...