读取 .txt 文件内存效率 Python
Read a .txt file memory efficient in Python
我正在阅读一些 .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="|"))
但这几乎是 RAM 内存中文件大小的 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))
所以我想我需要一次将所有列表记入内存。
将数据读取到 list
意味着您正在将所有行加载并保存到内存中。您可以做的是通过迭代 csv.reader() 来逐行迭代,如记录:
csv.reader(csvfile, dialect='excel', **fmtparams)
Return a reader object which will iterate over lines in the given csvfile. csvfile can be any object which supports the iterator protocol and returns a string each time its next() method is called... Each row read from the csv file is returned as a list of strings.
with open('../Results/DIMP_1120.txt', 'r') as f:
for row in csv.reader(f, delimiter="|")):
# Process the current line
这样做的缺点是您一次只能访问 1 行。但我相信,如果您不想一次将所有内容加载到内存中,这可能是唯一的方法。您只需要重新设计逻辑来处理每行需要完成的所有事情。
如果您可以一次处理一行数据而不存储每一行,例如
for row in csv.reader(f, delimiter="|"):
这样做;这是显着减少峰值内存使用的唯一方法。
否则,你能做的最好的就是在阅读时将行存储格式从 list
转换为 tuple
,这应该至少节省一点内存(如果 csv.reader
更多默认情况下不会截断过度分配 list
),因为 tuple
不会过度分配,并且它们存储数据与 Python object header (没有过度分配或额外的分配器 round-off 开销),而 list
的 header 只是添加一个指向单独分配的内存的指针(它会过度分配并产生两倍的 round-off 开销);对于大小为 2 的动态分配 list
(例如,在 CPython 3.9 中,解包概括的行为类似于顺序追加,[*(0, 1)]
),容器开销可以从 120 字节下降到 56 字节(可能此外,由于分配器 round-off 错误对 sys.getsizeof
不可见,并且 list
支付两次,tuple
仅支付一次)只需转换为 tuple
,可以对数百万这样的行产生影响。最有效的转换方式是更改:
DIMP_1120 = list(csv.reader(f, delimiter="|"))
至:
DIMP_1120 = list(map(tuple, csv.reader(f, delimiter="|")))
map
在 Python 3 上延迟操作,因此每一行将被读取为 list
,转换为 tuple
,并存储在外部 list
在阅读下一个之前;它不会涉及将整个输入同时存储为 list
s 和 tuple
s,即使是片刻。如果您的基础数据有一些字段可以转换 up-front 为更有效的存储类型(例如 int
),则 list
可以转换字段并将它们打包为 [=17] =]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,)]
从 csv.reader
解压 list
,将相关字段转换为 int
,并重新打包为 tuple
。
Side-note:确保将 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
;一个用于每个输入文件,一个用于连接前两个文件,一个用于连接所有文件三个文件,一个新文件用于所有三个文件的排序串联),包含所有数据,并且它是根据 get-go.
排序创建的
我会注意到,这可能在命令行中做得更好,至少在 *NIX-like 系统上,sort
命令行实用程序知道如何按字段对大文件进行排序, 自动溢出到磁盘以避免一次在内存中存储太多。它可以在 Python 中完成,但它会更丑陋(除非有一些我不知道的 PyPI 模块可以做到这一点)。
我正在阅读一些 .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="|"))
但这几乎是 RAM 内存中文件大小的 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))
所以我想我需要一次将所有列表记入内存。
将数据读取到 list
意味着您正在将所有行加载并保存到内存中。您可以做的是通过迭代 csv.reader() 来逐行迭代,如记录:
csv.reader(csvfile, dialect='excel', **fmtparams)
Return a reader object which will iterate over lines in the given csvfile. csvfile can be any object which supports the iterator protocol and returns a string each time its next() method is called... Each row read from the csv file is returned as a list of strings.
with open('../Results/DIMP_1120.txt', 'r') as f:
for row in csv.reader(f, delimiter="|")):
# Process the current line
这样做的缺点是您一次只能访问 1 行。但我相信,如果您不想一次将所有内容加载到内存中,这可能是唯一的方法。您只需要重新设计逻辑来处理每行需要完成的所有事情。
如果您可以一次处理一行数据而不存储每一行,例如
for row in csv.reader(f, delimiter="|"):
这样做;这是显着减少峰值内存使用的唯一方法。
否则,你能做的最好的就是在阅读时将行存储格式从 list
转换为 tuple
,这应该至少节省一点内存(如果 csv.reader
更多默认情况下不会截断过度分配 list
),因为 tuple
不会过度分配,并且它们存储数据与 Python object header (没有过度分配或额外的分配器 round-off 开销),而 list
的 header 只是添加一个指向单独分配的内存的指针(它会过度分配并产生两倍的 round-off 开销);对于大小为 2 的动态分配 list
(例如,在 CPython 3.9 中,解包概括的行为类似于顺序追加,[*(0, 1)]
),容器开销可以从 120 字节下降到 56 字节(可能此外,由于分配器 round-off 错误对 sys.getsizeof
不可见,并且 list
支付两次,tuple
仅支付一次)只需转换为 tuple
,可以对数百万这样的行产生影响。最有效的转换方式是更改:
DIMP_1120 = list(csv.reader(f, delimiter="|"))
至:
DIMP_1120 = list(map(tuple, csv.reader(f, delimiter="|")))
map
在 Python 3 上延迟操作,因此每一行将被读取为 list
,转换为 tuple
,并存储在外部 list
在阅读下一个之前;它不会涉及将整个输入同时存储为 list
s 和 tuple
s,即使是片刻。如果您的基础数据有一些字段可以转换 up-front 为更有效的存储类型(例如 int
),则 list
可以转换字段并将它们打包为 [=17] =]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,)]
从 csv.reader
解压 list
,将相关字段转换为 int
,并重新打包为 tuple
。
Side-note:确保将 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
;一个用于每个输入文件,一个用于连接前两个文件,一个用于连接所有文件三个文件,一个新文件用于所有三个文件的排序串联),包含所有数据,并且它是根据 get-go.
我会注意到,这可能在命令行中做得更好,至少在 *NIX-like 系统上,sort
命令行实用程序知道如何按字段对大文件进行排序, 自动溢出到磁盘以避免一次在内存中存储太多。它可以在 Python 中完成,但它会更丑陋(除非有一些我不知道的 PyPI 模块可以做到这一点)。