Быстрый способ фильтрации строк на основе списка ключевых слов на Python

#python #algorithm #for-loop #indexing

#python #алгоритм #для цикла #индексирование

Вопрос:

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

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

     found = []
    for identifier in ids:
        with open("file.txt", 'r') as f:
            for line in f.readlines():
                if identifier in line:
                    found.append(line)
 

Однако это происходит очень медленно, поскольку я запускаю два вложенных цикла, а список идентификаторов и текстовый файл огромны. Есть ли в python более эффективный и эффективный способ решить эту проблему менее чем за O (n ^ 2)?

Дополнительная информация и ограничения:

  • Любая строка может содержать только один идентификатор из моего списка или не содержать его
  • Я не могу отсортировать файл на основе идентификаторов, поскольку они не обязательно имеют форму, которая может быть иерархически структурирована

Комментарии:

1. Было бы лучше, добавив образец ids списка, текстовую строку file.txt smoe и выходной образец.

2. Чтение файла снова и снова, скорее всего, является узким местом. -> прочитайте файл только один раз и проверьте каждую строку на наличие каждого идентификатора.

3. Вы можете создать регулярное выражение для поиска по всем ключевым словам сразу.

Ответ №1:

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

 f = open("demofile.txt", "r")
mylines = f.readlines()
                       
found = []
for line in mylines:
   for identifier in ids:
      if identifier in line:
          found.append(line)
 

Комментарии:

1. Хорошая мысль! Я даже не заметил, что читаю его каждый раз.

2. Пожалуйста, примите ответ, если он решил вашу проблему

Ответ №2:

Неясно, как line составляется, но если его можно четко обозначить, вы можете создать identifier коллекцию a set , а затем проверить, есть ли идентификатор строки в наборе.

set это hashset , поэтому его поиск равен O (1), поэтому все должно выполняться в O (n) .

Использование readlines также кажется ненужным, лениво перебирайте файл.

 ids = set(ids)
with open('demofile.txt', 'r', encoding='utf-8') as f:
    found = [
        line
        for line in f
        if get_id(line) in ids
    ]
 

вам просто нужно предоставить get_id , который просто вырезает «уникальный идентификатор» «в некоторой позиции».

Ответ №3:

Я бы рекомендовал использовать https://pypi.org/project/triegex / чтобы создать регулярное выражение, которое соответствует любому из ваших идентификаторов и выполняет минимальное обратное отслеживание.

И теперь у вас есть только один цикл.

Ответ №4:

Ваш код будет повторять некоторые строки, если строка содержит более одного идентификатора.

Итак, здесь мы попробуем другую технику — я не уверен, что она будет быстрее или нет, — но в ней не будет повторяющихся линий, и эта техника основана на прочном фундаменте. Давайте попробуем использовать panads:

 import pandas as pd 
ids=['id1', 'id2', 'id3'] # list of string ids
df=pd.read_csv('file.txt', sep = "`", names = ['txt'])
found =list(df.txt.loc[df.txt.apply(lambda x: sum((id in x) for id in ids )>0)])
 

Некоторые пояснения

df=pd.read_csv('file.txt', sep = "`", names = ['txt']) Загрузите файл в DataFrame. Я использовал ` в качестве аргумента для рассмотрения всей строки как одного столбца, поскольку ` она редко используется в обычных текстовых строках.

(id in x) for id in ids генерирует список True и False на основе количества таких идентификаторов в каждой строке (строке), а внешний sum получит суммирование True

если у нас есть file.txt

 fwpep poweripoi id1 ewlfdfkd f p[woer[pwe dlkdfwero0iopwiperew
we;rioepo ,r rtoipweorit ,rt rtopipowerit
werert.rtrtid1eyri  id1  id2 pid2oerit poier tpoerit eropitpo
 

таким образом, найденный контент будет:

 Out[1]:found 
['fwpep poweripoi id1 ewlfdfkd f p[woer[pwe dlkdfwero0iopwiperew',
 'werert.rtrtid1eyri  id1  id2 pid2oerit poier tpoerit eropitpo']
 

между тем, ваше исходное содержимое кода будет

 Out[1]:found 
['fwpep poweripoi id1 ewlfdfkd f p[woer[pwe dlkdfwero0iopwiperew',
 'werert.rtrtid1eyri  id1  id2 pid2oerit poier tpoerit eropitpo',
 'werert.rtrtid1eyri  id1  id2 pid2oerit poier tpoerit eropitpo']