首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何有效地打开30 it的文件并处理其中的部分内容,而不会减慢速度?

如何有效地打开30 it的文件并处理其中的部分内容,而不会减慢速度?
EN

Stack Overflow用户
提问于 2019-04-23 08:08:29
回答 3查看 274关注 0票数 10

我有一些大文件(超过30 on ),其中包含一些信息,我需要对其进行一些计算,比如求平均值。我提到的片段是文件的片段,我知道每个片段的开始行号和后面的行数。

因此,我有一个字典,键作为开始行号,值作为后续行的计数,我使用这个字典遍历文件并对其进行切片。对于每个切片,我创建一个表,进行一些转换和平均,创建一个新表并将其转换为字典。我使用islice进行切片,使用pandas数据帧从每个切片创建表。

然而,随着时间的推移,过程变得越来越慢,甚至切片的大小也大致相同。第一个1k切片-在1h第二个1k切片中处理-在4h第三个1k切片中处理-在8h第二个1k切片中处理-在17h内处理,我正在等待几天来完成这些过程。

现在我在一台windows10机器上做这件事,1tb固态硬盘,32 GB内存。之前我也在Linux机器(ubuntu18.4)上测试过,它有250 8gb的SSD和8 8gb的ram +8 8gb的虚拟ram。两者的结果大同小异。

我在windows中注意到的是,17%的CPU和11%的内存被使用了,但是磁盘的使用率是100%。我不完全知道磁盘使用率意味着什么,也不知道如何改善它。

作为代码的一部分,我在Linux上工作时也将数据导入到mongodb中,我认为这可能是因为mongodb中的索引。但是当我打印处理时间和导入时间时,我注意到几乎所有的时间都花在处理上,导入只需要几秒钟。

同样为了争取时间,我现在正在一台更强大的windows机器上做处理部分,并将文档写成txt文件。我希望在磁盘上写入会稍微减慢进程,但txt文件大小不超过600kb。

下面是我如何读取该文件的代码片段:

代码语言:javascript
复制
with open(infile) as inp:
    for i in range(0,len(seg_ids)): 
        inp.seek(0)
        segment_slice = islice(inp,list(seg_ids.keys())[i], (list(seg_ids.keys())[i]+list(seg_ids.values())[i]+1)) 
        segment = list(segment_slice)

        for _, line in enumerate(segment[1:]):
            #create dataframe and perform calculations

所以我想知道是否有一种方法可以缩短处理时间。我认为我的代码从开始读取每个切片的整个文件,并且通过文件的结尾读取时间变得越来越长。

需要注意的是,由于时间限制,我从必须首先处理的最重要的切片开始。因此,其余部分将是文件上的更多随机切片。所以解决方案应该适用于随机切片,如果有的话(我希望)。

我在脚本方面没有经验,所以如果我问了一个愚蠢的问题,请原谅我,但我真的找不到任何答案。

EN

回答 3

Stack Overflow用户

发布于 2019-04-23 08:34:34

有几件事浮现在脑海中。

首先,如果你把数据带到一个pandas DataFrame中,有一个用于导入大数据的'chunksize‘参数。它允许您处理/转储您需要/不需要的内容,同时证明诸如df.describe之类的信息,这些信息将为您提供摘要统计信息。

此外,我还听说了一些关于dask的很棒的事情。它是一个可扩展的平台,通过并行、多核、多机处理,几乎和使用pandas和numpy一样简单,只需要很少的资源管理。

票数 1
EN

Stack Overflow用户

发布于 2020-12-09 19:15:48

使用pandas或dask,并注意read_csv()的选项。主要: chunck_size,nrow,skiprows,usecols,engine (使用C),low_memory,memory_map

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html

票数 1
EN

Stack Overflow用户

发布于 2020-12-15 23:51:37

这里的问题是,您正在从文件的开头逐行多次重新读取一个巨大的、未索引的文件。难怪这要花上几个小时。

代码中的每个islice都是从文件的开头开始的--每次都是这样--甚至在文件到达感兴趣的数据的开头之前读取和丢弃文件中的所有行。这是非常缓慢和低效的。

解决方案是为该文件创建一个穷人的索引,然后读取每个分片的较小块。

让我们创建一个测试文件:

代码语言:javascript
复制
from pathlib import Path

p=Path('/tmp/file')

with open(p, 'w') as f:
    for i in range(1024*1024*500):
        f.write(f'{i+1}\n')
        
print(f'{p} is {p.stat().st_size/(1024**3):.2f} GB')        

这样就创建了一个大约4.78 GB的文件。没有30 GB那么大,但如果你考虑不周到,它就会变得很慢。

现在尝试使用Unix实用程序wc逐行读取整个文件,以计算总行数(一般来说,wc是计算行数的最快方法):

代码语言:javascript
复制
$ time wc -l /tmp/file
 524288000 file

real    0m3.088s
user    0m2.452s
sys 0m0.636s

将其与Python3逐行读取文件并打印总数的速度进行比较:

代码语言:javascript
复制
$ time python3 -c 'with open("file","r") as f: print(sum(1 for l in f))'
524288000

real    0m53.880s
user    0m52.849s
sys 0m0.940s

Python逐行读取文件的速度几乎是wc的18倍。

现在做进一步的比较。查看Unix实用程序tail打印文件最后n行的速度:

代码语言:javascript
复制
$ time tail -n 3 file 
524287998
524287999
524288000

real    0m0.007s
user    0m0.003s
sys 0m0.004s

在到达文件的最后三行时,tail实用程序比wc快445倍(大约比Python快8,000倍),因为它使用了一个窗口索引缓冲区。也就是说,tail在文件末尾读取一定数量的字节,然后从它读取的缓冲区中获取最后的n行。

可以对您的应用程序使用相同的tail方法。

考虑这张照片:

您正在使用的方法相当于读取该机架上的每一盘磁带,以查找仅位于中间两盘磁带上的数据--并一遍又一遍地重复这一过程……

在20世纪50年代(照片的时代),每盘磁带都被粗略地索引了它所持有的内容。计算机会调用机架中的特定磁带,而不是机架中的所有磁带。

您的问题的解决方案(在监督中)是构建一个类似磁带的索引方案:

  1. 将遍历30 GB的文件一次,并通过数据块的起始行号创建子块的索引。将每个子块看作大约一盘磁带(除非它一直运行到使用int.seek(0)的end...)
  2. Instead每次读取之前,您将查找包含感兴趣的行号的块(就像tail一样),然后使用islice偏移量调整到该块的起始行号与文件开始处的关系。
  3. 与他们在50年代和60年代必须做的事情相比,您有一个巨大的优势:您只需要计算开始块,因为您可以访问整个剩余的文件。1950年代的磁带索引要求磁带x,y,z,...读取超过一盘磁带所能容纳的数据量。您只需要找到包含interest.
  4. BTW,起始行号的x,因为this type的每个IBM磁带大约有3MB,所以30 GB的文件将超过1000万个这样的磁带……

如果实现正确(这并不是很难做到),它将使读取性能提高100倍或更多。

通过行偏移量构建一个有用的文本文件索引可能就像下面这样简单:

代码语言:javascript
复制
def index_file(p, delimiter=b'\n', block_size=1024*1024):
    index={0:0}
    total_lines, cnt=(0,0)
    with open(p, 'rb') as f:
        while buf:=f.raw.read(block_size):
            cnt=buf.count(delimiter)
            idx=buf.rfind(delimiter)
            key=cnt+total_lines
            index[key]=f.tell()-(len(buf)-idx)+len(delimiter)
            total_lines+=cnt 
    
    return index

# this index is created in about 4.9 seconds on my computer...
# with a 4.8 GB file, there are ~4,800 index entries

它构建了一个索引,该索引将起始行号(在该块中)与文件开头的字节偏移量相关联:

代码语言:javascript
复制
>>> idx=index_file(p)
>>> idx
{0: 0, 165668: 1048571, 315465: 2097150, 465261: 3145722,
...
 524179347: 5130682368, 524284204: 5131730938, 524288000: 5131768898}

然后,如果你想访问lines[524179347:524179500],你不需要阅读4.5G的内容;你可以直接使用f.seek(5130682368)并立即开始阅读。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55802587

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档