上一節(jié)講述了結(jié)構(gòu)化文本的一些基本運(yùn)算,本節(jié)繼續(xù)用案例講述二目運(yùn)算和綜合運(yùn)算。
二目運(yùn)算
集合運(yùn)算(文件比較)
現(xiàn)有文件f1.txt和f2.txt,第一行是列名,需要對(duì)文件中的Name字段進(jìn)行交集運(yùn)算。部分?jǐn)?shù)據(jù)如下:
文件f1.txt:
Name Dept
Rachel Sales
Ashley R&D
Matthew Sales
Alexis Sales
Megan Marketing
文件f2.txt:
Name Dept
Emily HR
Ashley R&D
Matthew Sales
Alexis Sales
Megan Marketing
代碼如下:
AB
1=file("E:\\f1.txt").import@t()=file("E:\\f2.txt").import@t()
2=[A1.(Name),B1.(Name)].isect()
函數(shù)isect用于集合間的交集運(yùn)算,A1.(Name)表示取出A1的Name列,形成一個(gè)集合,B1.(Name)表示取出B1的Name列。本案例的最終結(jié)果如下:
類似地,求并集用函數(shù)
union,差集可用diff,合集可用conj(相當(dāng)于union all)。也可以直接用運(yùn)算符來(lái)代替函數(shù),寫法更加簡(jiǎn)潔,比如交集,并集、差集、合集可以改寫為:
A1.(Name) ^ B1.(Name)
A1.(Name) & B1.(Name)
A1.(Name) \ B1.(Name)
A1.(Name) | B1.(Name)
上面的示例顯示了讀入文本文件并自動(dòng)拆分為字段后,僅取其中的某一列進(jìn)行集合運(yùn)算。那如果想不拆分字段,對(duì)整行數(shù)據(jù)一起比較呢?很簡(jiǎn)單,在導(dǎo)入的選項(xiàng)加上 s 即可,表示不拆分字段。但需要注意的是,不進(jìn)行拆分后,相當(dāng)于直接返回一個(gè)只有一列的序表,且此時(shí)的列名也沒(méi)有拆分,變成了Name(Tab)Dept,也就是此時(shí)的列名中包含了不可見(jiàn)字符 Tab,這列名是非法的,都沒(méi)法直接引用了。不過(guò)還好可以用序號(hào)來(lái)表示第幾列,此時(shí)的代碼如下:
AB
1=file("D:\\f1.txt").import@ts()=file("D:\\f2.txt").import@ts()
2=A1.(#1)^B1.(#1)
顯然,不拆分字段時(shí),肯定只有一列,與其得到一個(gè)非法的列名,還不如不要列名,直接返回成集合(序列)多好,此時(shí)需要額外加上選項(xiàng) i,表示只有一列數(shù)據(jù)時(shí),直接返回成序列。此時(shí)交集直接就是集合的運(yùn)算了,寫成 A1^B1 即可,代碼如下:
AB
1=file("D:\\f1.txt").import@tis()=file("D:\\f2.txt").import@tis()
2=A1^B1
上面兩種算法,得到的都是相同的結(jié)果:
歸并
現(xiàn)有table1.txt和table2.txt已按邏輯主鍵ID1、ID2排序?,F(xiàn)在要根據(jù)主鍵用table2更新table1,即主鍵相同其他字段不同時(shí)更新table1,主鍵不同時(shí)向table1插入數(shù)據(jù)。
源數(shù)據(jù)如下:
Table1Table2
ID1 ID2 Col1 Col2
2 B1 2B1 row
2 B2 2B2 row
3 B1 3B1
4 B3 4B3 Row
ID1 ID2 Col1 Col2
1 B1 diff row
2 B1 diff row
2 diff diff row
3 B1 3B1
用table2更新table1之后,table1應(yīng)當(dāng)如下:
ID1ID2Col1Col2
1B1diffrow
2B1diffrow
2B22B2row
2diffdiffrow
3B13B1
4B34B3row
代碼如下:
AB
1=file("D:\\table1.txt").cursor@t()=file("D:\\table2.txt").cursor@t()
2=[B1,A1].mergex@u(ID1,ID2)
3=file("D:\\result.txt").export@t(A2)
以游標(biāo)方式讀取table1.txt和table2.txt,按照邏輯主鍵用B1更新A1。函數(shù)mergex可進(jìn)行數(shù)據(jù)歸并,并保持結(jié)果仍有序,@u表示計(jì)算并集。最后將計(jì)算結(jié)果寫入新文件。
這個(gè)代碼使用了游標(biāo),不必考慮內(nèi)存對(duì)數(shù)據(jù)文件的大小的限制,因此可以處理非常大的文件。
如果文件本身無(wú)序,那么需要先排序再歸并,這時(shí)只需要將每個(gè)游標(biāo)附加一個(gè)排序表達(dá)式即可,A2可以改寫為:
[B1.sortx(ID1,ID2),A1.sortx(ID1,ID2)].mergex@u(ID1,ID2)
有序集合運(yùn)算
假設(shè)文件f1.txt和f2.txt已按Name和Dept排序,需要計(jì)算兩者的交集。源數(shù)據(jù)如下:
文件f1.txt:
Name Dept
Alexis Sales
Ashley R&D
Matthew Sales
Megan Marketing
Rachel Sales
文件f2.txt:
Name Dept
Alexis Sales
Ashley R&D
Emily HR
Matthew Sales
Megan Marketing
當(dāng)文件有序時(shí),可以通過(guò)歸并算法來(lái)實(shí)現(xiàn)集合運(yùn)算,其性能比普通集合運(yùn)算更高。代碼如下:
AB
1=file("D:\\f1.txt").import@t()=file("D:\\f2.txt").import@t()
2=[B1,A1].merge@i(Name,Dept)
3=file("D:\\result.txt").export@t(A2)
merge表示對(duì)序表進(jìn)行歸并,@i表示交集,@u表示并集,@d表示差集。
計(jì)算結(jié)果如下:
如果文件本身無(wú)序,可先用函數(shù)sort排序,但要注意小文件排序歸并比普通集合運(yùn)算更慢,所以本方法適合較大的文件。
關(guān)聯(lián)計(jì)算
emp.txt是用tab分隔的文本文件,其EId字段對(duì)應(yīng)sales.txt中的SellerId字段,現(xiàn)在要將emp.txt的Name、Dept、Gender這三個(gè)字段對(duì)齊到sales.txt。
源數(shù)據(jù)如下:
EIdStateDeptNameGenderSalaryBirthday
2New YorkFinanceAshleyF110001980/07/19
3New MexicoSalesRachelF90001970/12/17
4TexasHREmilyF70001985/03/07
5TexasR&DAshleyF160001975/05/13
6CaliforniaSalesMatthewM110001984/07/07
7IllinoisSalesAlexisF90001972/08/16
8CaliforniaMarketingMeganF110001979/04/19
代碼如下:
A
1=sOrder=file("D:\\sales.txt").import@t()
2=emp=file("D:\\emp.txt").import@t(EId,Name,Dept,Gender)
3=join@1(sOrder:s,SellerId;emp:e,EId)
4
=A3.new(s.OrderID, s.Client, s.SellerId, s.Amount, s.OrderDate,
e.Name, e.Dept, e.Gender)
函數(shù)join執(zhí)行連接運(yùn)算,并將兩個(gè)表改名為s和e,默認(rèn)內(nèi)連接,@1表示左連接,@f表示全連接。之后從連接的表中取得需要的字段,組成新的二維表。結(jié)果如下:
綜合運(yùn)算
多層關(guān)聯(lián)
下面的例子中共有5個(gè)數(shù)據(jù)源文件,其中訂單是事實(shí)表,客戶、產(chǎn)品、地區(qū)、供應(yīng)商是維表。我們需要過(guò)濾出客戶和供應(yīng)商屬于同一個(gè)地區(qū)的訂單,然后根據(jù)這些訂單按城市分組,匯總各城市的訂單數(shù)和訂單金額。
關(guān)系結(jié)構(gòu)如下圖:
代碼如下:
A
1=file("D:/files/orders.txt").import@t()
2=file("D:/files/customer.txt").import@t()
3=file("D:/files/product.txt").import@t()
4=file("D:/files/supplier.txt").import@t()
5=file("D:/files/region.txt").import@t()
6=A2.switch(city,A5:city)
7=A4.switch(city,A5:city)
8=A3.switch(sid,A4:sid)
9=A1.switch(pid,A3:pid; cid,A2:cid)
10=A1.select(pid.sid.city.region==cid.city.region)
11=A10.groups(cid.cid:cid;count(oid):count,sum(price*quantity):amount)
讀入文本,建立事實(shí)表和維表之間的關(guān)聯(lián),之后按關(guān)聯(lián)關(guān)系查詢訂單,再進(jìn)行分組匯總,其中函數(shù)switch用于建立外鍵關(guān)聯(lián)。
異構(gòu)文件比較
Data.txt是tab分隔的文本,共有6個(gè)字段,其中here字段是分號(hào)分隔的字符串。另有文件list是單列數(shù)據(jù)。現(xiàn)在要比較這兩個(gè)文件,如果某條記錄的here字段拆分后和List.txt中的任意一行匹配,則將這條記錄輸出到result.txt中。
源數(shù)據(jù)如下:
List.txt
Gee
Whiz
Lol
Data.txt
field1field2field3herefield5etc
AB2Gee;Whiz;Hello1312
AB2Gee;Whizz;Hi5632
E
4Btm;Lol162
T
3Whizz133
代碼如下:
A
1=file("d:\\Data.txt").import@t()
2=file("d:\\List.txt").read@n()
3=A1.select(here.array(";")^A2!=[])
4=file("d:\\result.txt").export@t(A3)
A3格子的代碼中使用函數(shù)select進(jìn)行查詢,條件為here字段用array拆分為字符串序列后,再跟A2序列求交(“^”)集,結(jié)果不為空(“[]”)。
結(jié)果如下:
field1field2field3herefield5etc
AB2Gee;Whiz;Hello1312
AB2Gee;Whizz;Hi5632
E
4Btm;Lol162
多級(jí)目錄文件抽取
目錄“D:\files”包含多級(jí)子目錄,每個(gè)目錄下都有許多文本格式的文件,從這些文件中讀取指定的行(比如第二行),并將這些數(shù)據(jù)寫入新的文件d:\result.txt。
代碼如下:
A
1=directory@p(path)
2=A1.(file(~).cursor@s())
3=A2.((~.skip(1),~.fetch@x(1)))
4=A3.union()
5=file("d:\\result.txt").export@a(A4)
6=directory@dp(path)
7=A6.(call("c:\\readfile.dfx",~))
參數(shù)path的初始值設(shè)為“D:\files”,表示從該目錄開始抽取數(shù)據(jù),之后遞歸調(diào)用本腳本(c:\readfile.dfx),每次傳入給參數(shù)path的值不同。
函數(shù)directory用來(lái)讀出參數(shù)path中根目錄下的文件列表,選項(xiàng)@p表示文件名帶全路徑,@d表示只取目錄名。
~.skip(1)表示跳過(guò)一行。
~.fetch@x(1)表示從當(dāng)前位置讀取一行(即第二條)數(shù)據(jù)后立刻關(guān)閉游標(biāo)。
分組拆分寫出
文件sales.txt存儲(chǔ)了大量銷售訂單,現(xiàn)在將該文件按年和月拆分為多個(gè)文件,文件名格式為“年-月.txt”。
代碼如下:
A
1=file("D:\\ sales.txt").import@t()
2=A1.group(string(OrderDate,"yyyy-MM");~)
3=A2.run(file("d:\\temp\\"+#1+".txt").export(#2))
按年月分組解析分組,再按組循環(huán),并寫入文件。比如文件2009-01.txt,文件內(nèi)容如下:
65 YZ 8 29600.0 2009-01-06
62 JAXE 11 8134.0 2009-01-06
64 HP 13 20000.0 2009-01-02
60 PWQ 16 3430.0 2009-01-05
63 SJCH 16 5880.0 2009-01-02
61 SJCH 19 1078.0 2009-01-08
源數(shù)據(jù)超過(guò)內(nèi)存時(shí)應(yīng)用函數(shù)cursor讀文件,如組內(nèi)數(shù)據(jù)仍超內(nèi)存,應(yīng)當(dāng)使用函數(shù)groupx分組,但代碼結(jié)構(gòu)無(wú)變化。
綜合運(yùn)用(庫(kù)存計(jì)算)
文件Stock.txt存儲(chǔ)貨物的出入庫(kù)記錄,同種貨物每天可能出入庫(kù)多次,也可能連續(xù)幾天無(wú)任何貨物出入庫(kù),貨物初值為0,入庫(kù)用In表示,出庫(kù)用Out表示,需要計(jì)算出所有貨物的每日庫(kù)存。源數(shù)據(jù)如下:
date name quantity flag
2014-04-01 Item1 15 In
2014-04-01 Item1 4 In
2014-04-02 Item1 3 In
2014-04-02 Item1 10 Out
2014-04-03 Item1 3 In
2014-04-04 Item1 5 Out
2014-04-07 Item1 4 In
2014-04-10 Item1 2 Out
2014-04-01 Item2 20 In
2014-04-02 Item3 30 In
2014-04-03 Item3 14 Out
代碼如下:
AB
1=file("D:\\stock.txt")
.import@t()2=A1.group(name,date;~.select(flag==”In”).sum(quantity):in,~.select(flag==”O(jiān)ut”).sum(quantity):out)
3=A2.group(name)=periods((t=A2.id(date)).min(),t.max(),1)
4for A3=A4.align(B3,date)
5
>c=0
6
=B4.new(A4.name:name,
B3(#):date,
c:Opening,
in,
(b=c+in):Total,
out,
(c=b-out):Close)
7
>A8=A8|B6
8
代碼說(shuō)明:先用A2匯總出所有貨物每日的出入庫(kù)總數(shù),再按最早、最晚日期算出完整的日期列表,存于B3。然后按貨物分組,循環(huán)每組數(shù)據(jù),并將當(dāng)前組與B3對(duì)齊,在B6中計(jì)算出當(dāng)前貨物的每日庫(kù)存,計(jì)算完成后將所有庫(kù)存結(jié)果保存到A8。
計(jì)算完成后A8結(jié)果如下: