How to read a large file, line by line, in Python
我想迭代整个文件的每一行。一种方法是读取整个文件,将其保存到一个列表中,然后遍历感兴趣的行。这个方法使用了大量的内存,所以我正在寻找一个替代方法。
我的代码到目前为止:
1 2 3 4 5 | for each_line in fileinput.input(input_file): do_something(each_line) for each_line_again in fileinput.input(input_file): do_something(each_line_again) |
执行此代码会给出一条错误消息:
有什么建议吗?
目的是计算成对字符串的相似性,这意味着对于文件中的每一行,我要计算与其他每一行之间的Levenshtein距离。
读取文件的正确、完全的方法如下:
1 2 3 | with open(...) as f: for line in f: # Do something with 'line' |
There should be one -- and preferably only one -- obvious way to do it.
两种记忆效率排序方法(第一种是最好的)
1。
1 2 3 | with open("x.txt") as f: for line in f: do something with data |
2。
有时,可能需要对每个迭代中的读取量进行更细粒度的控制。在这种情况下,使用ITER&Yield。注意,使用此方法时,需要在结尾处显式地关闭文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def readInChunks(fileObj, chunkSize=2048): """ Lazy function to read a file piece by piece. Default chunk size: 2kB. """ while True: data = fileObj.read(chunkSize) if not data: break yield data f = open('bigFile') for chuck in readInChunks(f): do_something(chunk) f.close() |
为完整起见,以下方法对于阅读大型文件来说不是很好,也不是很优雅,但是请阅读以获得全面的理解。
在Python中,从文件中读取行的最常见方法是执行以下操作:
1 2 | for line in open('myfile','r').readlines(): do_something(line) |
但是,完成此操作后,
1 2 3 4 | import fileinput for line in fileinput.input(['myfile']): do_something(line) |
去除换行符:
1 2 3 4 5 | with open(file_path, 'rU') as f: for line_terminated in f: line = line_terminated.rstrip(' ') ... |
在通用换行支持下,所有文本文件行似乎都将以
'
'
'
'。
编辑-指定通用换行支持:
- Unix上的python 2-
open(file_path, mode='rU') 是必需的[谢谢@dave] - Windows上的python 2-
open(file_path, mode='rU') -可选 - python 3-
open(file_path, newline=None) -可选
转换为
Docs:
- open()用于python 2
- open()用于python 3
要保留本机行终止符,请执行以下操作:
1 2 3 | with open(file_path, 'rb') as f: with line_native_terminated in f: ... |
二进制模式仍然可以使用
多亏了@katrielalex的答案、python的open()医生和ipython实验。
这是用python读取文件的一种可能方法:
1 2 3 4 | f = open(input_file) for line in f: do_stuff(line) f.close() |
它没有分配完整的列表。它迭代这些行。
关于我来自哪里的一些背景。代码段在末尾。好的。
如果可以的话,我更喜欢使用诸如h2o这样的开源工具来进行超高性能的并行csv文件读取,但是这个工具在功能集上是有限的。最后,我编写了大量代码来创建数据科学管道,然后将其输入到h2o集群中,以便进行有监督的学习。好的。
我一直在从UCI repo读取8GB希格斯数据集之类的文件,甚至为了数据科学的目的读取40GB csv文件,通过添加大量与多处理库的池对象和映射函数的并行性,速度显著加快。例如,使用最近邻搜索以及DBSCAN和马尔可夫聚类算法进行聚类需要一些并行编程技巧来绕过一些严重挑战性的内存和壁时钟问题。好的。
我通常喜欢先使用GNU工具将文件按行划分为多个部分,然后全局文件屏蔽它们,以便在python程序中并行查找和读取它们。我通常使用1000多个部分文件。做这些技巧对处理速度和内存限制有很大的帮助。好的。
pandas dataframe.read_csv是单线程的,因此您可以通过运行map()进行并行执行,从而使pandas运行得更快。您可以使用htop来查看简单的padas sequential dataframe.read_csv,一个核心上的100%CPU是pd.read_csv的实际瓶颈,而不是磁盘。好的。
我应该补充一下,我在快速视频卡总线上使用的是SSD,而不是SATA6总线上旋转的HD,加上16个CPU核心。好的。
另外,我发现的另一种技术在某些应用程序中效果很好,那就是并行的csv文件在一个大文件中读取所有内容,启动每个工作人员以不同的偏移量进入文件,而不是将一个大文件预拆分为多个部分文件。在每个并行工作程序中使用python的file seek()和tell()在大文件中的不同字节偏移量起始字节和结束字节位置同时读取大文本文件。您可以对字节执行regex findall,并返回换行数。这是一个部分和。最后对部分求和进行求和,得到当map函数在工人完成后返回时的全局求和。好的。
以下是使用并行字节偏移技巧的一些示例基准:好的。
我使用2个文件:higgs.csv是8GB。它来自UCI机器学习库。所有的.csv文件都是40.4 GB,来自我当前的项目。我使用两个程序:Linux附带的GNUWC程序和我开发的纯python fastread.py程序。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv -rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv HP-Z820:/mnt/fastssd$ ls -l all_bin.csv -rw-rw-r-- 1 40412077758 Feb 2 09:00 all_bin.csv ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2 2367496 real 0m8.920s user 1m30.056s sys 2m38.744s In [1]: 40412077758. / 8.92 Out[1]: 4530501990.807175 |
这大约是4.5GB/s,或45GB/s,文件吞咽速度。那不是旋转硬盘,我的朋友。实际上是三星Pro 950固态硬盘。好的。
下面是GNUWC(一个纯C编译程序)计算的同一文件的速度基准。好的。
很酷的是,在本例中,您可以看到我的纯Python程序基本上与GNUWC编译的C程序的速度相匹配。python是被解释的,但c是被编译的,所以这是一个相当有趣的速度壮举,我想你会同意的。当然,wc真的需要改成一个并行程序,然后它将真正击败我的python程序。但正如今天的情况,GNUWC只是一个连续的程序。你做你能做的,而Python今天可以做平行的。Cython编译也许可以帮助我(在其他时间)。另外,内存映射文件还没有被探索过。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv 2367496 all_bin.csv real 0m8.807s user 0m1.168s sys 0m7.636s HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2 11000000 real 0m2.257s user 0m12.088s sys 0m20.512s HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv 11000000 HIGGS.csv real 0m1.820s user 0m0.364s sys 0m1.456s |
结论:与C程序相比,纯python程序的速度更好。但是,在C程序上使用纯Python程序还不够好,至少对于行计数来说是这样。一般来说,该技术可以用于其他文件处理,所以这个python代码仍然很好。好的。
问题:只编译一次regex并将其传递给所有工人,这是否会提高速度?答:在此应用程序中,regex预编译没有帮助。我想原因是所有工人的过程序列化和创建的开销占主导地位。好的。
还有一件事。并行csv文件读取是否有帮助?磁盘是瓶颈还是CPU?他们说,stackoverflow上许多所谓的顶级答案都包含常见的开发人员智慧,即您只需要一个线程就可以读取一个文件,这是您所能做到的最好的方法。不过,他们确定吗?好的。
让我们看看:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2 11000000 real 0m2.256s user 0m10.696s sys 0m19.952s HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1 11000000 real 0m17.380s user 0m11.124s sys 0m6.272s |
哦,是的,是的。并行文件读取非常好。好吧,你去吧!好的。
另外,如果你们中的一些人想知道,当使用一个工作进程时,如果平衡因子是2呢?好吧,这太可怕了:好的。
1 2 3 4 5 6 | HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2 11000000 real 1m37.077s user 0m12.432s sys 1m24.700s |
fastread.py python程序的关键部分:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | fileBytes = stat(fileName).st_size # Read quickly from OS how many bytes are in a text file startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor) p = Pool(numProcesses) partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values. globalSum = sum(partialSum) print(globalSum) def ReadFileSegment(startByte, endByte, fileName, searchChar=' '): # counts number of searchChar appearing in the byte range with open(fileName, 'r') as f: f.seek(startByte-1) # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte. bytes = f.read(endByte - startByte + 1) cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times. return cnt |
PartitionDataToWorkers的定义只是普通的顺序代码。我把它省去了,以防有人想练习并行编程是什么样子的。我免费赠送了更难的部分:经过测试和工作的并行代码,为您的学习带来好处。好的。
多亏了:阿诺和克里夫的开源H2O项目以及H2O工作人员的优秀软件和教学视频,这些都为我提供了这台纯Python高性能并行字节偏移阅读器的灵感,如上图所示。H2O使用Java并行文件读取,可以被Python和R程序调用,并且在读取大CSV文件时比在地球上的任何东西都快得快。好的。好啊。
KatrielAlex提供了打开和读取一个文件的方法。
但是,按照您的算法,它会读取文件每行的整个文件。这意味着,如果n是文件中的行数,那么读取文件的总数量——以及计算列文斯坦距离——将完成n*n。由于您关心的是文件大小,不希望将其保存在内存中,所以我关心的是生成的二次运行时。您的算法属于O(n^2)类算法,通常可以通过专门化进行改进。
我怀疑您已经知道内存与运行时之间的权衡,但是您可能想研究一下是否有一种有效的方法来并行计算多个Levenshtein距离。如果是这样的话,在这里分享你的解决方案会很有趣。
您的文件有多少行,您的算法必须在哪种机器(mem&cpu电源)上运行,允许的运行时间是什么?
代码如下:
1 2 3 4 5 | with f_outer as open(input_file, 'r'): for line_outer in f_outer: with f_inner as open(input_file, 'r'): for line_inner in f_inner: compute_distance(line_outer, line_inner) |
但问题是如何存储距离(矩阵?)您是否可以获得这样的优势:为处理而准备外部线路,或者缓存一些中间结果以供重用。
1 2 3 4 5 | #Using a text file for the example with open("yourFile.txt","r") as f: text = f.readlines() for line in text: print line |
- 打开文件阅读(R)
- 读取整个文件并将每行保存到一个列表中(文本)
- 在列表中循环打印每一行。
例如,如果要检查特定行的长度是否大于10,请使用现有的长度。
1 2 3 | for line in text: if len(line) > 10: print line |
我强烈建议不要使用默认文件加载,因为它的速度非常慢。您应该研究numpy函数和iopro函数(例如numpy.loadtxt())。
http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html网站
https://store.continuum.io/cshop/iopro/
然后您可以将成对操作分成块:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import numpy as np import math lines_total = n similarity = np.zeros(n,n) lines_per_chunk = m n_chunks = math.ceil(float(n)/m) for i in xrange(n_chunks): for j in xrange(n_chunks): chunk_i = (function of your choice to read lines i*lines_per_chunk to (i+1)*lines_per_chunk) chunk_j = (function of your choice to read lines j*lines_per_chunk to (j+1)*lines_per_chunk) similarity[i*lines_per_chunk:(i+1)*lines_per_chunk, j*lines_per_chunk:(j+1)*lines_per_chunk] = fast_operation(chunk_i, chunk_j) |
将数据分块加载,然后对其进行矩阵运算,几乎总是比逐元素加载快得多!!
从fileinput.input()的python文档中:
This iterates over the lines of all files listed in
sys.argv[1:] , defaulting tosys.stdin if the list is empty
此外,函数的定义是:
1 | fileinput.FileInput([files[, inplace[, backup[, mode[, openhook]]]]]) |
从字里行间看,这告诉我,
1 2 | for each_line in fileinput.input([input_file, input_file]): do_something(each_line) |
有关详细信息,请参阅此处
逐行读取大文件的最佳方法是使用python枚举函数
1 2 3 4 5 | with open(file_name,"rU") as read_file: for i, row in enumerate(read_file, 1): #do something #i in line of that line #row containts all data of that line |