2016年6月7日 星期二

Apache Pig 語法教學

Pig 語法教學(StackEdit)

目錄



前言

大家好我是 King 我在寫這篇文章的時是我大一的時候因為要比賽所以才開始學習 Pig , 但是當時在學習 Pig 的時候根本沒有「繁體中文」的教學文 , 因為這個技術實在是太新了所以我只好看原文的官方教學來學習 , 在這裡在建議大家要走資工的朋友們英文很重要而現在我也還在惡補我爛到不行的英文 , 廢話說完了以下兩個是 Pig 官方的教學手冊。

連結 :

入門

PigHDFS

Pig 的系統中所要使用的檔案與處理過後的檔案都是放置在 HDFS 的檔案統當中 , 換句話說就是在開始使用 Pig 之前就必須先把需要的資料上傳至 HDFS 檔案系統中這樣 Pig 才可以把檔案讀取近來歐 , 相反地儲存也是同樣的 Pig 會將檔案儲存在你指定的 HDFS 目錄當中 , 這邊有一個非常的重要的觀念要教大家就是「Pig 在儲存檔案的時候當是以目錄的方式做儲存」

啟動 PigPig 的執行方式

要啟動 Pig 有兩種方式 , 現在來介紹地一種方式「交談式」在這邊你可以像是跟 Pig 對話一般將你要的程式碼一行一行的打進去而且可以覆蓋之前所打的指令 , 在剛開始規劃程式的時候非常的好用可以一直跟改之前所打的程式碼並且可以隨時查看結果 , 但唯一的缺點就是不好查看之前所打的程式碼所以所以必須在邊規劃程式的時候就邊開一個 Notepad++1 把之前打的程式記錄下來在使用第二種方法將他執行

dsa01@cla01:~$ pig
grunt>

第一種「交談式」的方法介紹完了就要來介紹類似腳本的啟動方式了 , 在這邊你必須先用任意一個 Text Edit 新建一個附檔名為 .pig 的檔案 , 然後在檔案裡面把上面「交談式」所打的程式打進去檔案裡面 , 之後每次要執行的時候就只要下達以下指令就可以啟動了 , 在 Linux 下我推薦使用 Emacs2 作為 Text Edit

dsa01@cla01:~$ pig -f [.pig檔案路徑]

接下來就要說說 Pig 的執行方式了 , 但是在這之前有一個觀念要傳授給大家 , 「Pig 是一支 Java 程式然而 Pig 的工作就是在幫你快速有效率的完成一支 Java 的程式來處理你給他的資料」 , 但是一般人認為 Pig 在讀取每一條指令的時候其實背後已經在操作資料了 , 但其實不是 , Pig 中有一條指令是 DUMP 而這條指令的工作其實是非常大的 , 他並不光光只是把結果顯示出來而已 , 就像剛剛說的一樣「Pig」是在幫你寫 Java 程式 , 然而 DUMP 的工作就是把你打的指令通通 撰寫 -> 編譯 -> 執行 -> 顯示 , 所以在下達 DUMO 指令時電腦才會運算的比較久而在一般的指令下達時 Pig 只是在記錄你下了什麼指令而已

認識資料的個部分名稱

A = {1,(2,3),{(4,5),(6,7)}}

上面這一行資料如果要用說的方式來表示是這樣的「A 關連 {1,(2,3),{(4,5),(6,7)}
"1" 沒有任何括號的是一個 字段(Fields) , 而一個 字段(Fields) 也就是一個 數據(Data)
"(2,3)" 被小括號包住的是一個 元組(Tuples) , 而在 元組(Tuples) 裡面可以有若干個 字段(Fields) 或是一個 字段(Fields) ,例如:(1),(1,2),(1,2,3)
"{(4,5),(6,7)}" 被大括號包住的是一個 包(Bags) , 一個 包(Bags) 可以有若干個 元組(Tuples) , 也可以稱為是 元組(Tuples) 的集合

認識 Pig 的資料型態

資料名稱(英) 資料名稱(中) 敘述
普通資料類
int 整數 Signed 32-bit integer
long 長整數 Signed 64-bit integer
float 浮點數 32-bit floating point
double 精確浮點數 64-bit floating point
NULL 空值
陣列類
chararray 字串 Character array (string) in Unicode UTF-8 format
bytearray 位元組陣列 Byte array (blob)
Pig 資料類型
tuple 元組 An ordered set of fields.
bag An collection of tuples.
map 鍵與值 A set of key value pairs.

認識 Pig 的註解

--單行著註解,有效範圍只有在 --(雙減號) 與 Entel(換行) 之前/*多行註解,有效範圍在 /* 與 */ 之間,換行照樣有效*/

文章中會使用的符號

"..." -> 以此類推的意思

文章中會使用的資料

在之後的介紹中會出現許多的舉例 , 所以在這邊就先把以後會用到的原始資料放上來 , 之後久不再刻意 cat 資料了

a.txt

1,2,3
4,5,6

回到目錄


b.txt

1,2,3,6,9,8
1,2,3,5,9,6
5,9,6,3,5,8
5,9,6,2,4,8
3,2,4,8,9,5
3,6,9,8,5,4

回到目錄


c.txt

20150922,017,u016045383
20150821,001,u015769781
20150710,008,u008137791
20150830,018,u014711537
20150627,001,u014616537
20150830,002,u007591963
20150726,038,u014540733
20150715,040,u014792743
20150627,012,u006343463
20150707,018,u005655603
20150911,005,u008969559
20150820,018,u015235929
20150919,040,u003234985
20150602,039,u008469313
20150617,040,u201308777
20150916,021,u002529119

回到目錄


d.txt

Alice,turtle,1
Alice,goldfish,5
Alice,cat,2
Bob,dog,2
Bob,cat,2

回到目錄


e.txt

John,fl,3.9F
John,wt,3.7F
John,sp,4.0F
John,sm,3.8F
Mary,fl,3.8F
Mary,wt,3.9F
Mary,sp,4.0F
Mary,sm,4.0F

回到目錄


f.txt

8,9,0,1,8,9,1,1
2,3,4,5,2,3,4,5
6,7,3,7,2,2,3,7

回到目錄

指令介紹

這裡所介紹的指令是我自己有實際使用過的指令 , 換句話說就是我沒有用過的就不會介紹出來 , 以免我自己不清楚而教了錯誤的指令 , 所以如果我這邊沒有介紹到的就請移駕到 Pig 參考手冊 1Pig 參考手冊 2 尋找你要的答案吧 !

LOAD

語法

簡易型的載入敘述 , 比較不建議使用這種方式載入檔案 , 原因會在範例裡加以說明

代號 = LOAD '[檔案路徑]';

進階型的載入敘述 , 這條敘述不只是載入檔案 , 還包含著「設定分隔符號」與「欄位的代號與資料的型態」

代號 = LOAD '[檔案路徑]' using PigStorage('[分隔符]') AS ([欄位名稱]:[資料型別], [欄位名稱]:[資料型別], ...); 
  • LOAD '[檔案路徑]'

    • 載入資料的時候要使用的敘述 , '[檔案路徑]' 這邊要打上存在 HDFS 的檔案路徑
  • using PigStorage('[分隔符]')

    • 敘述中其中的這一段就是設定要Pig系統在讀取資料的時候要用什麼符號來辨識欄位的分隔 , 舉例來說如果你讀取的是 Excel(.csv) 的檔案在Excel的系統中是使用 , 號來做欄位的分隔 , 所以這邊的程式碼就邀這樣寫using PigStorage(',') , 注意要要依照自己要讀取的檔案來設定分隔符號 , 不要傻傻的都填上 , 這樣你會發現讀取進來的資料全部通通都擠在一個欄位裡面
  • AS ([欄位名稱]:[資料型別], [欄位名稱]:[資料型別], ...)

    • 這裡就是要分別給每一個欄位設定「欄位的名稱」與「資料的型態」,詳細的說明於範例中做加以說明

範例

1 - 1

A = LOAD '/teaching/a.txt';

DUMP A;
(1,2,3)(4,5,6)

DESCRIBE A;
Schema for A unknown.
  • A = LOAD '/teaching/a.txt';
    • 這邊先示範一次使用不建議的方式載入 a.txt 文件
  • DUMP A;
    (1,2,3)
    (4,5,6)
    • DUMP 指令是用來查看目前敘述所操作的出來的資料 , 所以係在看 A 敘述所載入的資料是正常的
  • DESCRIBE A;
    Schema for A unknown.
    • DESCRIBE 指令是用來查看目前敘述所操作的出來的資料結構 , 而當我們下達這指令時系統回復我們 Schema for A unknown. 翻譯成中文意思是 A 的資料結構不明 , 所以這樣就知道為什麼不建議使用簡易的方式載入資料 , 雖然在之後還會交到如何重新設定資料類型 , 但還是建議大家在載入的時候就做好設定

1 - 2

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

DUMP A;
(1,2,3)(4,5,6)

DESCRIBE A;
A: {a: int,b: int,c: int}
  • A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);
    • 接下來就是要使用推薦的方式載入 a.txt 文件 , 在本次範例中就可以看出實際的差別了
  • DUMP A;
    (1,2,3)
    (4,5,6)
    • 這裡在顯示的時候跟上一個範例是一樣的
  • DESCRIBE A;
    A: {a: int,b: int,c: int}
    • 實際的差別就會差別這邊顯示出來接下來就要來說明一下得到的這個資料結構的意思吧 , A: 表示是這個 A 敘述所處理過的資料結構 , 在 {a: int,b: int,c: int} 中的 a, b, c 都是欄位名稱的意思 , 在第一筆資料內 a 指的是 1b 指的是 2c 指的 3 , 而 int 指的就是資料的型態了 !

DUMP

將下達的指令 撰寫 -> 編譯 -> 執行 -> 顯示 , 如果你看不懂這句話的話就表示你沒有看 啟動 PigPig 的執行方式 或是忘記了 , 所以還不趕快回去複習裡面的觀念可是很重要的 !!! 但是不要以為 DUMP 永遠指能放在程式的最後面歐 , 也不要以為他在程式裡面指能出現一次歐 , 如果你需要看很多條敘述所操作出來的指令你可以在一份程式你呼叫 DUMP 無限次 , 而唯一需要遵守的規則指有兩條 1. 必須擺在你要看的敘述的後面2. 必須是存在的 [代號]

語法

DUMP [代號];

範例

2 - 1

A1 = LOAD '/teaching/a.txt';

DUMP A1;
(1,2,3)(4,5,6)

A2 = LOAD '/teaching/b.txt';

DUMP A2;
(1,2,3,6,9,8)
(1,2,3,5,9,6)
(5,9,6,3,5,8)
(5,9,6,2,4,8)
(3,2,4,8,9,5)
(3,6,9,8,5,4)
  • A1 = LOAD '/teaching/a.txt';
    • 只是要測試 DUMP 所以用簡易的方式載入 a.txt
  • DUMP A1;
    (1,2,3)
    (4,5,6)
    • 將像剛剛說的一樣只要在你要看的敘述後面就可以放 DUMP 來檢視你要看的資料
  • A2 = LOAD '/teaching/b.txt';
    • 只是要測試 DUMP 所以用簡易的方式載入 b.txt
  • DUMP A2;
    (1,2,3,6,9,8)
    (1,2,3,5,9,6)
    (5,9,6,3,5,8)
    (5,9,6,2,4,8)
    (3,2,4,8,9,5)
    (3,6,9,8,5,4)
    • DUMP 可以放在敘述下的任意位置 , 也可以在程式中放置任意多的 DUMP

DESCRIBE

在上面幾個範例中都有使用到的資料結構檢視指令 , 我自己在寫程式的時候都會把 DUMPDESCRIBE 一起使用

語法

DESCRIBE [代號];

範例

3 - 1

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

DUMP A;
(1,2,3)
(4,5,6)

DESCRIBE A;
A: {a: int,b: int,c: int}
  • DUMP A;
    (1,2,3)
    (4,5,6)

    DESCRIBE A;
    A: {a: int,b: int,c: int}

    • 一起服用(?

FOREACH

對資料中的每一筆做操作 , 可以做 增加欄位刪除欄位資料型態轉換欄位命名欄位運算 還有很多很多…

語法

[代號] = FOREACH [代號] GENERATE [欄位], [欄位], [欄位], ...;

範例

4 - 1

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = FOREACH A GENERATE a, b;

DUMP B;
(1,2)(4,5)

DESCRIBE B;
B: {a: int,b: int}

C = FOREACH A GENERATE a, b, c, 999;

DUMP C;
(1,2,3,999)
(4,5,6,999)

DESCRIBE C;
C: {a: int,b: int,c: int,int}

D = FOREACH A GENERATE a, b, 999, c;

DUMP D;
(1,2,999,3)
(4,5,999,6)

DESCRIBE D;
D: {a: int,b: int,int,c: int}

E = FOREACH A GENERATE a, b, (double)999, c;

DUMP E;
(1,2,999.0,3)
(4,5,999.0,6)

DESCRIBE E;
E: {a: int,b: int,double,c: int}

F = FOREACH A GENERATE a, b, '999', c;

DUMP F;
(1,2,999,3)
(4,5,999,6)

DESCRIBE F;
F: {a: int,b: int,chararray,c: int}
  • B = FOREACH A GENERATE a, b;
    • 這邊的敘述指留下 ab 的欄位
  • DUMP B;
    (1,2)
    (4,5)
    • DUMP 後的資料已經成功去除 C 欄位的資料了
  • DESCRIBE B;
    B: {a: int,b: int}
    • 資料結構中也可以看的出來
  • C = FOREACH A GENERATE a, b, c, 999;
    • 這條敘述的操作是要在每一筆資料的最後增加一個欄位並且在欄位裡填上 999
  • DUMP C;
    (1,2,3,999)
    (4,5,6,999)
    • 這邊就顯示出來剛剛增加的欄位與 999 是成功的
  • DESCRIBE C;
    C: {a: int,b: int,c: int,int}
    • 在觀看資料結構的時候就可以發現剛剛增加的 999 欄位被 Pig 預設為 int 的資料型態 , 如果增加欄位資料是 999.0 就會變成 duoblelong
  • D = FOREACH A GENERATE a, b, 999, c;
    • 這邊要說明的是要增加的欄位加在那邊都可以不一定要加在最後面 , 當然如果你把最後一個 c 去掉變成 D = FOREACH A GENERATE a, b, 999; 就會發現 c 欄位不會顯示了!
  • DUMP D;
    (1,2,999,3)
    (4,5,999,6)
    • 欄位也可以亂插 (?
  • DESCRIBE D;
    D: {a: int,b: int,int,c: int}
    • 預設的資料結構一樣也是 int
  • E = FOREACH A GENERATE a, b, (double)999, c;
    • 如果你在加入欄位的時候沒有指定資料型態 Pig 對數字的預設是 int , 相對的你也可以自訂所以我就在 999 前面加一個 (double) 就可以一便設定資料型態
  • DUMP E;
    (1,2,999.0,3)
    (4,5,999.0,6
    • 因為上一條敘述所加入的 999double 的資料型態 , 而 double精確浮點數 的意思所以會顯示有小數點的 999.0
  • DESCRIBE E;
    E: {a: int,b: int,double,c: int}
    • 這邊證實了我們設定的 double 是正常的
  • F = FOREACH A GENERATE a, b, '999', c;
    • 有沒有發現這邊的 999 前後加了 ' 號 , 所以這邊的 999 已經不是個 int 了他是一個 chararray(字串) , 不管任何資料只要加了 ' 號在前後就表示這是 chararray(字串) 資料 , 而即便 chararray(字串) 資料內的資料是數字也不能用於 計算 的函式 , 如果需要 計算 就要用到之後教的 資料轉型
  • DUMP F;
    (1,2,999,3)
    (4,5,999,6)
    • 看起來還是數字?不要被騙了!
  • DESCRIBE F;
    F: {a: int,b: int,chararray,c: int}
    • 真相只有一個(? 他就是 chararray(字串)

4 - 2

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = FOREACH A GENERATE (chararray)a, (double)b, c;

DUMP B;
(1,2.0,3)
(4,5.0,6)

DESCRIBE B;
B: {a: chararray,b: double,c: int}

C = FOREACH A GENERATE '11ASD';

D = FOREACH C GENERATE (int)$0;

DUMP D;
()()

DESCRIBE D;
D: {int}
  • B = FOREACH A GENERATE (chararray)a, (double)b, c;
    • 只要在欄位名稱前加入你要轉型的資料名稱就可以轉型
  • DUMP B;
    (1,2.0,3)
    (4,5.0,6)

    DESCRIBE B;
    B: {a: chararray,b: double,c: int}

    • 成功轉型結果
  • C = FOREACH A GENERATE '11ASD';
    • 這邊把全部的欄位都刪除並且增加欄位資料 11ASD
  • D = FOREACH C GENERATE (int)$0;
    • 因為將全部的欄位都刪除後新增加的欄位並沒有重新命名所以用 $0 表示
  • DUMP D;
    ()
    ()
    • 資料轉換失敗因為如果要把 chararray 轉成 int 就必須全部都是 數字 才會成功 !
  • DESCRIBE D;
    D: {int}
    • 雖然資料顯示不出來但是欄位的屬性來是不變的

4 - 3

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = FOREACH A GENERATE a AS x, b AS y, c AS z;

DUMP B;
(1,2,3)
(4,5,6)

DESCRIBE B;
B: {x: int,y: int,z: int}
  • B = FOREACH A GENERATE a AS x, b AS y, c AS z;
    • 這邊要展示的是 重新命名 的方法
  • DUMP B;
    (1,2,3)
    (4,5,6)
    • 重新命名不會跟改到資料本生
  • DESCRIBE B;
    B: {x: int,y: int,z: int}
    • a、b、c 改成 x、y、z

4 - 4

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = FOREACH A GENERATE a+b+c AS sum:int;

DUMP B;
(6)
(15)

DESCRIBE B;
B: {sum: int}

C = FOREACH A GENERATE (a, b, c) AS data, a+b+c AS sum:chararray;

DUMP C;
((1,2,3),6)
((4,5,6),15)

DESCRIBE C;
C: {data: (a: int,b: int,c: int),sum: chararray}
  • B = FOREACH A GENERATE a+b+c AS sum:int;
    • 將全部的欄位的值加起來並且命名為 sum 設定資料型態為 int , 是不是覺得 sum:int 很熟悉呢?其實就跟 LOAD 是一樣的設定方式
  • DUMP B;
    (6)
    (15)
    • 這裡所顯示的是 a+b+c
  • DESCRIBE B;
    B: {sum: int}
    • 資料的結構符合設定的規則
  • C = FOREACH A GENERATE (a, b, c) AS data, a+b+c AS sum:chararray;
    • 這邊我們把 a、b、c 欄位包成一個 元組(Tuples) 並且將這個新的欄位命名為 data , 而第二欄位就跟上面的敘述一樣把它們加起來
  • DUMP C;
    ((1,2,3),6)
    ((4,5,6),15)
    • $0元組(Tuples) 是原本的資料 , $1 則是總合
  • DESCRIBE C;
    C: {data: (a: int,b: int,c: int),sum: chararray}
    • 元組(Tuples) 命名為 data $1 命名為 sum 資料型態為 chararray

4 - 5

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = FOREACH A GENERATE *;

DUMP B;
(1,2,3)
(4,5,6)

DESCRIBE B;
B: {a: int,b: int,c: int}
  • B = FOREACH A GENERATE *;
    • 說實在我也不知道為什麼要做這個功能 , 因為有沒有這個敘述其實沒有差別 , 他會原封不動把資料掃過一變
  • DUMP B;
    (1,2,3)
    (4,5,6)
    • 看的出來什麼都沒有動
  • DESCRIBE B;
    B: {a: int,b: int,c: int}
    • 這邊也一樣

FILTER

可以使用 正規表示式 或是一般的 表達式 作過濾

語法

一般的 表達式 使用的敘述

[代號] = FILTER [代號] BY ([表達式]);

一般的 正規表示式 使用的敘述

[代號] = FILTER [代號] BY [欄位] MATCHES '[正規表示式`]';

兩個 表達式 的敘述

[代號] = FILTER [代號] BY ([表達式]) OR ([表達式]);

與上一個敘述不一樣的地方是在於第二個表達式的地方多了 NOT , 而 NOT 就表示要不等於這個 表達式

[代號] = FILTER [代號] BY ([表達式]) OR (NOT ([表達式]));

範例

5 - 1

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = FILTER A BY (d == 8);

DUMP B;
(3,2,4,8,9,5)
(3,6,9,8,5,4)

DESCRIBE B;
B: {a: int,b: int,c: int,d: int,e: int,f: int}

C = FILTER A BY (d > 4);

DUMP C;
(1,2,3,6,9,8)
(1,2,3,5,9,6)
(3,2,4,8,9,5)
(3,6,9,8,5,4)

DESCRIBE C;
C: {a: int,b: int,c: int,d: int,e: int,f: int}

D = FILTER A BY (e < 5);

DUMP D;
(5,9,6,2,4,8)

DESCRIBE D;
D: {a: int,b: int,c: int,d: int,e: int,f: int}
  • B = FILTER A BY (d == 8);
    • 指留下欄位 b 等於 8 的資料
  • C = FILTER A BY (d > 4);
    • 指留下欄位 b 大於 4 的資料
  • D = FILTER A BY (e < 5);
    • 指留下欄位 e 小於 5 的資料

5 - 2

A = LOAD '/teaching/c.txt' using PigStorage(',') AS (dates:chararray, catid:chararray, userid:chararray);

B = FILTER A BY dates MATCHES '201506.*';

DUMP B;
(20150627,001,u014616537)
(20150627,012,u006343463)
(20150602,039,u008469313)
(20150617,040,u201308777)

DESCRIBE B;
B: {dates: chararray,catid: chararray,userid: chararray}
  • B = FILTER A BY dates MATCHES '201506.*';
    • 使用 正規表示式 來過濾非 '201505.*' 的資料 , 而 .* 裡面的 . 表示「任意的字元」* 表示出現的次數不限至 , 所以兩個加起來就表是 後面不管接什麼通通都可以 !
  • DUMP B;
    (20150627,001,u014616537)
    (20150627,012,u006343463)
    (20150602,039,u008469313)
    (20150617,040,u201308777)
    • 當然的… 留下了這些資料

GROUP

依照指定的 [欄位] 將資料做分類

語法

[代號] = GROUP [代號] BY [欄位];

範例

6 - 1

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A BY a;

DUMP B;
(1,{(1,2,3,6,9,8),(1,2,3,5,9,6)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,3,5,8),(5,9,6,2,4,8)})

DESCRIBE B;
B: {group: int,A: {(a: int,b: int,c: int,d: int,e: int,f: int)}}
  • B = GROUP A BY a;
    • 使用欄位 a 的資料做分類

6 - 2

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A BY a;

DUMP B;
(1,{(1,2,3,6,9,8),(1,2,3,5,9,6)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,3,5,8),(5,9,6,2,4,8)})

DESCRIBE B;
B: {group: int,A: {(a: int,b: int,c: int,d: int,e: int,f: int)}}

C = FOREACH B GENERATE group, COUNT(A);

DUMP C;
(1,2)
(3,2)
(5,2)

DESCRIBE C;
C: {group: int,long}
  • C = FOREACH B GENERATE group, COUNT(A);
    • 這邊的 group 並不是 關鍵字(Keyword) 而是 $0 的一個欄位名稱而已 , 如果 DESCRIBE B; 出來的架構是 B: {NewName: int,A: {(a: int,b: int,c: int,d: int,e: int,f: int)}} 而我們的敘述就要改成 C = FOREACH B GENERATE NewName, COUNT(A);
    • COUNT(A) 就是剛剛說得計算功能 , 他只能計算 包(Bags) 裡面有幾個 元組(Tuples) , 所以在這邊就是分別計算出 $01 的資料有幾個 3 的有幾個 5 的有幾個
  • DUMP C;
    (1,2)
    (3,2)
    (5,2)
    • 所以這邊就可以知道 $01 的資料有 23 的資料有 2 5 的資料也有 2
  • DESCRIBE C;
    C: {group: int,long}
    • COUNT() 所算來的資料類型預設是 long

6 - 3

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A ALL;

DUMP B;
(all,{(1,2,3,6,9,8),(1,2,3,5,9,6),(5,9,6,3,5,8),(5,9,6,2,4,8),(3,2,4,8,9,5),(3,6,9,8,5,4)})

DESCRIBE B;
B: {group: chararray,A: {(a: int,b: int,c: int,d: int,e: int,f: int)}}

C = FOREACH B GENERATE group, COUNT(A);

DUMP C;
(all,6)

DESCRIBE C;
C: {group: chararray,long}
  • B = GROUP A ALL;
    • 這便要示範的是把每一筆資料都 包(Bags) 起來
  • DUMP B;
    (all,{(1,2,3,6,9,8),(1,2,3,5,9,6),(5,9,6,3,5,8),(5,9,6,2,4,8),(3,2,4,8,9,5),(3,6,9,8,5,4)})
    • 這邊看到他麼資料是一個 ALL + 你原本的資料
  • C = FOREACH B GENERATE group, COUNT(A);
    • 這邊他保留了前面的 ALL 並且計算 包(Bags) 裡面有幾個 元組(Tuples)
  • DUMP C;
    (all,6)
    • 這樣做就是要資料我們讀進去的檔案總共有幾筆資料 , 所以這邊顯示出來我們全部有 6 筆資料

6 - 4

這邊個範例比較特別所以我直接在範例內用註解做解說
不知道各位有沒有想過在 包(Bags) 裡面的 元組(Tuples) 要如何處理呢?這著範例就是要教大家怎麼處理 包(Bags) 的資料 , 我自己在學的時候也是搞了好久才搞定的

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A BY a;

DUMP B;
(1,{(1,2,3,6,9,8),(1,2,3,5,9,6)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,3,5,8),(5,9,6,2,4,8)})

DESCRIBE B;
B: {group: int,A: {(a: int,b: int,c: int,d: int,e: int,f: int)}}

C = FOREACH B{

  C1 = FOREACH A GENERATE (a+b+c+d+e+f);

  GENERATE group, C1;

  -- GENERATE 是關鍵字不能改而且必須放在最後做結尾 group 是上面 $0 的名字 C1 則是你在 { } 做的敘述句代號 , 外面能做的函式在這邊基本上都可以執行但只能操作 包(Bags) 的資料 
}

DUMP C;
(1,{(29),(26)})
(3,{(31),(35)})
(5,{(36),(34)})

DESCRIBE C;
C: {group: int,C1: {(int)}}

D = FOREACH B{

  C1 = FOREACH A GENERATE (a+b+c+d+e+f);

  GENERATE C1;
  -- 如果沒有打上 group 就不會出現前面的 1 3 5 了 , 只會留下剛剛全部加起來的數字在 包(Bags) 裡面
}

DUMP D;
({(29),(26)})
({(31),(35)})
({(36),(34)})


DESCRIBE D;
D: {C1: {(int)}}

COUNT

這是一個用來計算 包(Bags) 裡面有多少個 元組(Tuples) , 但是如果 包(Bags) 裡面有 NULL(空值) 是會被忽略的

語法

[代號] = FOREACH [代號] GENERATE COUNT([欄位名稱]);

範例

7 - 1

A = LOAD '/teaching/c.txt' using PigStorage(',') AS (dates:chararray, catid:int, userid:chararray);

B = GROUP A ALL;

C = FOREACH B GENERATE $1;

D = FOREACH C GENERATE COUNT(A);

DUMP C;
({(20150922,17,u016045383),(20150821,1,u015769781),(20150710,8,u008137791),(20150830,18,u014711537),(20150627,1,u014616537),(20150830,2,u007591963),(20150726,38,u014540733),(20150715,40,u014792743),(20150627,12,u006343463),(20150707,18,u005655603),(20150911,5,u008969559),(20150820,18,u015235929),(20150919,40,u003234985),(20150602,39,u008469313),(20150617,40,u201308777),(20150916,21,u002529119)})

DUMP D;
(16)

DESCRIBE D;
D: {long}
  • B = GROUP A ALL;

    • 把全部資料 包(Bags) 起來
  • C = FOREACH B GENERATE $1;

    • 去掉資料第一個欄位 ( $0 ) 的 ALL 字串
  • D = FOREACH C GENERATE COUNT(A);

    • 算出資料總筆數

TOTUPLE

TOTUPLE 用來將 字段(Fields) 組合成 元組(Tuples)

語法

[代號] = FOREACH [代號] GENERATE TOTUPLE([欄位]);

範例

8 - 1

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = FOREACH A GENERATE TOTUPLE(a, b), c, d, TOTUPLE(e, f);

DUMP B;
((1,2),3,6,(9,8))
((1,2),3,5,(9,6))
((5,9),6,3,(5,8))
((5,9),6,2,(4,8))
((3,2),4,8,(9,5))
((3,6),9,8,(5,4))
  • B = FOREACH A GENERATE TOTUPLE(a, b), c, d, TOTUPLE(e, f);
    • 將要 元組(Tuples)字段(Fields) 加入 TOTUPLE() , 並且需要配合 FOREACH 使用

FLATTEN

FLATTEN 是用來將 元組(Tuples) 拆解成 字段(Fields) , 也可以把 group 起來的資料復原!

語法

[代號] = FOREACH [代號] GENERATE FLATTEN([欄位]);

範例

9 - 1

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = FOREACH A GENERATE TOTUPLE(a, b), c, d, TOTUPLE(e, f);

C = FOREACH B GENERATE FLATTEN($0), c, d, FLATTEN($3);

DUMP B;
((1,2),3,6,(9,8))
((1,2),3,5,(9,6))
((5,9),6,3,(5,8))
((5,9),6,2,(4,8))
((3,2),4,8,(9,5))
((3,6),9,8,(5,4))

DUMP C;;
(1,2,3,6,9,8)
(1,2,3,5,9,6)
(5,9,6,3,5,8)
(5,9,6,2,4,8)
(3,2,4,8,9,5)
(3,6,9,8,5,4)
  • B = FOREACH A GENERATE TOTUPLE(a, b), c, d, TOTUPLE(e, f);

    • 使用上一節的程式
  • C = FOREACH B GENERATE FLATTEN($0), c, d, FLATTEN($3);

    • 依照 DUMP B 的結果得知 (1,2) 是排在 $0 , (9,8)$3 所以就分別在 FLATTEN() 填入 $0$3

9 - 2

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A BY a;

C = FOREACH B GENERATE FLATTEN(A);

DUMP B;
(1,{(1,2,3,6,9,8),(1,2,3,5,9,6)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,3,5,8),(5,9,6,2,4,8)})

DUMP C;
(1,2,3,6,9,8)
(1,2,3,5,9,6)
(3,2,4,8,9,5)
(3,6,9,8,5,4)
(5,9,6,3,5,8)
(5,9,6,2,4,8)
  • B = GROUP A BY a;

    • 依照 a 將資料 GROUP 起來
  • C = FOREACH B GENERATE FLATTEN(A);

    • 因為是用 aGROUP 所以在 FLATTEN() 中填入 a 就可以把它復原

9 - 3

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A BY a;

C = FOREACH B GENERATE BagToTuple(A);

D = FOREACH B GENERATE FLATTEN(BagToTuple(A));

DUMP B;
(1,{(1,2,3,6,9,8),(1,2,3,5,9,6)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,3,5,8),(5,9,6,2,4,8)})

DUMP C;
((1,2,3,6,9,8,1,2,3,5,9,6))
((3,2,4,8,9,5,3,6,9,8,5,4))
((5,9,6,3,5,8,5,9,6,2,4,8))


DUMP D;
(1,2,3,6,9,8,1,2,3,5,9,6)
(3,2,4,8,9,5,3,6,9,8,5,4)
(5,9,6,3,5,8,5,9,6,2,4,8)
  • C = FOREACH B GENERATE BagToTuple(A);

    • BagToTuple(A) 可以把 GROUP 的資料解出來 , 但是會把資料包成 元組(Tuples)
  • D = FOREACH B GENERATE FLATTEN(BagToTuple(A));

    • 由上面的敘述可以知道 BagToTuple(A) 可以轉成 元組(Tuples) , 而 FLATTEN() 就是把 BagToTuple(A) 轉出來的 元組(Tuples) 繼續轉成 字段(Fields)

ORDER

依照指定的 [欄位] 做排序

語法

[代號] = ORDER [代號] BY [欄位] [ASC|DESC];

範例

10 - 1

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = ORDER A BY a ASC, d DESC;

DUMP B;
(1,2,3,6,9,8)
(1,2,3,5,9,6)
(3,2,4,8,9,5)
(3,6,9,8,5,4)
(5,9,6,3,5,8)
(5,9,6,2,4,8)
  • B = ORDER A BY a ASC, d DESC;
    • 可以只排列一個欄位 , 也可以排列兩個欄位甚至是更多個欄位 , 並且可以分別指定排序方式
    • a ASC 將欄位 a 由小排到大
    • d DESC 將欄位 d 由大排到小

10 - 2

A = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

B = GROUP A BY a;

C = FOREACH B {
  C1 = ORDER A BY $3 ASC;
  GENERATE group, C1;
}

DUMP B;
(1,{(1,2,3,6,9,8),(1,2,3,5,9,6)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,3,5,8),(5,9,6,2,4,8)})

DUMP C;
(1,{(1,2,3,5,9,6),(1,2,3,6,9,8)})
(3,{(3,2,4,8,9,5),(3,6,9,8,5,4)})
(5,{(5,9,6,2,4,8),(5,9,6,3,5,8)})
  • C1 = ORDER A BY $3 ASC;
    • 在還沒有看解說之前你可羺會以為這邊的 ORDER 會改變每一個 元組(Tuples) 內的資料排序 , 但其實不是這樣的 , 這邊的 ORDER 是換改變每一個 元組(Tuples)包(Bags) 裡面的順序 , 完全不會動到 元組(Tuples) 內的資源 , 所以不用擔心資料會亂掉

UNION

將兩份文件或是兩個結果二合一的第一種辦法也是最簡單的辦法

語法

[代號] = UNION [代號], [代號];

範例

11 - 1

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

C = UNION A, B;

DUMP C;
(1,2,3,6,9,8)
(1,2,3,5,9,6)
(5,9,6,3,5,8)
(5,9,6,2,4,8)
(3,2,4,8,9,5)
(3,6,9,8,5,4)
(1,2,3)
(4,5,6)
  • C = UNION A, B;
    • 資料載入後就直接合體(?

11 - 2

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

C = FOREACH B GENERATE a, b, c;

D = UNION A, C;

DUMP D;
(1,2,3)
(4,5,6)
(1,2,3)
(1,2,3)
(5,9,6)
(5,9,6)
(3,2,4)
(3,6,9)
  • D = UNION A, C;
    • 處理過後的結果當然也可以拿來合體!

DISTINCT

移除重複的資料

語法

[代號] = DISTINCT [代號];

範例

12 - 1

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

C = FOREACH B GENERATE a, b, c;

D = UNION A, C;

E = DISTINCT D;

DUMP D;
(1,2,3)
(4,5,6)
(1,2,3)
(1,2,3)
(5,9,6)
(5,9,6)
(3,2,4)
(3,6,9)

DUMP E;
(1,2,3)
(3,2,4)
(3,6,9)
(4,5,6)
(5,9,6)
  • E = DISTINCT D;
    • 移除掉 D 中重複的料

CROSS

這也是將兩個檔案或是結果二合一的方法之一 , 但是比 UNION 還要進階一點 , 用說的解釋比較抽象也不好了解 , 所以就直接來看看範例吧!

語法

[代號] = CROSS [代號], [代號];

範例

13 - 1

A = LOAD '/teaching/a.txt' using PigStorage(',') AS (a:int, b:int, c:int);

B = LOAD '/teaching/b.txt' using PigStorage(',') AS (a:int, b:int, c:int, d:int, e:int, f:int);

D = CROSS B, A;

E = CROSS A, B;

DUMP D;
(5,9,6,2,4,8,1,2,3)
(5,9,6,2,4,8,4,5,6)
(5,9,6,3,5,8,1,2,3)
(5,9,6,3,5,8,4,5,6)
(1,2,3,6,9,8,1,2,3)
(3,6,9,8,5,4,1,2,3)
(3,6,9,8,5,4,4,5,6)
(1,2,3,6,9,8,4,5,6)
(3,2,4,8,9,5,1,2,3)
(1,2,3,5,9,6,1,2,3)
(3,2,4,8,9,5,4,5,6)
(1,2,3,5,9,6,4,5,6)

DUMP E;
(4,5,6,1,2,3,6,9,8)
(4,5,6,1,2,3,5,9,6)
(4,5,6,5,9,6,3,5,8)
(4,5,6,5,9,6,2,4,8)
(4,5,6,3,2,4,8,9,5)
(4,5,6,3,6,9,8,5,4)
(1,2,3,5,9,6,3,5,8)
(1,2,3,1,2,3,6,9,8)
(1,2,3,1,2,3,5,9,6)
(1,2,3,5,9,6,2,4,8)
(1,2,3,3,6,9,8,5,4)
(1,2,3,3,2,4,8,9,5)
  • D = CROSS B, A;

    • b 文件第一筆資料是 5,9,6,2,4,8
    • a 文件第一筆資料是 1,2,3
    • a 文件第二筆資料是 4,5,6
    • 所以當 BA 前面時 b 的第一筆資料會依序接上 a 的兩比資料 , 所以才會出現 (5,9,6,2,4,8,1,2,3)(5,9,6,2,4,8,4,5,6) 這兩比資料
  • E = CROSS A, B;

    • AB 前面時所合出來的檔案也會有位置的不同 , 這間就請各位同學自己了解一下資料的規則吧 , 其實一點也不難

SUM

包(Bags) 裡面的數字做加法

語法

[代號] = FOREACH [代號] GENERATE SUM([代號].[欄位]);

範例

14 - 1

A = LOAD '/teaching/d.txt' using PigStorage(',') AS (owner:chararray, pet_type:chararray, pet_num:int);

B = GROUP A BY owner;

C = FOREACH B GENERATE group, SUM(A.pet_num);

DUMP B;
(Alice,{(Alice,turtle,1),(Alice,goldfish,5),(Alice,cat,2)})
(Bob,{(Bob,dog,2),(Bob,cat,2)})

DUMP C;
(Alice,8)
(Bob,4)
  • C = FOREACH B GENERATE group, SUM(A.pet_num);
    • SUM(A.pet_num) 中的 A 就是被我們 GROUP 起來的代號 , 而 pet_num 就是我們要計算的欄位名稱 , 記得資料的類型絕對不可以是 chararray 不然會出現錯誤 , 因為文字是不能相加的

SIZE

用於計算 [欄位] 中得 chararray 資料類型共有幾個字

語法

[代號] = FOREACH [代號] GENERATE SIZE([欄位]);

範例

15 - 1

A = LOAD '/teaching/d.txt' using PigStorage(',') AS (owner:chararray, pet_type:chararray, pet_num:int);

B = FOREACH A GENERATE SIZE(pet_type);

C = FOREACH A GENERATE SIZE(pet_num);

DUMP B;
(6)
(8)
(3)
(3)
(3)

DUMP C;
(1)
(1)
(1)
(1)
(1)
  • B = FOREACH A GENERATE SIZE(pet_type);

    • 計算 pet_type 的字數
  • C = FOREACH A GENERATE SIZE(pet_num);

    • 當然數字類型的資料也可以計算

MAX

包(Bags) 裡面取最大的一個值

語法

[代號] = FOREACH [代號] GENERATE MAX([代號].[欄位]);

範例

16 - 1

A = LOAD '/teaching/e.txt' using PigStorage(',') AS (name:chararray, session:chararray, gpa:float);

B = GROUP A BY name;

C = FOREACH B GENERATE group, MAX(A.gpa);

DUMP B;
(John,{(John,fl,3.9),(John,wt,3.7),(John,sp,4.0),(John,sm,3.8)})
(Mary,{(Mary,fl,3.8),(Mary,wt,3.9),(Mary,sp,4.0),(Mary,sm,4.0)})

DUMP C;

(John,4.0)
(Mary,4.0)
  • C = FOREACH B GENERATE group, MAX(A.gpa);
    • 語法與上面的 SUM 有點類似只是 MAX 是要抓出最大的那個值

MIN

包(Bags) 裡面取最小的一個值

語法

[代號] = FOREACH [代號] GENERATE MIN([代號].[欄位]);

範例

17 - 1

A = LOAD '/teaching/e.txt' using PigStorage(',') AS (name:chararray, session:chararray, gpa:float);

B = GROUP A BY name;

C = FOREACH B GENERATE group, MIN(A.gpa);

DUMP B;
(John,{(John,fl,3.9),(John,wt,3.7),(John,sp,4.0),(John,sm,3.8)})
(Mary,{(Mary,fl,3.8),(Mary,wt,3.9),(Mary,sp,4.0),(Mary,sm,4.0)})

DUMP C;
(John,3.7)
(Mary,3.8)
  • C = FOREACH B GENERATE group, MIN(A.gpa);
    • 有取出最大當然有取出最小的了

TOBAG

TOBAG 輸出的結果跟 TOTUPLE 有點類似 , 只是他不只包成 元組(Tuples) 還會更進一步的包成 包(Bags)

語法

[代號] = FOREACH [代號] GENERATE TOBAG([欄位]);

範例

18 - 1

A = LOAD '/teaching/e.txt' using PigStorage(',') AS (name:chararray, session:chararray, gpa:float);

B = FOREACH A GENERATE TOBAG(name, gpa);

DUMP B;
({(John),(3.9)})
({(John),(3.7)})
({(John),(4.0)})
({(John),(3.8)})
({(Mary),(3.8)})
({(Mary),(3.9)})
({(Mary),(4.0)})
({(Mary),(4.0)})
  • B = FOREACH A GENERATE TOBAG(name, gpa);
    • TOBAG(name, gpa) 將欄位 namegpa 分別包成 元組(Tuples) 在放入一個 包(Bags)

DIFF

依序比較兩個 包(Bags) 裡面的 元組(Tuples) 取出不一樣的 , 如果沒有不一樣的會直接顯示 null

語法

[代號] = FOREACH [代號] GENERATE DIFF([欄位]);

範例

19 - 1

A = LOAD '/teaching/f.txt' using PigStorage(',') as (a:int, b:int, c:int, d:int, e:int, f:int, g:int , h:int);

B = FOREACH A GENERATE TOTUPLE(a, b), TOTUPLE(c, d), TOTUPLE(e, f), TOTUPLE(g, h);

C = FOREACH B GENERATE TOBAG($0, $1), TOBAG($2, $3);

D = FOREACH C GENERATE DIFF($0,$1);

DUMP C;
({(8,9),(0,1)},{(8,9),(1,1)})
({(2,3),(4,5)},{(2,3),(4,5)})
({(6,7),(3,7)},{(2,2),(3,7)})

DUMP D;
({(0,1),(1,1)})
({})
({(6,7),(2,2)})
  • DUMP C;

    • 這邊 DUMP 的資料是有經過處理的 , 而這些處理的步驟在上的教學都有 , 不懂的同學看上面教學
  • D = FOREACH C GENERATE DIFF($0,$1);

    • 這裡我們要用 {(8,9),(0,1)}{(8,9),(1,1)} 兩個來做比較 , 其中 (0,1)(1,1) 不同所以會被抓出來
    • 比較值得注意的是 ({}) 為什麼是空白 , 其實很簡單因為 (2,3),(4,5)(2,3),(4,5) 完全一樣 , 所以當然給你 NULL

  1. Notepad++ 是一個非常好用的 Text Edit 詳細的使用方式就請各位自己 Google
  2. Emacs 是在 Linux 上面也是一個我很推薦的 Text Edit 可以提供 Pig 高亮度語法 ( 有機會會在另外介紹 )

沒有留言:

張貼留言