#python #list #txt
Вопрос:
Я читаю некоторые файлы .txt в виде списков, используя этот метод:
with open('../Results/DIMP_1120.txt', 'r') as f:
DIMP_1120 = list(csv.reader(f, delimiter="|"))
with open('../Results/DIMP_1121.txt', 'r') as f:
DIMP_1121 = list(csv.reader(f, delimiter="|"))
with open('../Results/DIMP_1122.txt', 'r') as f:
DIMP_1122 = list(csv.reader(f, delimiter="|"))
Но это занимает почти в 10 раз больше размера файла в оперативной памяти.
Есть ли эффективный способ прочитать его?
После этого я добавлю эти списки и отсортирую их.
big_list = DIMP_1120 DIMP_1121 DIMP_1122
#Order all lists by *Sorter (Row_id2)
from operator import itemgetter
big_list= sorted(big_list, key=itemgetter(0))
Поэтому, я думаю, мне нужно запомнить все списки сразу.
Комментарии:
1. Вам это нужно как
list
все сразу? Если вы можете обрабатывать его строка за строкой, ему потребуется память только для строки за раз (ну, две строки за раз, учитывая, как работает итерация Python, но достаточно близко). В противном случае, да, накладные расходы Pythonstr
составляют ~49 байт за штуку, плюс накладные расходы каждойlist
оболочки, поэтому, если большинство полей короткие, накладные расходы по отношению к данным будут довольно высокими.2. Привет, спасибо за ответ. Да, мне нужно все это сразу, потому что я добавляю в другие списки и сортирую их все. Чтение в виде рассола вместо txt может помочь?
3. Маринование не сильно поможет, если базовые данные не могут быть сохранены с использованием более эффективных типов памяти (например, хранение и восстановление необработанных
int
). Я добавил примечание о том , как вы можете воспользоваться этим преимуществом, все еще используяcsv
, просто выполняя преобразования типов по мере загрузки.4. «добавление в другие списки и сортировка всех из них» не обязательно требует чтения всех сразу. Мы могли бы помочь вам лучше, если бы знали больше деталей.
5. Можете ли вы показать несколько строк данных? Сколько у вас файлов? Насколько велик каждый файл? Что вы делаете с последним большим отсортированным списком?
Ответ №1:
Если вы можете обрабатывать данные по строке за раз, не сохраняя каждую строку, например
for row in csv.reader(f, delimiter="|"):
сделайте это; это единственный способ значительно сократить пиковое использование памяти.
В противном случае, лучшее, что вы можете сделать, это преобразовать строку формата хранения От list
До tuple
, как Вы читаете, которая должна сохранить хоть немного памяти (больше, если бы csv.reader
не усек превышение доступности list
делает по умолчанию), а tuple
с не overallocate, и они хранят данные, встроенной в объект Python заголовка (без сверхвыделение или дополнительный распределитель округление надбавок), а list
с заголовка просто добавляет указатель на отдельно выделенной памяти (которая overallocates и за два округление надбавок); для динамически выделяемых list
размер 2 (е.g. в CPython 3.9, где обобщения распаковки ведут себя как последовательные добавления, [*(0, 1)]
) накладные расходы контейнера могут снизиться со 120 байт до 56 байт (возможно, больше , поскольку ошибка округления распределителя не видна sys.getsizeof
и list
оплачивается дважды, tuple
только один раз) просто путем преобразования в tuple
, что может иметь значение для миллионов таких строк. Наиболее эффективным средством его преобразования было бы изменение:
DIMP_1120 = list(csv.reader(f, delimiter="|"))
Для:
DIMP_1120 = list(map(tuple, csv.reader(f, delimiter="|")))
map
работает лениво на Python 3, поэтому каждая строка будет прочитана как a list
, преобразована в a tuple
и сохранена во внешней list
до того , как будет прочитана следующая; это не потребует одновременного хранения всего ввода в виде list
s и tuple
s, даже на мгновение. Если в ваших базовых данных есть некоторые поля, которые могут быть преобразованы заранее в более эффективно хранимый тип (например int
), list
понимание того, что поля преобразуются и упаковываются как tuple
s вместо list
s, может дать больше, например, для четырех полей в строке, последние три из которых логически int
являются s, вы могли бы сделать:
DIMP_1120 = [(a, int(b), int(c), int(d)) for a, b, c, d in csv.reader(f, delimiter="|")]
# If you might have some empty/missized rows you wish to ignore, an if check can discard
# wrong length lists; a nested "loop" over the single item can unpack after checking:
DIMP_1120 = [(a, int(b), int(c), int(d)) for lst in csv.reader(f, delimiter="|")
if len(lst) == 4
for a, b, c, d in (lst,)]
распаковка list
s из csv.reader
, преобразование соответствующих полей в int
и повторная упаковка как a tuple
.
Примечание: Обязательно передайте newline=""
(пустую строку) вашему open
вызову; csv
модуль требует этого для правильной обработки новых строк из разных диалектов CSV.
Обновление: Чтение в отдельные list
s, затем объединение, а затем сортировка увеличивает пиковые внешние list
накладные расходы с пропорциональности количеству строк до пропорциональности ~2,66 раза количеству строк (при условии, что все файлы имеют одинаковый размер). Вы можете избежать этих накладных расходов, изменив:
with open('../Results/DIMP_1120.txt', 'r') as f:
DIMP_1120 = list(csv.reader(f, delimiter="|"))
with open('../Results/DIMP_1121.txt', 'r') as f:
DIMP_1121 = list(csv.reader(f, delimiter="|"))
with open('../Results/DIMP_1122.txt', 'r') as f:
DIMP_1122 = list(csv.reader(f, delimiter="|"))
big_list = DIMP_1120 DIMP_1121 DIMP_1122
#Order all lists by *Sorter (Row_id2)
from operator import itemgetter
big_list= sorted(big_list, key=itemgetter(0))
Для:
from itertools import chain
with open('../Results/DIMP_1120.txt', 'r') as f1,
open('../Results/DIMP_1121.txt', 'r') as f2,
open('../Results/DIMP_1122.txt', 'r') as f3:
ALL_DIMP = chain.from_iterable(csv.reader(f, delimiter="|")
for f in (f1, f2, f3))
big_list = sorted(map(tuple, ALL_DIMP), key=itemgetter(0))
Только один list
из них когда-либо создавался (в вашем исходном коде было шесть list
s; по одному для каждого входного файла, один для объединения первых двух файлов, один для объединения всех трех файлов и новый для отсортированного объединения всех трех файлов), содержащий все данные, и он создается отсортированным с самого начала.
Я отмечу, что это может быть лучше сделано в командной строке, по крайней мере, в системах, подобных *NIX, где утилита sort
командной строки знает, как сортировать огромные файлы по полю, с автоматическим выводом на диск, чтобы избежать одновременного хранения слишком большого объема в памяти. Это можно было бы сделать на Python, но это было бы уродливее (если только для этого не существует какого-нибудь модуля PyPI, о котором я не знаю).
Комментарии:
1. небольшой эксперимент с накладными расходами.
2. @don’talkjustcode: По-прежнему существует нелинейная модель роста; попробуйте ее,
'a,b,c,d,e'
и использование памяти дляcsv.reader
вывода данных подскочит с 88 байт до 120 (tuple
затем снова снизится до 80). Похоже, разница в том, какsplit
распределяется (я случайно выбрал пример, который распределяет больше, чемlist
здание по умолчанию; когда оно неmaxsplit
предусмотрено, оно резервирует место для 12 элементов спереди и не обрезается в конце). Таким образом, преобразование вtuple
может сэкономить память, но не так сильно, как показывало мое сравнение игрушек.3. Спасибо. Это улучшило использование памяти почти на 30%.
4. Я обновил свой пример, чтобы избежать
.split
дополнительных превышение доступности (в то же время, умышленно, показав лучший сценарий, гдеlist
расходы более чем в два разаtuple
над головой; на практике всеtuple
может гарантировать , чтоtuple(somelist)
будет потреблять не менее 16 байт меньше, чем вызовlist(somelist)
, который в с CPython 3.9 не включает любое превышение доступности нагрузку на копиюsomelist
(его размеры точно к элементам данного).tuple
по-прежнему экономит 16 байт (плюс потеря округления распределителя), избегая хранения указателя и отдельногоssize_t
поля емкости.
Ответ №2:
Считывание данных в a list
означает, что вы загружаете и сохраняете все строки в памяти. Вместо этого вы можете выполнить итерацию по строкам одну за другой, выполнив итерацию по csv.reader() вместо этого, что, как описано в документе:
csv.reader(csv-файл, диалект=’excel’, **fmtparams)
Возвращает объект чтения, который будет повторять строки в данном csv-файле. csvfile может быть любым объектом, который поддерживает протокол итератора и возвращает строку при каждом вызове метода next ()… Каждая строка, считанная из csv-файла, возвращается в виде списка строк.
with open('../Results/DIMP_1120.txt', 'r') as f:
for row in csv.reader(f, delimiter="|")):
# Process the current line
Недостатком этого является то, что у вас есть доступ только к 1 строке одновременно. Но я считаю, что если вы не хотите загружать все сразу в память, это может быть единственным способом. Вам просто нужно изменить свою логику, чтобы обрабатывать все, что нужно сделать в каждой строке.
Комментарии:
1. Привет, спасибо за ответ. Да, мне нужно все это сразу, потому что я добавляю в другие списки и сортирую их все. Чтение в виде рассола вместо txt может помочь?