【摘要】
數(shù)據(jù)分析時构挤,難免會遇到內(nèi)存裝不下的大數(shù)據(jù)文件婶希,如何對大文件進行查詢計算?本文分析比較了幾種常用辦法的優(yōu)缺點孔轴,esProc SPL 是其中最適合數(shù)據(jù)分析師用于處理大文件的腳本。讓我們一起通過下面的文章一起來了解一下詳情吧~
什么是大文件碎捺?大文件是指大到計算機內(nèi)存不足以一次性讀入的文件路鹰。這種情況,直接使用桌面數(shù)據(jù)工具(比如 Excel)都無能為力了收厨,常常需要編寫程序來處理晋柱。而即使是寫程序,也必須是分批讀入進行計算處理诵叁,最后再按照不同的計算類型對分批處理結(jié)果進行恰當?shù)膮R總處理雁竞,比小文件數(shù)據(jù)的處理要很雜很多。大文件的種類也有多種拧额,例如文本文件碑诉、Excel文件、XML文件侥锦、JSON文件进栽、HTTP文件……等等,其中以文本 (txt 或 csv) 最為常見恭垦。
可以用于處理大文件的程序語言一般有以下幾種:
1快毛、 常規(guī)高級編程語言格嗅,比如 Java、C/C++唠帝、C#屯掖、Basic 等
2、? 將文件數(shù)據(jù)導入到數(shù)據(jù)庫后用SQL來處理
3襟衰、? Python
4懂扼、? esProc SPL
本文以文本文件舉例,依次介紹以上幾種程序方法進行大文件計算的特點右蒲。其它類型文件數(shù)據(jù)阀湿,除了讀入數(shù)據(jù)的方式不同,讀入后的處理思路就都與文本文件相似瑰妄。
文中要用到的訂單文件orders.txt中有5個列:orderkey陷嘴、orderdate、state间坐、quantity灾挨、amount,列數(shù)據(jù)間以TAB分隔竹宋,文件中第一行是列名劳澄,總共有1000萬行數(shù)據(jù)。如下:
一蜈七、??高級語言(以Java為例)
用高級語言編程來計算秒拔,計算過程怎么編寫,與具體的計算類型有關飒硅,不同類型的計算需要不同的計算過程砂缩。我們先來看看最簡單的合計運算,例如求訂單表orders.txt中訂單金額amount的總和三娩,用Java寫出來是這樣的:
BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream("orders.txt") ) );
String[] fieldNames = br.readLine().split("\t");
double sumAmount = 0;
String line = null;
while(( line = br.readLine() )!= null) {
String[] values = line.split("\t");
float amount = Float.parseFloat(values[4] );? //假定已知第 5 列是 amount
sumAmount += amount;
}
這段程序是一次讀一行數(shù)據(jù)庵芭,訪問文件時間太多,運行速度慢雀监。想要運行快一些双吆,需要一次讀入大塊數(shù)據(jù)(比如1萬行),然后再用代碼把數(shù)據(jù)拆成行來處理会前,過程會麻煩很多好乐。
這是最簡單的計算了,沒有數(shù)據(jù)過濾和分組回官、排序等要求曹宴。其它求平均值、最大值與最小值的計算歉提,跟這差不多笛坦。
如果要做分組匯總区转,代碼就會麻煩得多。比如按州state分組后求各州的訂單金額總和版扩,計算思路是這樣的:把各個分組保存起來废离,然后一行行地讀各行中的state值,與保存的組比較礁芦,找到了則將本行訂單金額加到組上蜻韭,沒找到則新加入一個組。最后直到所有行都處理完柿扣。如果按多個字段分組求多個統(tǒng)計值肖方,比如按日期和州分組求訂單金額總和、最大訂單金額未状,程序的復雜度就會增加很多俯画。
排序就更麻煩,還需要中間緩存文件司草。例如要按訂單金額從大到小排序艰垂,因內(nèi)存不足,不能讀入所有數(shù)據(jù)來排序埋虹,計算思路是這樣的:先讀入5000行(讀多少行合適要根據(jù)內(nèi)存而定)數(shù)據(jù)猜憎,排序后存到一個臨時文件,再讀入5000行排序后存到另一個臨時文件……直到所有數(shù)據(jù)處理完搔课,最后對這些臨時文件進行有序歸并——讀出每個臨時文件的第一行胰柑,找出應該排在最前面的那一行,寫入到結(jié)果文件辣辫,然后從那個臨時文件中再讀出一行旦事,繼續(xù)比較找出最前面的一行寫入結(jié)果文件。按此方法不斷進行急灭,直到所有數(shù)據(jù)行都寫入結(jié)果文件。
用高級語言完成大文件的處理確實是相當繁瑣的谷遂,對于非專業(yè)的程序人員葬馋,這幾乎是不可行的。
二肾扰、??利用數(shù)據(jù)庫
數(shù)據(jù)庫內(nèi)置了許多計算算法畴嘶,對數(shù)據(jù)的查詢計算功能比較完善,性能也比較好集晚,因此可以考慮將文件型數(shù)據(jù)導入到數(shù)據(jù)庫窗悯,生成數(shù)據(jù)庫表,再使用SQL來進行數(shù)據(jù)查詢計算偷拔。
這個辦法麻煩的地方就是要將文件數(shù)據(jù)導入數(shù)據(jù)庫蒋院,在導入數(shù)據(jù)之前亏钩,先要創(chuàng)建數(shù)據(jù)表結(jié)構(gòu),指定每個列的數(shù)據(jù)類型欺旧,例如創(chuàng)建訂單表的SQL如下:
CREATE TABLE orders ( orderkey INTEGER NOT NULL,
orderdate DATE NOT NULL,
state? CHAR(20) NOT NULL,
quantity? INTEGER NOT NULL,
amount? DECIMAL(15,2) NOT NULL,
PRIMARY KEY(orderkey)
);
如果換個其它結(jié)構(gòu)的數(shù)據(jù)文件姑丑,那么需要另寫一條建表的SQL。這里特別需要指定數(shù)據(jù)類型辞友,否則數(shù)據(jù)庫無法接受這些數(shù)據(jù)栅哀,而這是許多非專業(yè)程序員很不熟悉的地方。
對于導入過程称龙,數(shù)據(jù)庫一般都提供了直接導入文本文件的工具留拾,而其它文件則不能直接導入,需要先轉(zhuǎn)換為文本文件鲫尊。Excel文件還可以直接另存為文本间驮,而對于XML文件、JSON文件马昨、HTTP文件等則又需要編寫程序?qū)⑺鼈冝D(zhuǎn)化為文本文件竞帽,或者編寫程序先讀入文件數(shù)據(jù),生成一條SQL語句將數(shù)據(jù)寫入數(shù)據(jù)庫表中鸿捧,無論如何都是非常繁瑣的事情屹篓。
數(shù)據(jù)存入到數(shù)據(jù)庫表以后,查詢計算確實就非常簡單了匙奴,分組堆巧、排序都挺容易,示例如下:
1泼菌、? 求訂單金額總和
select sum(amount) from orders;
2谍肤、? 按州分組求各州訂單金額總和
select state, sum(amount) from orders group by state;
3、? 按訂單金額排序
select * from orders order by amount;
利用數(shù)據(jù)庫能很方便地查詢計算較大數(shù)據(jù)量哗伯,但把大文件導入數(shù)據(jù)庫卻很繁瑣而且有一定的專業(yè)技能要求荒揣。相對于高級語言的可行性大幅提高,但仍不夠好焊刹。
三系任、??Python
Python也沒有提供直接針對大文件的處理語法,其實現(xiàn)思路和高級語言類似虐块,如前面的計算訂單金額總和寫出來大概是:
sumAmount=0
with open("orders.txt",'r') as f:
while True:
line = f.readline()
if not line:
break
sumAmount += float(line.split("\t")[4])
print(sumAmount)
對于分組排序這類復雜一些的運算俩滥,如果實現(xiàn)前面說過的思路,用Python也非常麻煩贺奠,并不比java簡單多少霜旧。但Python有個pandas包,封裝了不少結(jié)構(gòu)化數(shù)據(jù)的處理函數(shù)儡率。如果是可讀入內(nèi)存的小文件挂据,它可以很簡單地處理以清。可惜的是棱貌,pandas沒有針對大文件提供直接分批處理的方法玖媚,還是要自己寫。使用pandas的復雜度比直接硬寫要小很多婚脱,但仍然要實現(xiàn)前面討論過的思路今魔。
分組運算寫起來太麻煩,我們還是把上面的求和運算基于pandas寫出來感受一下障贸。
import pandas as pd
chunk_data = pd.read_csv("orders.txt",sep="\t",header=None,chunksize=100000)
sumAmount=0
for chunk in chunk_data:
sumAmount+=chunk[4].sum()
print(sumAmount)
使用pandas后可以把文本看成一行行有結(jié)構(gòu)的數(shù)據(jù)错森,不再需要自己拆分。
Python處理小文件沒有大問題篮洁,但對于大文件未提供有效支持涩维。和高級語言相比,減輕的工作量很有限袁波,可用程度不高瓦阐,還不如數(shù)據(jù)庫。
另外篷牌,Python是個解釋執(zhí)行語言睡蟋,執(zhí)行速度遠遠低于高級語言,處理大文件時感受會更明顯枷颊。
四戳杀、??esProc ?SPL
esProc是專業(yè)的數(shù)據(jù)處理工具,與數(shù)據(jù)庫一樣內(nèi)置了各種查詢計算算法夭苗,可以直接使用文本信卡、Excel、Xml题造、Json等文件數(shù)據(jù)計算傍菇,不需要導入數(shù)據(jù)的過程。
esProc提供了游標晌梨,可以分批讀入數(shù)據(jù)再計算桥嗤,這樣就可以很方便地處理大文件了。象前面的例子仔蝌,只要寫一行代碼就可以:
1、? 求訂單金額總和
=file("orders.txt").cursor@t().total(sum(amount))
如果想再加個過濾也很容易荒吏,比如只統(tǒng)計2009年開始的訂單金額總和:
=file("orders.txt").cursor@t().select(orderdate>=date("2009-01-01")).total(sum(amount))
做分組敛惊、排序也簡單:
2、? 按州分組求各州訂單金額總和
=file("orders.txt").cursor@t().groups(state;sum(amount))
3绰更、? 按訂單金額排序
=file("orders.txt").cursor@t().sortx(amount)
esProc甚至還允許直接對文件使用SQL查詢瞧挤,如前面3例寫出來如下:
$select sum(amount) from "orders.txt"
$select state, sum(amount) from "orders.txt" group by state
$select * from "orders.txt" order by amount
esProc還內(nèi)置了并行計算锡宋,能充分利用多核CPU提高性能,這對于大文件是特別有用的特恬。比如分組匯總計算寫成:
=file("orders.txt").cursor@tm(;4).groups(state;sum(amount))
將會按 4 路并行方式計算执俩,在普通多核筆記本上速度能提高 2-3 倍。相對來講癌刽,高級語言能實現(xiàn)并行役首,但不僅麻煩,而且只有專業(yè)程序員才會做显拜。Python基本上不能并行衡奥。SQL得看數(shù)據(jù)庫,Oracle這種專業(yè)數(shù)據(jù)庫沒問題远荠,而簡易的MySQL就不行矮固。
esProc SPL擁有了SQL的優(yōu)點,又避免了數(shù)據(jù)導入的麻煩譬淳,對于桌面數(shù)據(jù)分析人員來講档址,是用于處理大文件的最合適工具。