學會這些“套路”,excel 合併彙總都不是事
1. 問題背景
在日常工作中我們經常遇到具有相同表頭的 Excel 檔案,需要將它們合併到同一個工作表中再進行分析。當檔案比較多時,手工合併表格通常是件很麻煩的事情,而如果資料量很大,用 Excel 自帶的 VBA 來處理也會經常卡死。今天我就來分享一個專業的外部資料工具——集算器,掌握了集算器處理 Excel 多表合併的方法,就不用再編寫複雜且低效的 VBA 程式碼了,簡單的幾行 SPL(Structured Process Language,結構化過程處理語言)程式碼就能輕鬆搞定 Excel 檔案合併,即使檔案再多、再大也不用擔心。
2. 基本合併
A. 同一個 excel 中的多表合併
下面的例子是一個包含了銷售資料的 excel 檔案,其中包含了按月劃分的 3 個結構相同的 sheet 工作表,資料如下:
january_2013:
Customer ID |
Customer Name |
Invoice Number |
Sale Amount |
Purchase Date |
1234 |
John Smith |
100-0002 |
$1,200.00 |
2013/1/1 |
2345 |
Mary Harrison |
100-0003 |
$1,425.00 |
2013/1/6 |
3456 |
Lucy Gomez |
100-0004 |
$1,390.00 |
2013/1/11 |
4567 |
Rupert Jones |
100-0005 |
$1,257.00 |
2013/1/18 |
5678 |
Jenny Walters |
100-0006 |
$1,725.00 |
2013/1/24 |
6789 |
Samantha Donaldson |
100-0007 |
$1,995.00 |
2013/1/31 |
february_2013:
Customer ID |
Customer Name |
Invoice Number |
Sale Amount |
Purchase Date |
9876 |
Daniel Farber |
100-0008 |
$1,115.00 |
2013/2/2 |
8765 |
Laney Stone |
100-0009 |
$1,367.00 |
2013/2/8 |
7654 |
Roger Lipney |
100-0010 |
$2,135.00 |
2013/2/15 |
6543 |
Thomas Haines |
100-0011 |
$1,346.00 |
2013/2/17 |
5432 |
Anushka Vaz |
100-0012 |
$1,560.00 |
2013/2/21 |
4321 |
Harriet Cooper |
100-0013 |
$1,852.00 |
2013/2/25 |
march_2013:
Customer ID |
Customer Name |
Invoice Number |
Sale Amount |
Purchase Date |
6634 |
Poop Smith |
100-0014 |
$1,350.00 |
2013/3/4 |
8765 |
Tony Song |
100-0015 |
$1,167.00 |
2013/3/8 |
2345 |
Mary Harrison |
100-0016 |
$1,789.00 |
2013/3/17 |
6543 |
Rachel Paz |
100-0017 |
$2,042.00 |
2013/3/22 |
3456 |
Lucy Gomez |
100-0018 |
$1,511.00 |
2013/3/28 |
4321 |
Susan Wallace |
100-0019 |
$2,280.00 |
2013/3/30 |
在合併3個sheet的同時,我們還可以同時從每個sheet中篩選出欄位Customer Name, Sale Amount。最後的效果如下:
Customer Name |
Sale Amount |
John Smith |
1200 |
Mary Harrison |
1425 |
Lucy Gomez |
1390 |
Rupert Jones |
1257 |
Jenny Walters |
1725 |
....... |
........ |
Susan Wallace |
2280 |
集算器SPL指令碼:
A |
|
1 |
=file(”D:/sales_2013.xlsx”).xlsopen() |
2 |
=A1.conj([email protected]('Customer Name','Sale Amount';~.stname)) |
3 |
>file(“D:/result_2013.xlsx”). [email protected](A2;"merge_sheets") |
指令碼說明:
A1:開啟指定的 excel 檔案,建立一個由多個 sheet 工作表組成的序列。
A2:利用 conj 函式遍歷 A1 序列中所有的成員工作表,匯入每個工作表中指定列'Customer Name','Sale Amount',並將資料併合並。其中 xlsimport 函式匯入指定列,最後一列用分號; 隔開。 引數~.stname表示指定當前工作表,由於在 conj 函式的迴圈中,所以就可以逐個匯入所有工作表。同時,xlsimport 使用選項@t指明將工作表的第一行記錄作為欄位名。
A3:將序表 A2 作為一個新的工作表“merge_sheets”儲存到原來的 excel 檔案中,同樣用選項 @t 指明首行記錄為標題。
這段指令碼只有三句話,短小精幹之餘,邏輯清晰,也比較容易理解。下面我們再看看如何合併多個檔案中的多個工作表。
B. 不同 excel 中的多表合併
下面是要合併的多個 excel 檔案,它們都具有和上面例子相同的表結構,每個檔案記錄了當年的資料 :
集算器 SPL 指令碼:
A |
B |
|
1 |
for [email protected](”d:/excel/*.xlsx“) |
=file(A1).xlsopen() |
2 |
=B1.conj([email protected]('Customer Name','Sale Amount','Purchase Date';~.stname)) |
|
3 |
||
4 |
> file(“d:/result.xlsx”). [email protected](B3;"merge_data") |
合併的效果如下:
Customer Name |
Sale Amount |
Purchase Date |
John Smith |
1200 |
2013-01-01 |
Mary Harrison |
1425 |
2013-01-06 |
Lucy Gomez |
1390 |
2013-01-11 |
Rupert Jones |
1257 |
2013-01-18 |
...... |
...... |
...... |
Thomas Haines |
1346 |
2013-02-17 |
指令碼說明:
A1:通過 for 迴圈,遍歷指定目錄下的 excel 檔案,在 B1 到 B3 之間進行迴圈內處理.
B1:開啟目錄下的一個 excel 檔案,生成序列。
B2:匯入當前檔案中的每個 sheet 工作表中指定列'Customer Name','Sale Amount','Purchase Date'的資料,然後合併這些資料,與前面例子中的 A2 類似。
B3:將序表 B2 的資料與 @表示的本網格的值進行合併。
A4:將序表 B3 儲存到result.xlsx檔案中的 merge_data 工作表中。
上面程式用兩個迴圈就實現了多個 excel 檔案資料合併,外迴圈 for 遍歷了目錄下所有的 excel 檔案,內迴圈B1.conj則合併每個excel檔案中的多個sheet工作表的資料。
C. 合併出大檔案
前面第一個例子中的 A2、第二個例子中的 B3 都是在記憶體中裝載了合併後的 Excel 的所有資料,然後一次性寫出。如果檔案太多太大,那麼對記憶體的佔用也會很大,甚至超出記憶體允許的範圍。為此,我們可以採用流式追加的方式生成大檔案。
集算器 SPL 指令碼:
A |
B |
|
1 |
=file("D:/out.xlsx") |
|
2 |
for [email protected](”d:/excel/*.xlsx“) |
=file(A2).xlsopen() |
3 |
=if(A1.exists(),[email protected](),B2.xlsimport()) |
|
4 |
>[email protected](B3;"merger") |
合併後的效果如下:
Customer ID |
Customer Name |
Invoice Number |
Sale Amount |
Purchase Date |
1234 |
John Smith |
100-0002 |
1200 |
2013-01-01 |
2345 |
Mary Harrison |
100-0003 |
1425 |
2013-01-06 |
3456 |
Lucy Gomez |
100-0004 |
1390 |
2013-01-11 |
4567 |
Rupert Jones |
100-0005 |
1257 |
2013-01-18 |
...... |
...... |
...... |
...... |
...... |
6789 |
Thomas Haines |
100-0002 |
1346 |
2013-02-17 |
指令碼說明:
A1:開啟指定輸出的檔案。 A2: 遍歷目錄下需要合併的 excel 檔案。
B2:開啟一個需要合併的 excel 檔案。
B3:如果輸出檔案不存在,讀取 sheet 工作表的所有資料,包括標題行;如果輸出檔案已經有了,就通過 @t 選項指明第一行是標題,從第二行開始讀取資料。
B4:將 B3 讀取的資料以流式追加到 A1 指定的輸出檔案的 merger 工作表中。
通過流式逐個讀取檔案資料後追加寫入,這個方式適合將大量小的 excel 檔案合併成一個大的 excel 檔案。
3. 分組彙總
下面繼續以前面的銷售資料 excel 檔案為例。
A. 欄位分組
根據某個欄位或多個欄位實現分組計算,指令碼如下:
A |
B |
1 |
=file(”D:/sales_2013.xlsx”).xlsopen() |
2 |
=A1.conj([email protected](;~.stname)) |
3 |
=A2.groups('Customer ID';sum('Sale Amount'):Total,avg('Sale Amount'):Average) |
4 |
=A2.groups('Customer ID','Purchase Date';sum('Sale Amount'):Total) |
A3的效果:
Customer ID |
Total |
Average |
1234 |
2550 |
1275.0 |
2345 |
3214 |
1607.0 |
3456 |
2901 |
1450.5 |
4321 |
4132 |
2066.0 |
…… |
…… |
…… |
4567 |
1257 |
1257.0 |
A4的效果:
Customer ID |
Purchase Date |
Total |
1234 |
2013-01-01 |
1200 |
1234 |
2013-03-04 |
1350 |
2345 |
2013-01-11 |
1425 |
2345 |
2013-03-17 |
1789 |
…… |
…… |
…… |
9876 |
2013-02-02 |
1115 |
指令碼說明:
A1:開啟指定的 excel 檔案。
A2:讀取併合並檔案中所有 sheet 工作表的資料。
A3:在合併後的資料上按欄位 'Customer ID' 分組求銷售額、平均值
A4:在合併後的資料上按欄位 'Customer ID', 'Purchase Date' 分組求銷售額
B. 按序分組
集算器在進行分組聚合時還可以和相鄰資料行對比,在原資料已經有序時可以不再排序,從而節省時間,並保持原有的次序。假設原資料已經按日期排序,我們想按月份分組統計時,程式碼如下。
集算器 SPL 指令碼:
A |
B |
|
1 |
for [email protected](”d:/excel/*.xlsx“) |
=file(A1).xlsopen() |
2 |
=B1.conj([email protected](;~.stname)) |
|
3 |
||
4 |
=B3.derive(year('Purchase Date'):Year,month('Purchase Date'):Month) |
|
5 |
=A4.groups (month('Purchase Date'):Month;sum('Sale Amount'):Total,avg('Sale Amount'):Average) |
|
6 |
[email protected] (month('Purchase Date'):Month;sum('Sale Amount'):Total,avg('Sale Amount'):Average) |
A5分組效果:
Month |
Total |
Average |
1 |
272414 |
15134.111111111111 |
2 |
168038 |
9335.444444444445 |
3 |
357693 |
19871.833333333332 |
A6分組效果:
Month |
Total |
Average |
1 |
8992 |
1498.6666666666667 |
2 |
9375 |
1562.5 |
3 |
10139 |
1689.8333333333333 |
1 |
260221 |
43370.166666666664 |
2 |
103656 |
17276.0 |
3 |
101509 |
16918.166666666668 |
1 |
3201 |
533.5 |
2 |
55007 |
9167.833333333334 |
3 |
246045 |
41007.5 |
指令碼說明:
A1至 B3:在前面的例子中已經介紹,將同一目錄下所有相同結構的 excel 檔案的工作表進行合併。
A4:在序表 B3 的基本上重新構造了一個序表 A4,將日期拆分,新增年、月欄位。
A5:groups 跨年度按月分組彙總銷售額、平均值。
A6:[email protected] 按年月分組彙總銷售額、平均值, 帶引數 @o 實現分組歸併處理.
其中,A4 為資料記錄明細;A5 按月統計, 不區分年;A6 則按年月統計。這三個單元格中的資料展現出了不同層次的合併彙總結果。
C. 分段分組
將要統計的資料按條件分成幾段,統計各組的情況。
集算器 SPL 指令碼:
A |
B |
|
1 |
for [email protected](”d:/excel/*.xlsx“) |
=file(A1).xlsopen() |
2 |
=B1.conj([email protected](;~.stname)) |
|
3 |
||
4 |
=B3.groups(if ('Sale Amount'<1000,"1::<1000", if ('Sale Amount'<1500,"2::1000~~1500", if ('Sale Amount'<2000,"3::1500~~2000", if ('Sale Amount'<2500,"4::2000~~2500", "5::>=2500")))):Segment; count(1):Number,sum('Sale Amount'):Total) |
分組效果:
Segment |
Number |
Total |
1::<1000 |
22 |
8280 |
2::1000~~1500 |
9 |
11617 |
3::1500~~2000 |
6 |
10432 |
4::2000~~2500 |
4 |
8810 |
5::>=2500 |
13 |
759006 |
程式碼說明:
步驟A1到 B3 之間參考前面例子的說明。
A4:欄位'Sale Amount'金額的範圍分成 5 段,然後累計求出各段的數量及總數。
不過,這樣的寫法不夠方便,如果我們想調整分段方案,就需要修改 groups 函式的引數,而這個引數表示式還是比較複雜的。這時,我們還可以利用集算器中另一個 pseg 函式,更方便地實現這個功能,指令碼如下:
A |
B |
|
1 |
[0,1000,1500,2000,2500] |
|
2 |
for [email protected](”d:/excel/*.xlsx“) |
=file(A1).xlsopen() |
3 |
=B1.conj([email protected](;~.stname)) |
|
4 |
||
=B4.groups(A1.pseg(~.'Sale Amount'):Segment; count(1):Number,sum('Sale Amount'):Total) |
當然,我們也可以根據需要,按不同欄位不同要求進行分組,然後進行統計處理。例如,在統計班級考生成績時,各科成績可劃分成優、良、中、差、及格的分數區段,一次為條件進行統計。groups 用法還有很多,可以參考函式手冊中相應的章節。
D. 大資料分組
前面的例子中,要讀取的 excel 檔案都不能很大,也就是都能一次讀進記憶體。手工處理大檔案,也會有類似的要求,因為同時開啟多個檔案,意味著把這些檔案都裝入記憶體,很可能會超過機器的實體記憶體,而用 VBA 讀取的情況也差不多。這時,我們就需要用流式的方法讀取資料,不需一次讀進記憶體,而是邊讀取邊合併。
集算器 SPL 指令碼:
A |
B |
|
1 |
=file(“d:/tdata.xlsx”)[email protected]() |
|
2 |
for A1.count() |
[email protected](;A1(A2). stname) |
3 |
||
4 |
= B3.conjx() |
=A4.groups('Customer ID';sum('Sale Amount'):SaleTotal) |
>file(“d:/out.xlsx”)[email protected](B4;"Customer&Sales") |
篩選分組的效果:
Customer ID |
SaleTotal |
1234 |
107721792 |
2345 |
139041639 |
3456 |
137985543 |
4321 |
96170742 |
... |
... |
9876 |
37590417 |
程式碼說明:
A1:使用 @r 選項指明以流式開啟 excel 檔案。 A2:遍歷 excel 中的 sheet 工作表。
B2:使用 @c 選項指明以遊標方式匯入資料。
B3:將遊標B2彙集到B3序列中。
A4:將遊標序列B3的成員合併到一起組成新的遊標。
B4: 序列A4按‘Customer ID’分組累計‘Sale Amount’。
A5:將結果儲存。
通過遊標以流的方式迴圈從大檔案中讀取一段段資料,實現對資料的分組合並。
4. 去重處理
實際資料合併過程中,往往會出現資料重複的現象,重複資料肯定會影響到我們對資料的計算分析。下面介紹使用集算器 SPL 指令碼去除重複資料的幾種主要解決方法。
A. 主鍵去重
sales_2013中的資料,設其主鍵為’Invoice Number’,則根據主鍵去掉重複記錄。
A |
B |
1 |
=file(“d:/sales_2013.xlsx”).xlsopen() |
2 |
=A1.conj([email protected]('Customer Name', 'Invoice Number', 'Sale Amount';~. stname)) |
3 |
[email protected]('Invoice Number') |
4 |
>file(“d:/out.xlsx”). [email protected](A3;"result") |
合併去重後的資料:
Customer Name |
Invoice Number |
Sale Amount |
John Smith |
100-0002 |
1200 |
Mary Harrison |
100-0003 |
1425 |
Lucy Gomez |
100-0004 |
1390 |
Rupert Jones |
100-0005 |
1257 |
Jenny Walters |
100-0006 |
1725 |
…… |
…… |
…… |
Susan Wallace |
100-0019 |
2280 |
程式碼說明:
A1:開啟指定的 excel 檔案。
A2:匯入 sheet 工作表中指定列的資料。
A3:將序表 A2 按主鍵' Invoice Number '分組去重處理, 其中引數 @1 表示取每一個分組的第一條記錄組成排列後返回(注意是數字 1,不是字母 l)。
A4:將結果儲存。
各個 sheet> 中的資料是唯一的,但合併的資料不一定是唯一的,因此採用主鍵方式去掉重複資料。
B. 某欄位去重
根據資料表sales_2013中的某欄位去重處理, 檢視不同姓名的僱員記錄.
A |
B |
1 |
=file(“d:/sales_2013.xlsx”).xlsopen() |
2 |
=A1.conj([email protected]('Customer ID', 'Customer Name';~. stname)) |
3 |
=A2.id('Customer Name') |
4 |
[email protected](' Customer Name') |
5 |
>file(“d:/out.xlsx”). [email protected](A4;"result") |
程式碼說明:
A1:開啟指定的 excel 檔案。
A2:匯入 sheet 工作表中指定列的資料。
A3: 從序表 A2 中獲取不重複姓名的記錄
A4:從序表 A2中獲取不重複姓名的記錄列表。
A5:將序表 A4 另存,首行記錄為標題。
A3資料去重結果:
Member |
Anushka Vaz |
Daniel Farber |
Harriet Cooper |
…… |
Tony Song |
A4資料去重結果:
Customer ID |
Customer Name |
5432 |
Anushka Vaz |
9876 |
Daniel Farber |
4321 |
Harriet Cooper |
…… |
…… |
8765 |
Tony Song |
C. 聯合多欄位去重
有的記錄雖然有主鍵,但判斷是否為重複的記錄,需要用其它幾個欄位來確定,此時用多個欄位聯合來確定是否有重複記錄.
A |
B |
1 |
=file(“d:/sales_2013.xlsx”)[email protected]t() |
2 |
=file(“d:/sales_2014.xlsx”)[email protected]() |
3 |
=[A1,A2].merge('Customer ID', 'Purchase Date') |
4 |
[email protected]('Customer ID', 'Purchase Date') |
5 |
>file(“d:/out.xlsx”). [email protected](A4;"result") |
程式碼說明:
A1:匯入指定 excel 檔案的資料。
A2:同上。
A3:按欄位 'Customer ID', 'Purchase Date' 合併序表 A1,A2,返回序表 A3
A4:序表 A3 按 'Customer ID', 'Purchase Date' 分組去重。
A5:將結果儲存。 當然,也可以根據需要,參考更多的欄位進行分組合並,去掉重複記錄。
D. 記錄級去重
解決要合併的每個檔案中的記錄本身是不重複的,但合併後可能存在重複記錄。
A |
B |
|
1 |
=file(“d:/sales_2013.xlsx”)[email protected]() |
[email protected]('Invoice Number') |
2 |
=file(“d:/sales_2014.xlsx”)[email protected]() |
[email protected]('Invoice Number') |
3 |
=[B1,B2][email protected]() |
=A3.count() |
程式碼說明:
A1:匯入 excel 檔案的資料。
B1: 根據欄位'Invoice Number'去掉序表 A1中的重複資料
A2、B2:同上。
A3:合併序表 B1,B2 的資料,並去掉重複資料記錄返回序表 A3。選項 @u 表示序表成員按順序合併到一起組成新的序表, 去掉重複的記錄。
B3: 檢視合併後的資料記錄數。 [email protected]適合對多序表合併處理, 其中序表內部有序且無重複資料。
本文主要介紹了集算器處理同構 excel 多檔案合併、分組彙總資料及資料去重幾種情況,在實際工作中,還會遇到異構的情況,只要把需要合併的欄位讀成集算器的集合物件,後續處理和同構的邏輯是一樣的。學會了用這種專業資料處理工具,不僅能合併 Excel 檔案, 合併其他文字資料方法也是一致的,再也不用擔心合併資料中的多檔案、大檔案和結構差異問題了。
5.附件: