最近,我用pandas處理了一把大數(shù)據(jù)……
導(dǎo)讀
pandas是python數(shù)據(jù)分析的不二選擇,堪稱瑞士軍刀般的存在,幾乎可以勝任數(shù)據(jù)分析的全過程。如果說有什么缺點(diǎn)的話,那么就是其不支持分布式,所以對于小數(shù)據(jù)量完全不壓力,但面對大數(shù)據(jù)時(shí)卻當(dāng)真有些乏力。近日,自己便用pandas處理了一些大數(shù)據(jù)場景,現(xiàn)分享幾個(gè)心得技巧。

首先簡單介紹下場景:數(shù)據(jù)是每個(gè)月一份的csv文件,字段數(shù)目10個(gè)左右,單個(gè)文件記錄數(shù)約6-8億之間,單個(gè)文件體積50G+的樣子。表中是一條條的帶有時(shí)間字段的數(shù)據(jù),需求是對數(shù)據(jù)進(jìn)行匯總統(tǒng)計(jì)和簡單分析處理(一般而言,數(shù)據(jù)量巨大的需求處理邏輯都不會特別復(fù)雜)。所以,雖然標(biāo)題稱之為大數(shù)據(jù),但實(shí)際上也沒有特別夸張。
pandas自帶了常用文件的讀取方法,例如csv文件對應(yīng)的讀取函數(shù)即為pd.read_csv,這也是日常應(yīng)用中經(jīng)常接觸的方法。然而對于處理這個(gè)50G的csv文件而言,直接使用是肯定不行的,當(dāng)前個(gè)人電腦內(nèi)存普遍在8G-16G內(nèi)存之間,筆者的是一臺8G內(nèi)存的工作機(jī),除去系統(tǒng)占用基本留給用于加載數(shù)據(jù)的空間不到6G,另一方面通過多次試驗(yàn)結(jié)果:對于一個(gè)2G的文件,讀取過程中內(nèi)存占用會達(dá)到4G左右,大概是實(shí)際文件體積的兩倍,加載完畢之后會有有所回落。所以,就8G內(nèi)存的工作機(jī)而言,讀取一個(gè)2.5G的大文件本身已經(jīng)存在一定風(fēng)險(xiǎn)。
為此,pandas開發(fā)者專為此設(shè)計(jì)了兩組很有用的參數(shù),分別用于控制行和列信息:
skiprows + nrows,前者用于控制跳過多少行記錄,后者用于控制讀取行數(shù),skiprows默認(rèn)值為0,nrows缺省讀取所有行數(shù),這也是最常用的方式。但合理的設(shè)置兩個(gè)參數(shù),可以實(shí)現(xiàn)循環(huán)讀取特定范圍的記錄
usecols:顧名思義,僅加載文件中特定的列字段,非常適用于列數(shù)很多而實(shí)際僅需其中部分字段的情況,要求輸入的列名實(shí)際存在于表中

pd.read_csv()中相關(guān)參數(shù)說明
具體到實(shí)際需求,個(gè)人實(shí)現(xiàn)時(shí)首先通過循環(huán)控制skiprows參數(shù)來遍歷整個(gè)大文件,每次讀取后對文件再按天分割,同時(shí)僅選取其中需要的3個(gè)列字段作為加載數(shù)據(jù),如此一來便實(shí)現(xiàn)了大表到小表的切分。雖然受限于內(nèi)存而執(zhí)行效率有限,但也終究算是一種解決方案。
嚴(yán)格來說,這可能并不是大數(shù)據(jù)處理中才涉及到的問題,而是由Python的變量管理特性決定的。不同于C++中的手動回收、Java中的自動回收,Python中的對象采用引用計(jì)數(shù)管理,當(dāng)計(jì)數(shù)為0時(shí)內(nèi)存回收。所以,如果當(dāng)一個(gè)變量不再需要使用時(shí),最簡單的辦法是將其引用數(shù)-1,以加速其內(nèi)存回收。有一定python基礎(chǔ)的讀者可能會想到用關(guān)鍵字del實(shí)現(xiàn),這個(gè)思路是對的,但有時(shí)還不夠保險(xiǎn)和徹底,更為靠譜的方案是del + gc.collect()顯式回收。這里gc是一個(gè)python內(nèi)置模塊,用于內(nèi)存回收,gc = garbage collect。
del?xx
gc.collect()
給定的大文件中,時(shí)間字段是一個(gè)包含年月日時(shí)分秒的字符串列,雖然在read_csv方法中自帶了時(shí)間解析參數(shù),但對于頻繁多次應(yīng)用時(shí)間列進(jìn)行處理時(shí),其實(shí)還有更好的方法:轉(zhuǎn)為時(shí)間戳。
例如,在個(gè)人的實(shí)際處理中主要用到的操作包括:按時(shí)間排序、按固定周期進(jìn)行重采樣、分組聚合統(tǒng)計(jì)等,這幾個(gè)操作中無一例外都涉及到時(shí)間列的比較,如果是字符串格式或者時(shí)間格式的時(shí)間列,那么在每次比較中實(shí)際要執(zhí)行多次比較,而如果轉(zhuǎn)換為時(shí)間戳后,則參與比較的實(shí)際上是一個(gè)整數(shù)值,毫無疑問這是效率最高的比較類型。進(jìn)一步地,對于重采樣需求而言,還可以通過整除特定的時(shí)間間隔,然后執(zhí)行g(shù)roupby操作即可。例如,執(zhí)行每5分鐘重采樣,則可將所有時(shí)間戳(秒級)整除300,然后以相應(yīng)結(jié)果作為groupby字段即可。
#?假設(shè)df['dt']列是時(shí)間格式,需將其轉(zhuǎn)換為時(shí)間戳格式
#?方法一:
df['dt']?=?(pd.to_datetime(df['dt'])-pd.to_datetime('1970-01-01T08:00:00')).total_seconds()
#?方法二:
df['dt']?=?(pd.to_datetime(df['dt']).values?-?np.datetime64('1970-01-01T08:00:00Z'))?//?np.timedelta64(1,?'s')

相關(guān)閱讀:
