Обработка больших текстовых файлов на python

#python #dictionary #large-files

#python #словарь #большие файлы

Вопрос:

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

Первый файл содержит 30 миллионов строк, а второй файл содержит 5 миллионов строк.

Я пытаюсь сделать это с помощью dictionary, но это занимает огромное время.

Не могли бы вы, пожалуйста, предложить мне лучший способ сделать это.

Вот фрагмент из моего кода и несколько строк из моих файлов.

Основной файл

#

Джон 2343245

Карим 126754

Rob 6543289

Виджай 2247861

Sam 2649860

….

Подмножество второго файла

Сэм

Роб

Джон

 def extract_info(sub_file,master_file):
    sub_fh = open(sub_file,'r',16777216)
    sub_inst_list = []
    for line in sub_fh:
        if line.startswith('#'):
            continue
        else:
            name = line.rstrip()
            sub_inst_list.append(name)
    sub_fh.close()


out_file = "results.rpt"
outf = open(out_file,'w')
bunchsize = 10000
bunch = []
master_fh = open(master_file,'r',16777216)
for line in master_fh:
    if line.startswith('#'):
        continue
    else:
        data = line.split()
        name = data[0]
        if str(data[1]) == "n/a":
            continue
        else:
            if name in sub_inst_list:
                id = str(data[1])
                line = "%-80s %-15sn" % (name, id)
                bunch.append(line)
                if len(bunch) == bunchsize: 
                    outf.writelines(bunch)
                    bunch= []
                outf.writelines(bunch)
  master_fh.close()
  outf.close()
  

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

1. почему бы не использовать соответствующую базу данных? они созданы для такого рода запросов.

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

Ответ №1:

Лучший способ — поместить все данные из основного файла в базу данных, а затем выполнить поиск значений на основе ключей из второго файла:

 import sqlite3

conn = sqlite3.connect(":memory:")
c = conn.cursor()
c.execute("CREATE TABLE data (Name VARCHAR(255), ID INT)")

# fill the DB
with open("master.txt") as f:
    for line in f:
        c.execute("INSERT INTO data VALUES (?, ?)", line.split())
conn.commit()

# search for data
with open("slave.txt") as f:
    for line in f:
        print(c.execute("SELECT ID FROM data WHERE Name=:search_name", {"search_name": line.strip()}).fetchall())

conn.close()
  

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

1. Спасибо ForceBru за предоставление кода. Есть ли способ, которым я могу получить и имя, и идентификатор в файл вместо записи в оболочке.

2.курс .

3. Ребята, я попробовал то, что было предложено @ForceBru, но я все еще вижу, что обработка файла занимает огромное время выполнения. Я сообщил время для каждых 1000 поисковых строк. отладка: запись в выходной файл 1000 строк, время, затрачиваемое на запись 1000 строк = 18102 отладка: запись в выходной файл 2000 строк, время, затрачиваемое на запись 1000 строк = 19825 отладка: запись в выходной файл 3000 строк, время, затрачиваемое на запись 1000 строк = 21060. Ожидается ли это? Я новичок в использовании базы данных на Python. Пожалуйста, помогите.

Ответ №2:

Другое потенциальное решение (вероятно, медленнее, чем у ForceBru, но, эй, было забавно кодировать XD) — использовать потоки для значительного сокращения времени:

 from queue import Queue
from threading import Thread

q = Queue()
number_of_threads = 3


def get_customer_id():
    while True:
        customer_name = q.get()
        with open('Master.txt', 'r') as f:
            for line in f.readlines():
                if customer_name.strip() in line:
                    print(line.strip())
                    break
        q.task_done()


with open('Slave.txt', 'r') as f:
    for line in f.readlines():
        q.put(line)

for i in range(number_of_threads):
    new_thread = Thread(target=get_customer_id)
    new_thread.setDaemon(True)
    new_thread.start()


print('main thread waiting')
q.join()
print('done')
  

Вы могли бы увеличить количество потоков, скажем, до 100-200, возможно, и позволить им работать дальше! Это будет очень дорого с точки зрения вычислений, хотя, поскольку у вас максимальное количество итераций составляет почти 125 000 000 000 000 в худшем случае. Однако это довольно преувеличено, потому что break оператор должен сократить значительное количество этих итераций. И если он выполняется в 100 потоках, то вы, возможно, можете разделить это число на 100 после того, как уже уменьшили его по сравнению с break (предполагая, что вы еще не максимизируете загрузку процессора, и в этом случае многопроцессорность была бы лучше). Все еще, хотя и МНОГО вычислений с этим методом.

По сути, это делает то же самое, что и ваш исходный скрипт, но запускает его во много раз быстрее за счет разделения и завоевания!

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

1. Ребята, я попробовал то, что было предложено ForceBru, но я все еще вижу, что обработка файла занимает огромное время выполнения. Я сообщил время для каждых 1000 поисковых строк. отладка: запись в выходной файл 1000 строк, время, затрачиваемое на запись 1000 строк = 18102 отладка: запись в выходной файл 2000 строк, время, затрачиваемое на запись 1000 строк = 19825 отладка: запись в выходной файл 3000 строк, время, затрачиваемое на запись 1000 строк = 21060. Ожидается ли это? Я новичок в использовании базы данных на Python. Пожалуйста, помогите.

2. @user3770616 почему бы тогда не попробовать использовать базу данных и поточные методы вместе взятые? Я не уверен, что sql может быть потоковым, но если это так, это кажется хорошим решением

3. @user3770616 Я также не разбираюсь в базах данных, поэтому я не уверен, ожидаемо ли это время. Вы говорите, что для вычисления 1000 строк требуется 18 000 секунд, т.е. по 18 секунд на строку? Это не может быть необоснованным для поиска по 5 000 000 строк…

4. При тех темпах, с которыми продвигается поиск по 5 миллионам строк, это заняло бы 3 года. Должен быть какой-то лучший способ сделать это. SQL с потоковой обработкой может быть одним из них. Есть еще идеи или соображения? Спасибо.

5. @akashaya Мне жаль. Кроме их объединения, я не могу придумать ничего другого, кроме обновления вашего оборудования. Я бы попробовал сначала объединить их, потому что вы потенциально могли бы увеличить 18 секунд на строку до .18 секунд на строку при многопроцессорной обработке или многопоточности (в зависимости от ввода-вывода и загрузки процессора). Если бы вы могли достичь такой скорости, вычисления заняли бы всего 10 дней вместо 1000, хотя