查詢計算大檔案的桌面程式工具

cainiao_M發表於2020-12-03

什麼是大檔案?大檔案是指大到計算機記憶體不足以一次性讀入的檔案。這種情況,直接使用桌面資料工具(比如 Excel)都無能為力了,常常需要編寫程式來處理。而即使是寫程式,也必須是分批讀入進行計算處理,最後再按照不同的計算型別對分批處理結果進行恰當的彙總處理,比小檔案資料的處理要很雜很多。大檔案的種類也有多種,例如文字檔案、Excel檔案、XML檔案、JSON檔案、HTTP檔案……等等,其中以文字 (txt 或 csv) 最為常見。

可以用於處理大檔案的程式語言一般有以下幾種:
1、 常規高階程式語言,比如 Java、C/C++、C#、Basic 等
2、 將檔案資料匯入到資料庫後用SQL來處理
3、 Python
4、 esProc SPL

本文以文字檔案舉例,依次介紹以上幾種程式方法進行大檔案計算的特點。其它型別檔案資料,除了讀入資料的方式不同,讀入後的處理思路就都與文字檔案相似。

文中要用到的訂單檔案orders.txt中有5個列:orderkey、orderdate、state、quantity、amount,列資料間以TAB分隔,檔案中第一行是列名,總共有1000萬行資料。如下:

..

用高階語言程式設計來計算,計算過程怎麼編寫,與具體的計算型別有關,不同型別的計算需要不同的計算過程。我們先來看看最簡單的合計運算,例如求訂單表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;
}

這段程式是一次讀一行資料,訪問檔案時間太多,執行速度慢。想要執行快一些,需要一次讀入大塊資料(比如1萬行),然後再用程式碼把資料拆成行來處理,過程會麻煩很多。

這是最簡單的計算了,沒有資料過濾和分組、排序等要求。其它求平均值、最大值與最小值的計算,跟這差不多。

如果要做分組彙總,程式碼就會麻煩得多。比如按州state分組後求各州的訂單金額總和,計算思路是這樣的:把各個分組儲存起來,然後一行行地讀各行中的state值,與儲存的組比較,找到了則將本行訂單金額加到組上,沒找到則新加入一個組。最後直到所有行都處理完。如果按多個欄位分組求多個統計值,比如按日期和州分組求訂單金額總和、最大訂單金額,程式的複雜度就會增加很多。

排序就更麻煩,還需要中間快取檔案。例如要按訂單金額從大到小排序,因記憶體不足,不能讀入所有資料來排序,計算思路是這樣的:先讀入5000行(讀多少行合適要根據記憶體而定)資料,排序後存到一個臨時檔案,再讀入5000行排序後存到另一個臨時檔案……直到所有資料處理完,最後對這些臨時檔案進行有序歸併——讀出每個臨時檔案的第一行,找出應該排在最前面的那一行,寫入到結果檔案,然後從那個臨時檔案中再讀出一行,繼續比較找出最前面的一行寫入結果檔案。按此方法不斷進行,直到所有資料行都寫入結果檔案。

用高階語言完成大檔案的處理確實是相當繁瑣的,對於非專業的程式人員,這幾乎是不可行的。

資料庫內建了許多計算演算法,對資料的查詢計算功能比較完善,效能也比較好,因此可以考慮將檔案型資料匯入到資料庫,生成資料庫表,再使用SQL來進行資料查詢計算。

這個辦法麻煩的地方就是要將檔案資料匯入資料庫,在匯入資料之前,先要建立資料表結構,指定每個列的資料型別,例如建立訂單表的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)
);

如果換個其它結構的資料檔案,那麼需要另寫一條建表的SQL。這裡特別需要指定資料型別,否則資料庫無法接受這些資料,而這是許多非專業程式設計師很不熟悉的地方。

對於匯入過程,資料庫一般都提供了直接匯入文字檔案的工具,而其它檔案則不能直接匯入,需要先轉換為文字檔案。Excel檔案還可以直接另存為文字,而對於XML檔案、JSON檔案、HTTP檔案等則又需要編寫程式將它們轉化為文字檔案,或者編寫程式先讀入檔案資料,生成一條SQL語句將資料寫入資料庫表中,無論如何都是非常繁瑣的事情。

資料存入到資料庫表以後,查詢計算確實就非常簡單了,分組、排序都挺容易,示例如下:

1、 求訂單金額總和

select sum(amount) from orders;

2、 按州分組求各州訂單金額總和

select state, sum(amount) from orders group by state;

3、 按訂單金額排序

select * from orders order by amount;

利用資料庫能很方便地查詢計算較大資料量,但把大檔案匯入資料庫卻很繁瑣而且有一定的專業技能要求。相對於高階語言的可行性大幅提高,但仍不夠好。

Python也沒有提供直接針對大檔案的處理語法,其實現思路和高階語言類似,如前面的計算訂單金額總和寫出來大概是:

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)

對於分組排序這類複雜一些的運算,如果實現前面說過的思路,用Python也非常麻煩,並不比java簡單多少。但Python有個pandas包,封裝了不少結構化資料的處理函式。如果是可讀入記憶體的小檔案,它可以很簡單地處理。可惜的是,pandas沒有針對大檔案提供直接分批處理的方法,還是要自己寫。使用pandas的複雜度比直接硬寫要小很多,但仍然要實現前面討論過的思路。

分組運算寫起來太麻煩,我們還是把上面的求和運算基於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後可以把文字看成一行行有結構的資料,不再需要自己拆分。

Python處理小檔案沒有大問題,但對於大檔案未提供有效支援。和高階語言相比,減輕的工作量很有限,可用程度不高,還不如資料庫。

另外,Python是個解釋執行語言,執行速度遠遠低於高階語言,處理大檔案時感受會更明顯。

esProc是專業的資料處理工具,與資料庫一樣內建了各種查詢計算演算法,可以直接使用文字、Excel、Xml、Json等檔案資料計算,不需要匯入資料的過程。

esProc提供了遊標,可以分批讀入資料再計算,這樣就可以很方便地處理大檔案了。象前面的例子,只要寫一行程式碼就可以:

1、 求訂單金額總和

=file(“orders.txt”).cursor@t().total(sum(amount))

如果想再加個過濾也很容易,比如只統計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還內建了平行計算,能充分利用多核CPU提高效能,這對於大檔案是特別有用的。比如分組彙總計算寫成:

=file(“orders.txt”).cursor@tm(;4).groups(state;sum(amount))

將會按 4 路並行方式計算,在普通多核筆記本上速度能提高 2-3 倍。相對來講,高階語言能實現並行,但不僅麻煩,而且只有專業程式設計師才會做。Python基本上不能並行。SQL得看資料庫,Oracle這種專業資料庫沒問題,而簡易的MySQL就不行。

esProc SPL擁有了SQL的優點,又避免了資料匯入的麻煩,對於桌面資料分析人員來講,是用於處理大檔案的最合適工具。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章