python 导入数据对不齐_[Python] 大文件数据读取分析
首先我們可以確定的是不能用read()與readlines()函數;
因為如果將這兩個函數均將數據全部讀入內存,會造成內存不足的情況。
針對數據按行劃分的文件
以計算行數為例,首先針對幾種不同的方法來作比較:
1、使用for遍歷的方法,比較美觀,網上搜索到八有十九是讓你這樣做,盡管已經很快了但還不是最快的
start = time.time()
with open(dataPath, 'r') as f:
count = 0
for line in f:
count += 1
print(count)
print(time.time() - start)
輸出:
5000
0.09386205673217773
2、使用readline()模擬遍歷,發現其實結果和第一種差不多
start = time.time()
with open(dataPath, 'r') as f:
line = f.readline()
count = 1
while line:
count += 1
line = f.readline()
print(count - 1)
print(time.time() - start)
輸出:
5000
0.09433221817016602
3、對比readlines()直接去訪問,結果卻更慢了!
start = time.time()
with open(dataPath, 'r') as f:
count = 0
for line in f.readlines():
count += 1
print(count)
print(time.time() - start)
輸出:
5000
0.12223696708679199
4、不斷去檢測文件指針位置,有的時候我們可能需要讀到特定的文件位置就停下;就會發現tell()十分耗時!
start = time.time()
with open(dataPath, 'r') as f:
count = 0
while f.tell() < datasize:
f.readline()
count += 1;
print(count)
print(time.time() - start)
輸出:
5000
0.29171299934387207
5、使用mmap的方法,mmap是一種虛擬內存映射文件的方法,即將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。通過建立一個文件的內存映射將使用操作系統虛擬內存來直接訪問文件系統上的數據,而不是使用常規的I/O函數訪問數據。內存映射通常可以提供I/O性能,因為使用內存映射是,不需要對每個訪問都建立一個單獨的系統調用,也不需要在緩沖區之間復制數據;實際上,內核和用戶應用都能直接訪問內存,是目前測到最快的方法。
import mmap
start = time.time()
with open(dataPath, "r") as f:
# memory-map the file, size 0 means whole file
map = mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ)
count = 0
while map.readline():
count += 1
print(count)
map.close()
print(time.time() - start)
輸出:
5000
0.023865938186645508
6、可以不按行讀取,而是按塊讀取,然后分析\n的個數,但這只針對計算行數而論可行,但我們真正想要的是按行讀取數據,所以這里只給出實現方法,不進行對比。
with open(r"d:\lines_test.txt",'rb') as f:
count = 0
while True:
buffer = f.read(1024 * 8192)
if not buffer:
break
count += buffer.count('\n')
print count
考慮MPI的情況
當文件很大的時候,任務又需要并行化的話,我們可以將文件拆分成多段去處理,例如對于4核的電腦,可以讓4條進程分別去處理文件不同的部分,每條進程讀四分之一的數據。但這時候要考慮到,分割點不一定剛好是換行符的情況,所以我們可以考慮從分割點下一個換行符開始搜索,分割點第一個換行符之前的交給前一個進程去處理,處理方法如圖:
實現類似:
from mpi4py import MPI
import platform
import sys
import io
import os
import mmap
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
comm = MPI.COMM_WORLD
comm_size = comm.size
comm_rank = comm.rank
with open(filePath, 'r', encoding='utf-8') as f:
# Set the file pointer to the beginning of a line after blockSize * rank
# Use mmap to run faster
map = mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ)
map.seek(comm_rank * blockSize)
if comm_rank != 0:
map.readline()
# Each process handle about blocksize lines.
blockEnd = (comm_rank + 1) * blockSize
# Use index here to avoid using twice map.tell()
index = map.tell()
while index <= blockEnd and index < dataSize:
# line = map.readline().translate(None, b'\x00').decode()
line = map.readline().decode('utf-8')
index = map.tell()
try:
dosomething(line)
except Exception as err:
print(err)
continue
如果不用mmap.tell()改用f.tell()的話,效率其差,一開始我遇到這種情況的時候是想著自己不斷去加len(line)去自己計算文件指針的位置的。但又發現一個問題,file.readline()會幫你去除部分字符,例如\r\n只會保留\n,而mmap.readline()則不會,而且試過表示很難,總是和f.tell()對不齊。
數據按特殊符號劃分
考慮到可能數據劃分點不是\n, 我們可以這樣讀取:
def rows(f, chunksize=1024, sep='|'):
"""
Read a file where the row separator is '|' lazily.
Usage:
>>> with open('big.csv') as f:
>>>? ? for r in rows(f):
>>>? ? ? ? process(row)
"""
curr_row = ''
while True:
chunk = f.read(chunksize)
if chunk == '': # End of file
yield curr_row
break
while True:
i = chunk.find(sep)
if i == -1:
break
yield curr_row + chunk[:i]
curr_row = ''
chunk = chunk[i+1:]
curr_row += chunk
數據無特定劃分方式
一種方法是用yield:
def read_in_chunks(file_object, chunk_size=1024):
"""Lazy function (generator) to read a file piece by piece.
Default chunk size: 1k."""
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open('really_big_file.dat') as f:
for piece in read_in_chunks(f):
process_data(piece)
另外一種方法是用iter和一個helper function:
f = open('really_big_file.dat')
def read1k():
return f.read(1024)
for piece in iter(read1k, ''):
process_data(piece)
總結
以上是生活随笔為你收集整理的python 导入数据对不齐_[Python] 大文件数据读取分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php取不到post数据库,安卓post
- 下一篇: 线程 协程 python_在Python