Замена набора слов в большом текстовом файле - PullRequest
0 голосов
/ 26 мая 2020

У меня есть большой текстовый файл (около 20 ГБ). Я хочу заменить все экземпляры списка слов из этого большого файла. Я изо всех сил пытаюсь найти способ оптимизировать этот код. Это приводит к тому, что я обрабатываю этот файл очень долго.

что я могу улучшить?

    corpus_input =  open(corpus_in,"rt")
    corpus_out = open(corpus_out,"wt")
    for line in corpus_input:
        temp_str=line
        for word in dict_keys:
            if word in line:
                new_word = word+"_lauren_ipsum"
                temp_str = re.sub(fr'\b{word}\b',new_word,temp_str)

            else:
                continue
        
        corpus_out.writelines(temp_str)

     corpus_input.close()
     corpus_out.close()

1 Ответ

1 голос
/ 26 мая 2020

Самое важное для оптимизации - понять, что именно работает плохо. Тогда вы увидите, что можно оптимизировать.

Если, например, чтение и запись занимают 99% времени, на самом деле не стоит оптимизировать обработку ваших данных. Даже если бы вы могли ускорить обработку на 10, вы бы просто получили 0,9%, если бы чтение и запись потребляло 99%

Я предлагаю измерить и сравнить некоторые версии и опубликовать различия в производительности. Это может привести к потенциальным дальнейшим предложениям по оптимизации.

Во всех приведенных ниже примерах я заменил writelines на write, поскольку строки письма, вероятно, разлагают ваш строчный символ за символом перед записью.

В любом случае. Вы хотите использовать write У вас уже должно быть ускорение примерно на 5.

1.) Просто чтение и запись

with open(corpus_in,"rt") as corpus_input, open(corpus_out,"wt")
 as corpus_out:
   for line in corpus_input:
       corpus_out.write(line)

2.) Просто чтение и запись с большим буфером

import io

BUF_SIZE = 50 * io.DEFAULT_BUFFER_SIZE # try other buffer sizes if you see an impact
with open(corpus_in,"rt", BUF_SIZE) as corpus_input, open(corpus_out,"wt", BUF_SIZE)
 as corpus_out:
   for line in corpus_input:
corpus_out.write(line)

Для меня это увеличивает производительность на несколько процентов

3.) переместить поисковое регулярное выражение и генерацию замены из l oop.

   rules = []
   for word in dict_keys:
       rules.append((re.compile(fr'\b{word}\b'), word + "_lorem_ipsum"))

   for line in corpus_input:
       for regexp, new_word in rules: 
           line = regexp.sub(new_word, line)
       corpus_out.write(line)

На моей машине с моей частотой строк, содержащих слова, это решение фактически медленнее, чем решение, имеющее строку if word in line

Так что, возможно, попробуйте: 3.a) переместить поисковое регулярное выражение и генерацию замены из l oop.

   rules = []
   for word in dict_keys:
       rules.append((word, re.compile(fr'\b{word}\b'), word + "_lorem_ipsum"))

   for line in corpus_input:
       for word, regexp, new_word in rules: 
           if word in line:
               line = regexp.sub(new_word, line)
       corpus_out.write(line)

3.b) Если все замещающие строки длиннее исходных, это будет немного быстрее.

   rules = []
   for word in dict_keys:
       rules.append((word, re.compile(fr'\b{word}\b'), word + "_lorem_ipsum"))

   for line in corpus_input:
       temp_line = line
       for word, regexp, new_word in rules: 
           if word in line:
               temp_line = regexp.sub(new_word, temp_line)
       corpus_out.write(temp_line)

4. ), если вы действительно замените всегда на word + "_lorem_ipsum", объедините регулярное выражение в одно.

   regexp = re.compile(fr'\b({"|".join(dict_keys)})\b')

   for line in corpus_input:
       line = regexp.sub("\1_lorem_ipsum", line)
       corpus_out.write(line)

4.a) в зависимости от распределения слов это может быть быстрее:

   regexp = re.compile(fr'\b({"|".join(dict_keys)})\b')

   for line in corpus_input:
       if any(word in line for word in dict_keys):
           line = regexp.sub("\1_lorem_ipsum", line)
       corpus_out.write(line)

Независимо от того, это более эффективно или нет, вероятно, зависит от количества слов для поиска и заменить и частота этих слов. У меня нет этой даты.

Для 5 слов и моего распределения медленнее, чем 3.a

5) если заменяемые слова разные, вы все равно можете попытаться объединить регулярные выражения и использовать функция для замены

   replace_table = {
      "word1": "word1_laram_apsam",
      "word2": "word2_lerem_epsem",
      "word3": "word3_lorom_opsom",

   }

   def repl(match):
      return replace_table[match.group(1)]

   regexp = re.compile(fr'\b({"|".join(dict_keys)})\b')

   for line in corpus_input:
       line = regexp.sub(repl, line)
       corpus_out.write(line)

Медленнее 5 или лучше, чем 3.a, зависит от количества слов и распределения / частоты wird.

...