#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 за предоставление кода. Есть ли способ, которым я могу получить и имя, и идентификатор в файл вместо записи в оболочке.
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, хотя