【问题标题】:splitting a large tab delimited file in python在python中拆分一个大的制表符分隔文件
【发布时间】:2013-02-07 18:11:30
【问题描述】:

我有一个包含大约 140 万行和 50 列的大型制表符分隔文件。在对文件中包含的数据进行任何操作之前,我想将这个大文件拆分成大约几千个小文件。我的文件的第一列包含位置信息,我希望每个较小的文件都是基于此信息的特定间隔。在单独的列表中,我有要分割较大文件的每个间隔的开始和停止。这是我执行此操作的代码部分,开始和停止位置包含在名为 start_L 和 stop_L 的列表中:

for i in range(len(id)):
   out1=((file%s.txt)%(id[i]))
   table=open('largefile.tsv',"r")
   start=int(start_L[i])
   stop=int(stop_L[i])
   table.next()
   temp_out=open(out1,"w")
   reader=csv.reader(table,delimiter="\t")
   for line in reader:
       if int(line[0]) in range(start,stop):
           for y in line:
               temp_out.write(("%s\t")%(y))
           temp_out.write("\n")
    else:
        if int(line[0]) > stop:
            break
        else:
            pass
print "temporary file..." , id[i]

上面的代码实现了我想要的,但是非常慢。它可以在几分钟内处理前一百个左右的时间间隔,但随着每个时间间隔的过去,它会以指数方式变慢,因此需要几天时间才能运行。有没有更快或更有效的方法来做到这一点?我认为问题在于它必须扫描整个文件以在每次循环中找到指定间隔内的位置。

【问题讨论】:

  • 是按位置信息排序的 CSV 文件(即第 1 列)?
  • 顺便说一句,你不应该使用id作为变量,因为这会覆盖Python的内置id函数。
  • 是的...按位置排序

标签: python split large-files csv


【解决方案1】:

您的程序会随着时间的推移而变慢的原因是您一遍又一遍地为每个输出文件重新读取 CSV 文件。随着您查看的范围在 CSV 文件中向下移动,您需要为每个输出文件读取越来越多的数据(其中大部分您会跳过)。因此,性能呈指数级下降。

您需要重新组织代码,以便仅按顺序读取 CSV 一次,并在循环中挑选出感兴趣的范围(并将它们写入文件)。这只有在 CSV 按范围排序(您说是)并且您的 start_L/stop_L 也相应排序的情况下才有可能。

【讨论】:

  • @abovezero:if int(line[0]) in range(start,stop): 的效率也相当低,应该用更快的东西代替,比如if start <= int(line[0]) < stop:
【解决方案2】:

在大多数情况下,上面提供的解决方案对我有帮助,但由于我的输入没有 # 行,我不得不进行以下更改。

    table=fileinput.input('largefile.csv',mode="r")
    #
    #
    #
         if fileinput.lineno() >= stop :

我的文件是 |以大约 600k 行和大约 120MB 大小分隔;整个文件在几秒钟内就被拆分了。

【讨论】:

    【解决方案3】:

    好的,我试图保持您代码的精神。它只遍历大文件一次,它不需要通过 csv 模块解析行,因为您只是在写入期间重新加入它们。

    id=("a","b")
    start_L=(1,15)
    stop_L=(16,40)
    
    i=0
    table=open('largefile.tsv',"r")
    out1=(("file%s.txt")%(id[i]))
    temp_out=open(out1,"w")
    
    # start iterating through the file 
    for line in table:
         stop=int(stop_L[i])
    
         # Split the line into a position piece, and a 
         # throw away variable based upon the 1st tab char
         position,the_rest= line.split("\t",1)
    
         # I'm ignoring start as you mentioned it was sorted in the file
         if int(position) >= stop :
               # Close the current file
               temp_out.close()
    
               # Increment index so file name is pulled from id properly
               # If the index is past the length of the id list then 
               # break otherwise open the new file for writing
               i += 1  
               if (i < len(id)):
                 out1=(("file%s.txt")%(id[i]))
                 temp_out=open(out1,"w")
               else:
                 break 
    
         temp_out.write(line)
    

    我的测试文件行看起来像

    1       1a      b       c       d       e
    2       2a      b       c       d       e
    3       3a      b       c       d       e
    

    这可以根据您的具体数据进行相当多的简化,但我希望它至少能给您一个开始。

    【讨论】:

    • 恕我直言,这可能是一个更好、更完整的答案,如果它至少表明它在做什么,也许还有为什么它比 OP 的方法更快(如果它不明显的话)。跨度>
    • @martineau,我在代码中添加了一些 cmets,并将我的第一条评论的内容也移到了答案中。如果您认为其他内容有用,请告诉我。
    • 这很好用。感谢添加 cmets 来解释每个步骤的作用。
    • @abovezero,我刚刚意识到,它会跳过大文件的最后一行。您必须稍微调整一下才能选择该阵容
    • @JamesThompson:抱歉,虽然你确实改进了你的答案,但当你考虑到所有不必要的工作将做 140 万次时,我不愿意支持仍然如此低效的东西。也许你应该放弃一点 OP 代码的精神,让程序更像一个“Pythonista”。 ;-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 1970-01-01
    • 2011-09-06
    • 2013-02-07
    • 2017-07-04
    相关资源
    最近更新 更多