Apache Pig的一些基礎概念及用法總結2(轉)

(7)在多維度組合下,如何計算某個維度組合裏的不重複記錄的條數
以數據文件 c.txt 爲例:

1
2
3
4
5
6
7
[root@localhost pig]$ cat c.txt
a 1 2 3 4.2 9.8 100
a 3 0 5 3.5 2.1 200
b 7 9 9 - - 300
a 7 9 9 2.6 6.2 300
a 1 2 5 7.7 5.9 200
a 1 2 3 1.4 0.2 500

問題:如何計算在第2、3、4列的所有維度組合下,最後一列不重複的記錄分別有多少條?例如,第2、3、4列有一個維度組合是(1,2,3),在這個維度維度下,最後一列有兩種值:100 和 500,因此不重複的記錄數爲2。同理可求得其他的記錄條數。
pig代碼及輸出結果如下:

1
2
3
4
5
6
7
8
grunt> A = LOAD 'c.txt' AS (col1:chararray, col2:int, col3:int, col4:int, col5:double, col6:double, col7:int);
grunt> B = GROUP A BY (col2, col3, col4);
grunt> C = FOREACH B {D = DISTINCT A.col7; GENERATE group, COUNT(D);};
grunt> DUMP C;
((1,2,3),2)
((1,2,5),1)
((3,0,5),1)
((7,9,9),1)

我們來看看每一步分別生成了什麼樣的數據:
LOAD不用說了,就是加載數據;
GROUP也不用說了,和前文所說的一樣。GROUP之後得到了這樣的數據:

1
2
3
4
5
grunt> DUMP B;
((1,2,3),{(a,1,2,3,4.2,9.8,100),(a,1,2,3,1.4,0.2,500)})
((1,2,5),{(a,1,2,5,7.7,5.9,200)})
((3,0,5),{(a,3,0,5,3.5,2.1,200)})
((7,9,9),{(b,7,9,9,,,300),(a,7,9,9,2.6,6.2,300)})

其實到這裏,我們肉眼就可以看出來最後要求的結果是什麼了,當然,必須要由pig代碼來完成,要不然怎麼應對海量數據?
文章來源:http://www.codelast.com/
這裏的 FOREACH 與前面有點不一樣,第一次看到這種寫法,肯定會覺得很奇怪。先看一下用於去重DISTINCT關鍵字的說明:

Removes duplicate tuples in a relation.

然後再解釋一下:FOREACH 是對B的每一行進行遍歷,其中,B的每一行裏含有一個包(bag),每一個包中含有若干元組(tuple)A,因此,FOREACH 後面的大括號裏的操作,其實是對所謂的“內部包”(inner bag)的操作(詳情請參看FOREACH的說明),在這裏,我們指定了對A的col7這一列進行去重,去重的結果被命名爲D,然後再對D計數(COUNT),就得到了我們想要的結果。
輸出結果數據,與前文所述的差不多。
這樣就達成了我們的目的。從總體上說,剛接觸pig不久的人會覺得這些寫法怪怪的,就是扭不過來,但是要堅持,時間長了,連倒影也會讓你覺得是正的了。

(8)如何將關係(relation)轉換爲標量(scalar)
在前文中,我們要統計符合某些條件的數據的條數,使用了COUNT函數來計算,但在COUNT之後,我們得到的還是一個關係(relation),而不是一個標量的數字,如何把一個關係轉換爲標量,從而可以在後續處理中便於使用呢?
具體請看這個鏈接

(9)pig中如何使用shell進行輔助數據處理
pig中可以嵌套使用shell進行輔助處理,下面,就以一個實際的例子來說明。
假設我們在某一步pig處理後,得到了類似於下面 b.txt 中的數據:

1
2
3
4
[root@localhost pig]$ cat b.txt
1 5 98  = 7
34  8 6 3 2
62  0 6 = 65

問題:如何將數據中第4列中的“=”符號全部替換爲9999?
pig代碼及輸出結果如下:

1
2
3
4
5
6
grunt> A = LOAD 'b.txt' AS (col1:int, col2:int, col3:int, col4:chararray, col5:int);
grunt> B = STREAM A THROUGH `awk '{if($4 == "=") print $1"\t"$2"\t"$3"\t9999\t"$5; else print $0}'`;
grunt> DUMP B;
(1,5,98,9999,7)
(34,8,6,3,2)
(62,0,6,9999,65)

我們來看看這段代碼是如何做到的:
加載數據,這個沒什麼好說的。
通過“STREAM … THROUGH …”的方式,我們可以調用一個shell語句,用該shell語句對A的每一行數據進行處理。此處的shell邏輯爲:當某一行數據的第4列爲“=”符號時,將其替換爲“9999”;否則就照原樣輸出這一行。
輸出B,可見結果正確。

(10)向pig腳本中傳入參數
假設你的pig腳本輸出的文件是通過外部參數指定的,則此參數不能寫死,需要傳入。在pig中,使用傳入的參數如下所示:

1
STORE A INTO '$output_dir';

則這個“output_dir”就是個傳入的參數。在調用這個pig腳本的shell腳本中,我們可以這樣傳入參數:

1
pig -param output_dir="/home/my_ourput_dir/" my_pig_script.pig

這裏傳入的參數“output_dir”的值爲“/home/my_output_dir/”。
文章來源:http://www.codelast.com/
(11)就算是同樣一段pig代碼,多次計算所得的結果也有可能是不同的
例如用AVG函數來計算平均值時,同樣一段pig代碼,多次計算所得的結果中,小數點的最後幾位也有可能是不相同的(當然也有可能相同),大概是因爲精度的原因吧。不過,一般來說小數點的最後幾位已經不重要了。例如我對一個數據集進行處理後,小數點後13位纔開始有不同,這樣的精度完全足夠了。

(12)如何編寫及使用自定義函數(UDF)
首先給出一個鏈接:Pig 0.8.1 API,還有Pig UDF Manual。這兩個文檔能提供很多有用的參考。
自定義函數有何用?這裏以一個極其簡單的例子來說明一下:
假設你有如下數據:

1
2
3
4
5
[root@localhost pig]$ cat a.txt
uidk  12  3
hfd 132 99
bbN 463 231
UFD 13  10

現在你要將第二列的值先+500,再-300,然後再÷2.6,那麼我們可以這樣寫:

1
2
3
4
5
6
7
grunt> A = LOAD 'a.txt' AS(col1:chararray, col2:double, col3:int);
grunt> B = FOREACH A GENERATE col1, (col2 + 500 - 300)/2.6, col3;
grunt> DUMP B;
(uidk,81.53846153846153,3)
(hfd,127.6923076923077,99)
(bbN,255.0,231)
(UFD,81.92307692307692,10)

我們看到,對第二列進行了 (col2 + 500 – 300)/2.6 這樣的計算。麻煩不?或許這點小意思沒什麼。但是,如果有比這複雜得多的處理,每次你需要輸入多少pig代碼呢?我們希望有這樣一個函數,可以讓第二行pig代碼簡化如下:

1
grunt> B = FOREACH A GENERATE col1, com.codelast.MyUDF(col2), col3;

這樣的話,對於我們經常使用的操作,豈不是很方便?
pig的UDF(user-defined function)就是拿來做這個的。
文章來源:http://www.codelast.com/
下面,就以IntelliJ這個IDE爲例(其實用什麼IDE倒無所謂,大同小異吧),說明我們如何實現這樣一個功能。
新建一個新工程,在工程下創建“lib”目錄,然後把pig安裝包中的“pig-0.8.1-core.jar”文件放置到此lib目錄下,然後在“Project Structure→Libraries”下添加(點擊“+”號)一個庫,就命名爲“lib”,然後點擊右側的“Attach Classes”按鈕,選擇pig-0.8.1-core.jar文件,再點擊下方的“Apply”按鈕應用此更改。這樣做之後,你就可以在IDE的編輯器中實現輸入代碼時看到智能提示了。
此外,你還需要用同樣的方法,將一堆Hadoop的jar包添加到工程中,包括以下文件:

1
2
3
4
5
hadoop-XXX-ant.jar
hadoop-XXX-core.jar
hadoop-XXX-examples.jar
hadoop-XXX-test.jar
hadoop-XXX-tools.jar

其中,XXX是版本號。
如果沒有這些文件,你在編譯jar包的時候會報錯。
文章來源:http://www.codelast.com/
跟我一起,在工程目錄下的 src/com/coldelast/ 目錄下創建Java源代碼文件 MyUDF.java,其內容如下:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.codelast;
 
import java.io.IOException;
 
import org.apache.pig.EvalFunc;
import org.apache.pig.data.Tuple;
 
/**
 * Author: Darran Zhang @ codelast.com
 * Date: 2011-09-29
 */
 
public class MyUDF extends EvalFunc<Double> {
 
  @Override
  public Double exec(Tuple input) throws IOException {
    if (input == null || input.size() == 0) {
      return null;
    }
 
    try {
      Double val = (Double) input.get(0);
      val = (val + 500 - 300) / 2.6;
      return val;
    } catch (Exception e) {
      throw new IOException(e.getMessage());
    }
  }
}

在上面的代碼中,input.get(0)是獲取UDF的第一個參數(可以向UDF傳入多個參數);同理,如果你的UDF接受兩個參數(例如一個求和的UDF),那麼input.get(1)可以取到第二個參數。
然後編寫build.xml(相當於C++裏面的Makefile),用ant來編譯、打包此工程——這裏就不把冗長的build.xml寫上來了,而且這也不是關鍵,沒有太多意義。
文章來源:http://www.codelast.com/
假定編譯、打包得到的jar包名爲cl.jar,我們到這裏幾乎已經完成了大部分工作。下面就看看如何在pig中調用我們剛編寫的自定義函數了。

1
2
3
4
5
6
7
8
grunt> REGISTER cl.jar;
grunt> A = LOAD 'a.txt' AS(col1:chararray, col2:double, col3:int);
grunt> B = FOREACH A GENERATE col1, com.codelast.MyUDF(col2), col3;
grunt> DUMP B;
(uidk,81.53846153846153,3)
(hfd,127.6923076923077,99)
(bbN,255.0,231)
(UFD,81.92307692307692,10)

注:第一句是註冊你編寫的UDF,使用前必須先註冊。
從結果可見,我們實現了預定的效果。
UDF大有用途!
注意:對如果你的UDF返回一個標量類型(類似於我上面的例子),那麼pig就可以使用反射(reflection)來識別出返回類型。如果你的UDF返回的是一個包(bag)或一個元組(tuple),並且你希望pig能理解包(bag)或元組(tuple)的內容的話,那麼你就要實現outputSchema方法,否則後果很不好。具體可看這個鏈接的說明。

(13)什麼是聚合函數(Aggregate Function)
在pig中,聚合函數就是那些接受一個輸入包(bag),返回一個標量(scalar)值的函數。COUNT函數就是一個例子。

(14)COGROUP做了什麼
與GROUP操作符一樣,COGROUP也是用來分組的,不同的是,COGROUP可以按多個關係中的字段進行分組。
還是以一個實例來說明,假設有以下兩個數據文件:

01
02
03
04
05
06
07
08
09
10
[root@localhost pig]$ cat a.txt
uidk  12  3
hfd 132 99
bbN 463 231
UFD 13  10
 
[root@localhost pig]$ cat b.txt
908 uidk  888
345 hfd 557
28790 re  00000

現在我們用pig做如下操作及得到的結果爲:

1
2
3
4
5
6
7
8
9
grunt> A = LOAD 'a.txt' AS (acol1:chararray, acol2:int, acol3:int);
grunt> B = LOAD 'b.txt' AS (bcol1:int, bcol2:chararray, bcol3:int);
grunt> C = COGROUP A BY acol1, B BY bcol2;
grunt> DUMP C;
(re,{},{(28790,re,0)})
(UFD,{(UFD,13,10)},{})
(bbN,{(bbN,463,231)},{})
(hfd,{(hfd,132,99)},{(345,hfd,557)})
(uidk,{(uidk,12,3)},{(908,uidk,888)})

每一行輸出的第一項都是分組的key,第二項和第三項分別都是一個包(bag),其中,第二項是根據前面的key找到的A中的數據包,第三項是根據前面的key找到的B中的數據包。
來看看第一行輸出:“re”作爲group的key時,其找不到對應的A中的數據,因此第二項就是一個空的包“{}”,“re”這個key在B中找到了對應的數據(28790    re    00000),因此第三項就是包{(28790,re,0)}。
其他輸出數據也類似。

(15)安裝pig後,運行pig命令時提示“Cannot find hadoop configurations in classpath”等錯誤的解決辦法
pig安裝好後,運行pig命令時提示以下錯誤:

ERROR org.apache.pig.Main – ERROR 4010: Cannot find hadoop configurations in classpath (neither hadoop-site.xml nor core-site.xml was found in the classpath).If you plan to use local mode, please put -x local option in command line

顯而易見,提示找不到與hadoop相關的配置文件。所以我們需要把hadoop安裝目錄下的“conf”子目錄添加到系統環境變量PATH中:
修改 /etc/profile 文件,添加:

1
2
3
4
export HADOOP_HOME=/usr/local/hadoop
export PIG_CLASSPATH=$HADOOP_HOME/conf
 
PATH=$JAVA_HOME/bin:$HADOOP_HOME/bin:$PIG_CLASSPATH:$PATH

然後重新加載 /etc/profile 文件:

1
source /etc/profile

文章來源:http://www.codelast.com/
(16)piggybank是什麼東西

Pig also hosts a UDF repository called piggybank that allows users to share UDFs that they have written.

說白了就是Apache把大家寫的自定義函數放在一塊兒,起了個名字,就叫做piggybank。你可以把它理解爲一個SVN代碼倉庫。具體請看這裏

(17)UDF的構造函數會被調用幾次
你可能會想在UDF的構造函數中做一些初始化的工作,例如創建一些文件,等等。但是你不能假設UDF的構造函數只被調用一次,因此,如果你要在構造函數中做一些只能做一次的工作,你就要當心了——可能會導致錯誤。

轉載:http://www.codelast.com/


   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章