如何在python中解析非常大的文件?
Posted
技术标签:
【中文标题】如何在python中解析非常大的文件?【英文标题】:How to parse very big files in python? 【发布时间】:2018-11-23 06:31:39 【问题描述】:我有一个非常大的 tsv 文件:1.5 GB。我想解析这个文件。我使用以下功能:
def readEvalFileAsDictInverse(evalFile):
eval = open(evalFile, "r")
evalIDs =
for row in eval:
ids = row.split("\t")
if ids[0] not in evalIDs.keys():
evalIDs[ids[0]] = []
evalIDs[ids[0]].append(ids[1])
eval.close()
return evalIDs
这需要 10 多个小时,它仍在工作。我不知道如何加快这一步,是否还有其他方法可以解析,例如文件
【问题讨论】:
可能与***.com/questions/17444679/reading-a-huge-csv-file重复 什么操作系统? 我正在使用 130 GB 作为 ram 的 linux 服务器。 @GuillaumeJacquenot 不,一点也不。 OP 没有先读这些行。 @bib 退一步,你打算用字典eval
做什么?
【参考方案1】:
这里有几个问题:
用if ids[0] not in evalIDs.keys()
测试键在python 2 中需要永远,因为keys()
是list
。 .keys()
无论如何都很少有用。更好的方法已经是if ids[0] not in evalIDs
,但是,但是...
为什么不改用collections.defaultdict
?
为什么不使用csv
模块?
覆盖 eval
内置(好吧,看看它有多危险,这并不是一个真正的问题)
我的建议:
import csv, collections
def readEvalFileAsDictInverse(evalFile):
with open(evalFile, "r") as handle:
evalIDs = collections.defaultdict(list)
cr = csv.reader(handle,delimiter='\t')
for ids in cr:
evalIDs[ids[0]].append(ids[1]]
如果 list
尚不存在,魔法 evalIDs[ids[0]].append(ids[1]]
会创建一个 list
。无论python版本如何,它都是可移植的且非常快,并保存if
我认为使用默认库不会更快,但 pandas 解决方案可能会。
【讨论】:
【参考方案2】:一些建议:
使用defaultdict(list)
而不是自己创建内部列表或使用dict.setdefault()
。
dict.setfdefault()
每次都会创建默认值,这是一个时间燃烧器 - defautldict(list)
没有 - 它已被优化:
from collections import defaultdict
def readEvalFileAsDictInverse(evalFile):
eval = open(evalFile, "r")
evalIDs = defaultdict(list)
for row in eval:
ids = row.split("\t")
evalIDs[ids[0]].append(ids[1])
eval.close()
如果您的密钥是有效的文件名,您可能需要调查awk
以获得更高的性能,然后在 python 中执行此操作。
类似
awk -F $'\t' 'print > $1' file1
将更快地创建您的拆分文件,您可以简单地使用以下代码的后半部分从每个文件中读取(假设您的密钥是有效的文件名)来构建您的列表。 (署名:here) - 您需要使用os.walk
或类似方式获取您创建的文件。文件中的每一行仍然是制表符分隔的,并在前面包含 ID
如果您的密钥本身不是文件名,请考虑将不同的行存储到不同的文件中,并且只保留 key,filename
的字典。
拆分数据后,再次将文件加载为列表:
创建测试文件:
with open ("file.txt","w") as w:
w.write("""
1\ttata\ti
2\tyipp\ti
3\turks\ti
1\tTTtata\ti
2\tYYyipp\ti
3\tUUurks\ti
1\ttttttttata\ti
2\tyyyyyyyipp\ti
3\tuuuuuuurks\ti
""")
代码:
# f.e. https://***.com/questions/295135/turn-a-string-into-a-valid-filename
def make_filename(k):
"""In case your keys contain non-filename-characters, make it a valid name"""
return k # assuming k is a valid file name else modify it
evalFile = "file.txt"
files =
with open(evalFile, "r") as eval_file:
for line in eval_file:
if not line.strip():
continue
key,value, *rest = line.split("\t") # omit ,*rest if you only have 2 values
fn = files.setdefault(key, make_filename(key))
# this wil open and close files _a lot_ you might want to keep file handles
# instead in your dict - but that depends on the key/data/lines ratio in
# your data - if you have few keys, file handles ought to be better, if
# have many it does not matter
with open(fn,"a") as f:
f.write(value+"\n")
# create your list data from your files:
data =
for key,fn in files.items():
with open(fn) as r:
data[key] = [x.strip() for x in r]
print(data)
输出:
# for my data: loaded from files called '1', '2' and '3'
'1': ['tata', 'TTtata', 'tttttttata'],
'2': ['yipp', 'YYyipp', 'yyyyyyyipp'],
'3': ['urks', 'UUurks', 'uuuuuuurks']
【讨论】:
为什么要创建defaultdict
并测试密钥?
@Jean-FrançoisFabre 复制和粘贴错误 - 感谢您指出【参考方案3】:
-
将
evalIDs
更改为collections.defaultdict(list)
。您可以避免使用if
来检查是否存在密钥。
考虑在外部使用split(1)
或什至在python 内部使用读取偏移量来分割文件。然后使用multiprocessing.pool
并行加载。
【讨论】:
您能否提供更多详细信息。我对 multiprocessing.pool 没有任何想法 如果 I/O 是瓶颈,则并行加载不会有任何好处 如果 I/O 是瓶颈,那么是的,它不会有太大的好处,但除了每个人都建议的defaultdict
之外,这是我能想到的唯一值得尝试的另一件事。
@bib 文件中所有行的长度都一样吗?【参考方案4】:
也许,你可以让它更快一些;改变它:
if ids[0] not in evalIDs.keys():
evalIDs[ids[0]] = []
evalIDs[ids[0]].append(ids[1])
到
evalIDs.setdefault(ids[0],[]).append(ids[1])
第一种解决方案在“evalID”字典中搜索 3 次。
【讨论】:
setdefault
比默认字典慢。 timeit.timeit(lambda : d.setdefault('x',[]).append(1))
报告0.4583683079981711
和timeit.timeit(lambda : c['x'].append(1))
报告0.28720847200020216
其中d
是
和c
是collections.defaultdict(list)
。所有答案都建议如此。你为什么选择这个作为正确的?这里的解决方案不如提到的其他解决方案。
我无法测量显着差异(Python 3.7.1),但 OP 应该测量它。
在我的测量中,defaultdict 的速度大约是 setdefault 的两倍。 (3.5.3),我认为这是合理的,因为 setdefault 每次调用它时都会评估它的参数(每次都会创建一个新的空列表)。以上是关于如何在python中解析非常大的文件?的主要内容,如果未能解决你的问题,请参考以下文章