如何在python中解析很大的文件?

时间:2018-11-23 06:31:40

标签: python bigdata

我有一个很大的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多个小时,并且仍在工作。我不知道如何加快这一步,以及是否还有其他方法可以解析,例如文件

4 个答案:

答案 0 :(得分:3)

这里有几个问题:

  • 在Python 2中使用if ids[0] not in evalIDs.keys()测试密钥需要永远,因为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]]

魔术evalIDs[ids[0]].append(ids[1]]创建一个list(如果尚不存在)。无论python版本如何,它都可移植且非常快速,并保存了if

我认为使用默认库可能不会更快,但是熊猫解决方案可能会更快。

答案 1 :(得分:2)

一些建议:

使用defaultdict(list)来代替自己创建内部列表,或者使用dict.setdefault()来。

dict.setfdefault()每次都会创建defautvalue,这就是时间燃烧器-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://stackoverflow.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']}

答案 2 :(得分:1)

  1. evalIDs更改为collections.defaultdict(list)。您可以避免使用if检查其中是否有钥匙。
  2. 考虑使用split(1)在外部拆分文件,甚至使用读取偏移量在python内部拆分文件。然后使用multiprocessing.pool并行化加载。

答案 3 :(得分:0)

也许您可以使其更快一些;更改它:

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次。