pandas高效讀取大文件的示例詳解
使用 pandas
進(jìn)行數(shù)據(jù)分析時(shí),第一步就是讀取文件。
在平時(shí)學(xué)習(xí)和練習(xí)的過(guò)程中,用到的數(shù)據(jù)量不會(huì)太大,所以讀取文件的步驟往往會(huì)被我們忽視。
然而,在實(shí)際場(chǎng)景中,面對(duì)十萬(wàn),百萬(wàn)級(jí)別的數(shù)據(jù)量是家常便飯,即使千萬(wàn),上億級(jí)別的數(shù)據(jù),單機(jī)處理也問(wèn)題不大。
不過(guò),當(dāng)數(shù)據(jù)量和數(shù)據(jù)屬性多了之后,讀取文件的性能瓶頸就開始浮現(xiàn)出來(lái)。
當(dāng)我們第一次拿到數(shù)據(jù)時(shí),經(jīng)常會(huì)反反復(fù)復(fù)的讀取文件,嘗試各種分析數(shù)據(jù)的方法。
如果每次讀取文件都要等一段時(shí)間,不僅會(huì)影響工作效率,還影響心情。
下面記錄了我自己優(yōu)化pandas
讀取大文件效率的探索過(guò)程。
1. 準(zhǔn)備部分
首先,準(zhǔn)備數(shù)據(jù)。
下面的測(cè)試用的數(shù)據(jù)是一些虛擬幣的交易數(shù)據(jù),除了常用的K線數(shù)據(jù)之外,還包含很多分析因子的值。
import pandas as pd fp = "all_coin_factor_data_12H.csv" df = pd.read_csv(fp, encoding="gbk") df.shape # 運(yùn)行結(jié)果 (398070, 224)
總數(shù)據(jù)量接近40萬(wàn),每條數(shù)據(jù)有224
個(gè)屬性。
然后,封裝一個(gè)簡(jiǎn)單的裝飾器來(lái)計(jì)時(shí)函數(shù)運(yùn)行時(shí)間。
from time import time def timeit(func): def func_wrapper(*args, **kwargs): start = time() ret = func(*args, **kwargs) end = time() spend = end - start print("{} cost time: {:.3f} s".format(func.__name__, spend)) return ret return func_wrapper
2. 正常讀取
先看看讀取這樣規(guī)模的數(shù)據(jù),需要多少時(shí)間。
下面的示例中,循環(huán)讀取10次上面準(zhǔn)備的數(shù)據(jù)all_coin_factor_data_12H.csv
。
import pandas as pd @timeit def read(fp): df = pd.read_csv( fp, encoding="gbk", parse_dates=["time"], ) return df if __name__ == "__main__": fp = "./all_coin_factor_data_12H.csv" for i in range(10): read(fp)
運(yùn)行結(jié)果如下:
讀取一次大概27秒左右。
3. 壓縮讀取
讀取的文件all_coin_factor_data_12H.csv
大概1.5GB
左右,pandas
是可以直接讀取壓縮文件的,嘗試壓縮之后讀取性能是否能夠提高。
壓縮之后,大約 615MB
左右,壓縮前大小的一半不到點(diǎn)。
import pandas as pd @timeit def read_zip(fp): df = pd.read_csv( fp, encoding="gbk", parse_dates=["time"], compression="zip", ) return df if __name__ == "__main__": fp = "./all_coin_factor_data_12H.zip" for i in range(10): read_zip(fp)
運(yùn)行結(jié)果如下:
讀取一次大概34秒左右,還不如直接讀取來(lái)得快。
4. 分批讀取
接下來(lái)試試分批讀取能不能提高速度,分批讀取的方式是針對(duì)數(shù)據(jù)量特別大的情況,單機(jī)處理過(guò)億數(shù)據(jù)量的時(shí)候,經(jīng)常會(huì)用到這個(gè)方法,防止內(nèi)存溢出。
先試試每次讀取1萬(wàn)條:
import pandas as pd @timeit def read_chunk(fp, chunksize=1000): df = pd.DataFrame() reader = pd.read_csv( fp, encoding="gbk", parse_dates=["time"], chunksize=chunksize, ) for chunk in reader: df = pd.concat([df, chunk]) df = df.reset_index() return df if __name__ == "__main__": fp = "./all_coin_factor_data_12H.csv" for i in range(10): read_chunk(fp, 10000)
運(yùn)行結(jié)果如下:
和讀取壓縮文件的性能差不多。
如果調(diào)整成每次讀取10萬(wàn)條,性能會(huì)有一些微提高。
分批讀取時(shí),一次讀取的越多(只要內(nèi)存夠用),速度越快。
其實(shí)我也試了一次讀取1千條的性能,非常慢,這里就不截圖了。
5. 使用polars讀取
前面嘗試的方法,效果都不太好,下面引入一個(gè)和pandas
兼容的庫(kù)Polars
。
Polars
是一個(gè)高性能的DataFrame
庫(kù),它主要用于操作結(jié)構(gòu)化數(shù)據(jù)。
它是用Rust
寫的,主打就是高性能。
使用Polars
讀取文件之后返回的Dataframe
雖然和pandas
的DataFrame
不完全一樣,當(dāng)可以通過(guò)一個(gè)簡(jiǎn)單的to_pandas
方法來(lái)完成轉(zhuǎn)換。
下面看看使用Polars
讀取文件的性能:
import polars as pl @timeit def read_pl(fp): df = pl.read_csv( fp, encoding="gbk", try_parse_dates=True, ) return df.to_pandas() if __name__ == "__main__": fp = "./all_coin_factor_data_12H.csv" for i in range(10): read_pl(fp)
運(yùn)行結(jié)果如下:
使用Polars
后性能提高非常明顯,看來(lái),混合使用Polars
和pandas
是一個(gè)不錯(cuò)的方案。
6. 序列化后讀取
最后這個(gè)方法,其實(shí)不是直接讀取原始數(shù)據(jù),而是將原始數(shù)據(jù)轉(zhuǎn)換為python
自己的序列化格式(pickle
)之后,再去讀取。
這個(gè)方法多了一個(gè)轉(zhuǎn)換的步驟:
fp = "./all_coin_factor_data_12H.csv" df = read(fp) df.to_pickle("./all_coin_factor_data_12H.pkl")
生成一個(gè) 序列化文件:all_coin_factor_data_12H.pkl
。
然后,測(cè)試下讀取這個(gè)序列化文件的性能。
@timeit def read_pkl(fp): df = pd.read_pickle(fp) return df if __name__ == "__main__": fp = "./all_coin_factor_data_12H.pkl" for i in range(10): read_pkl(fp)
運(yùn)行結(jié)果如下:
這個(gè)性能出乎意料之外的好,而且csv文件序列化成pkl文件之后,占用磁盤的大小也只有原來(lái)的一半。
csv
文件1.5GB
左右,pkl
文件只有690MB
。
這個(gè)方案雖然性能驚人,但也有一些局限,首先是原始文件不能是那種實(shí)時(shí)變化的數(shù)據(jù),因?yàn)樵?strong>csv文件轉(zhuǎn)換為pkl文件也是要花時(shí)間的(上面的測(cè)試沒(méi)有算這個(gè)時(shí)間)。
其次,序列化之后的pkl文件是python專用的,不像csv文件那樣通用,不利于其他非python的系統(tǒng)使用。
7. 總結(jié)
本文探討了一些pandas
讀取大文件的優(yōu)化方案,最后比較好的就是Polars方案和pickle序列化方案。
如果我們的項(xiàng)目是分析固定的數(shù)據(jù),比如歷史的交易數(shù)據(jù),歷史天氣數(shù)據(jù),歷史銷售數(shù)據(jù)等等,那么,就可以考慮pickle序列化方案,先花時(shí)間講原始數(shù)據(jù)序列化,后續(xù)的分析中不擔(dān)心讀取文件浪費(fèi)時(shí)間,可以更高效的嘗試各種分析思路。
除此之外的情況,建議使用Polars方案。
最后補(bǔ)充一點(diǎn),如果讀取文件的性能對(duì)你影響不大,那就用原來(lái)的方式,千萬(wàn)不要畫蛇添足的去優(yōu)化,把精力花在數(shù)據(jù)分析的業(yè)務(wù)上。
以上就是pandas高效讀取大文件的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于pandas讀取大文件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
完美解決安裝完tensorflow后pip無(wú)法使用的問(wèn)題
今天小編就為大家分享一篇完美解決安裝完tensorflow后pip無(wú)法使用的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Python集合基本概念與相關(guān)操作實(shí)例分析
這篇文章主要介紹了Python集合基本概念與相關(guān)操作,結(jié)合實(shí)例形式分析了Python集合的功能、原理、基本使用方法及操作注意事項(xiàng),需要的朋友可以參考下2019-10-10Python第三方庫(kù)OS庫(kù)方法實(shí)操
這篇文章主要給大家介紹了關(guān)于Python第三方庫(kù)OS庫(kù)的相關(guān)資料,os庫(kù)主要是對(duì)文件和文件夾進(jìn)行操作,在Python中對(duì)?件和?件夾的操作要借助os模塊??的相關(guān)功能,需要的朋友可以參考下2024-06-06Python函數(shù)式編程中itertools模塊詳解
這篇文章主要介紹了在Python中使用itertools模塊中的組合函數(shù)的教程,來(lái)自IBM官方技術(shù)文檔,需要的朋友可以參考下,希望能夠給你帶來(lái)幫助2021-09-09