Apache Pig的一些基础概念及用法总结4(转)

来源:互联网 发布:黑莓q20软件下载 编辑:程序博客网 时间:2024/06/06 14:04

(26)错误“ERROR org.apache.pig.tools.grunt.Grunt – ERROR 2042: Error in new logical plan. Try -Dpig.usenewlogicalplan=false.”的可能原因
Pig的bug,详见此链接;
其他原因。我遇到并解决了一例。具体的代码不便在此陈列,但是基本可以说是由于自己写的Pig代码对复杂数据结构的处理不当导致的,后来我尝试更改了一种实现方式,就绕过了这个问题。关于这点,确实还是要具体问题具体分析的,在这里没有实例的话,无法给大家一个明确的解决问题的指南。

(27)如何在Pig中使用正则表达式对字符串进行匹配
假设你有如下数据文件:

1
2
3
4
5
[root@localhost ~]# cat a.txt
1 http://ui.qq.com/abcd.html
5 http://tr.qq.com/743.html
8 http://vid.163.com/trees.php
9 http:auto.qq.com/us.php

现在要找出该文件中,第二列符合“*//*.qq.com/*”模式的所有行(此处只有前两行符合条件),怎么做?
Pig代码如下:

1
2
3
A = LOAD'a.txt' AS (col1: int, col2: chararray);
B = FILTER A BY col2 matches '.*//.*\\.qq\\.com/.*';
DUMP B;

我们看到,matches关键字对 col2 进行了正则匹配,它使用的是Java格式的正则表达式匹配规则。
表示任意字符,表示字符出现任意次数;\. 对 进行了转义,表示匹配 这个字符;就是表示匹配 这个字符。
这里需要注意的是,在引号中,用于转义的字符 需要打两个才能表示一个,所以上面的 \\. 就是与正则中的 \. 是一样的,即匹配 这个字符。
文章来源:http://www.codelast.com/
最后输出结果为:

1
2
(1,http://ui.qq.com/abcd.html)
(5,http://tr.qq.com/743.html)

可见结果是正确的。

(28)如何截取一个字符串中的某一段
在处理数据时,如果你想提取出一个日期字符串的年份,例如提取出“2011-10-26”中的“2011”,可以用内置函数 SUBSTRING 来实现:

SUBSTRING
Returns a substring from a given string.
Syntax
SUBSTRING(string, startIndex, stopIndex)

下面举一个例子。假设有数据文件:

1
2
3
4
5
6
[root@localhost ~]# cat a.txt
2010-05-06  abc
2008-06-18  uio
2011-10-11  tyr
2010-12-23  fgh
2011-01-05  vbn

第一列是日期,现在要找出所有不重复的年份有哪些,可以这样做:

1
2
3
4
A = LOAD'a.txt' AS (dateStr: chararray, flag: chararray);
B = FOREACH A GENERATESUBSTRING(dateStr, 0, 4);
C = DISTINCTB;
DUMP C;

输出结果为:

1
2
3
(2008)
(2010)
(2011)

可见达到了我们想要的效果。
上面的代码太简单了,不必多言,唯一需要说明一下的是 SUBSTRING 函数,它的第一个参数是要截取的字符串,第二个参数是起始索引(从0开始),第三个参数是结束索引。
文章来源:http://www.codelast.com/
(29)如何拼接两个字符串
假设有以下数据文件:

1
2
3
4
5
[root@localhost ~]# cat 1.txt
abc 123
cde 456
fgh 789
ijk 200

现在要把第一列和第二列作为字符串拼接起来,例如第一行会变成“abc123”,那么使用CONCAT这个求值函数(eval function)就可以做到:

1
2
3
A = LOAD'1.txt' AS (col1: chararray, col2: int);
B = FOREACH A GENERATE CONCAT(col1, (chararray)col2);
DUMP B;

输出结果为:

1
2
3
4
(abc123)
(cde456)
(fgh789)
(ijk200)

注意这里故意在加载数据的时候把第二列指定为int类型,这是为了说明数据类型不一致的时候CONCAT会出错(你可以试验一下):

ERROR org.apache.pig.tools.grunt.Grunt – ERROR 1045: Could not infer the matching function for org.apache.pig.builtin.CONCAT as multiple or none of them fit. Please use an explicit cast.

所以在后面CONCAT的时候,对第二列进行了类型转换。
另外,如果数据文件内容为:

1
2
3
4
5
[root@localhost ~]# cat 1.txt
5 123
7 456
8 789
0 200

那么,如果对两列整数CONCAT:

1
2
A = LOAD'1.txt' AS (col1: int, col2:int);
B = FOREACH A GENERATE CONCAT(col1, col2);

同样也会出错

ERROR org.apache.pig.tools.grunt.Grunt – ERROR 1045: Could not infer the matching function for org.apache.pig.builtin.CONCAT as multiple or none of them fit. Please use an explicit cast.

要注意这一点。
有人可能会问:要拼接几个字符串的话怎么办?CONCAT 套 CONCAT 就要可以了(有点笨,但管用): CONCAT(a, CONCAT(b, c))

(30)如何求两个数据集的重合 & 不同的数据类型JOIN会失败
假设有以下两个数据文件:

1
2
3
4
5
[root@localhost ~]# cat 1.txt
123
456
789
200

以及:

1
2
3
4
[root@localhost ~]# cat 2.txt
200
333
789

现在要找出两个文件中,相同的数据有多少行,怎么做?这也就是所谓的求两个数据集的重合
用关系操作符JOIN,我们可以达到这个目的。在处理海量数据时,经常会有求重合的需求。所以JOIN是Pig中一个极其重要的操作。
在本例中,两个文件中有两个相同的数据行:789以及200,因此,结果应该是2。
我们先来看看正确的代码:

1
2
3
4
5
6
A = LOAD'1.txt' AS (a: int);
B = LOAD'2.txt' AS (b: int);
C = JOINA BY a, B BY b;
D = GROUPC ALL;
E = FOREACH D GENERATECOUNT(C);
DUMP E;

解释一下:
第一、二行是加载数据,不必多言。
第三行按A的第1列、B的第二列进行“结合”,JOIN之后,a、b两列不相同的数据就被剔除掉了。C的数据结构为:

1
C: {A::a: int,B::b: int}

C的数据为:

1
2
(200,200)
(789,789)

由于我们要统计的是数据行数,所以上面的Pig代码中的第4、5行就进行了计数的运算。
如果文件 2.txt 多一行数据“200”,结果会是什么?答案是:结果为3。这个时候C的数据为:

1
2
3
(200,200)
(200,200)
(789,789)

所以如果你要去除重复的,还需要用DISTINCE对C处理一下:

1
2
3
4
5
6
7
A = LOAD'1.txt' AS (a: int);
B = LOAD'2.txt' AS (b: int);
C = JOINA BY a, B BY b;
uniq_C = DISTINCTC;
D = GROUPuniq_C ALL;
E = FOREACH D GENERATECOUNT(uniq_C);
DUMP E;

这样得到的结果就是2了。
文章来源:http://www.codelast.com/
尤其需要注意的是,如果JOIN的两列具有不同的数据类型,是会失败的。例如以下代码:

1
2
3
4
5
6
A = LOAD'1.txt' AS (a: int);
B = LOAD'2.txt' AS (b: chararray);
C = JOINA BY a, B BY b;
D = GROUPC ALL;
E = FOREACH D GENERATECOUNT(C);
DUMP E;
在语法上是没有错误的,但是一运行就会报错:
ERROR org.apache.pig.tools.grunt.Grunt – ERROR 1107: Cannot merge join keys, incompatible types
这是因为a、b具有不同的类型:int和chararray。

(31)使用三目运算符“ ? : ”有时候必须加括号
假设有以下数据文件:

1
2
3
4
[root@localhost ~]# cat a.txt
5 8 9
6   0
4 3 1

其中,第二行的第二列数据是有缺失的,因此,加载数据之后,它会成为null。顺便废话一句,在处理海量数据时,数据有缺失是经常遇到的现象。
现在,我们如果要把所有缺失的数据填为 -1, 可以使用三目运算符来操作:

1
2
3
A = LOAD'a.txt' AS (col1:int, col2:int, col3:int);
B = FOREACH A GENERATE col1, ((col2is null)? -1 : col2), col3;
DUMP B;

输出结果为:

1
2
3
(5,8,9)
(6,-1,0)
(4,3,1)

((col2 is null)? -1 : col2) 的含义不用解释你也知道,就是当col2为null的时候将其置为-1,否则就保持原来的值,但是注意,它最外面是用括号括起来的,如果去掉括号,写成 (col2 is null)? -1 : col2,那么就会有语法错误:

ERROR org.apache.pig.tools.grunt.Grunt – ERROR 1000: Error during parsing. Encountered " "is" "is "" at line 1, column 36.
Was expecting one of (后面省略)
错误提示有点不直观。所以,有时候使用三目运算符是必须要使用括号的。

(32)如何补上缺失的数据
通过前面的文章,我们已经知道了如何按自己的需求补上缺失的数据,那么这里还有一个例子,可以让你多了解一些特殊的情况。
数据文件如下:

1
2
3
4
5
6
[root@localhost ~]# cat 1.txt
1 (4,9)
5
8 (3,0)
5 (9,2)
6

这些数据的布局比较怪,我们要把它加载成什么样的schema呢?答:第一列为一个int,第二列为一个tuple,此tuple又含两个int。加载成这样的模式不是为了制造复杂度,而是为了说明后面的问题而设计的。
同时,我们也注意到,第二列数据是有缺失的。
问题:怎样求在第一列数据相同的情况下,第二列数据中的第一个整数的和分别为多少?
例如,第一列为1的数据只有一行(即第一行),因此,第二列的第一个整数的和就是4。
但是对最后一行,也就是第一列为6时,由于其第二列数据缺失,我们希望它输出的结果是0。
先来看看Pig代码:

1
2
3
4
5
A = LOAD'1.txt' AS (a:int, b:tuple(x:int, y:int));
B = FOREACH A GENERATE a, FLATTEN(b);
C = GROUPB BY a;
D = FOREACH C GENERATEgroup, SUM(B.x);
DUMP D;

结果为:

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

我们注意到,(5,9) 这一行是由数据文件 1.txt 的第 2、4行计算得到的,其中,第2行数据有缺失,但这并不影响求和计算,因为另一行数据没有缺失。你可以这样想:一个包(bag)中有多个数,当其中一个为null,而其他不为null时,把它们相加会自动忽略null。
然而,第三行 (6,) 是不是太刺眼了?没错,因为数据文件 1.txt 的最后一行缺失了第二列,所以,在 SUM(B.x) 中的 B.x 为null就会导致计算结果为null,从而什么也输出不了。
这就与我们期望的输出有点不同了。我们希望这种缺失的数据不要空着,而是输出0。该怎么做呢?
文章来源:http://www.codelast.com/
想法1

1
D = FOREACH C GENERATEgroup, ((IsEmpty(B.x)) ? 0 :SUM(B.x));

输出结果为:

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

可见行不通。从这个结果我们知道,IsEmpty(B.x) 为false,即B.x不是empty的,所以不能这样做。
想法2

1
D = FOREACH C GENERATEgroup, ((B.x is null) ? 0 :SUM(B.x));

输出结果还是与上面一样!仍然行不通。这更奇怪了:B.x既非empty,也非null,那么它是什么情况?按照我的理解,当group为6时,它应该是一个非空的包(bag),里面有一个null的东西,所以,这个包不是empty的,它也非null。我不知道这样理解是否正确,但是它看上去就像是这样的。
想法3

1
2
3
D = FOREACH C GENERATEgroup, SUM(B.x)AS s;
E = FOREACH D GENERATEgroup, ((s is null) ? -1 : s);
DUMP E;

输出结果为:

1
2
3
4
(1,4)
(5,9)
(6,-1)
(8,3)

可见达到了我们想要的结果。这与本文前面部分的做法是一致的,即:先得到含null的结果,再把这个结果中的null替换为指定的值。
有人会问:就没有办法在生成数据集D的时候,就直接通过判断语句来实现这个效果吗?据我目前所知是不行的,如果哪位读者知道,不妨告知。

(33)DISTINCT操作用于去重,正因为它要把数据集合到一起,才知道哪些数据是重复的,因此,它会产生reduce过程。同时,在map阶段,它也会利用combiner来先去除一部分重复数据以加快处理速度。

(34)如何将Pig job的优先级设为HIGH
嫌Pig job运行太慢?只需在Pig脚本的开头加上一句:

1
set job.priority HIGH;

即可将Pig job的优先级设为高了。

(35)Scalars can be only used with projections”错误的原因
这个错误提示比较不直观,光看这句话是不容易发现错误所在的,但是,只要你一Google,可能就找到原因了,例如这个链接里的反馈。
在这里,我也想用一个简单的例子给大家用演示一下产生这个错误的原因之一。
假设有如下数据文件:

1
2
3
4
5
6
7
8
9
[root@localhost ~]$cat 1.txt
a 1
b 8
c 3
c 3
d 6
d 3
c 5
e 7

现在要统计:在第1列的每一种组合下,第二列为3和6的数据分别有多少条?
例如,当第1列为 c 时,第二列为3的数据有2条,为6的数据有0条;当第1列为d时,第二列为3的数据有1条,为6的数据有1条。其他的依此类推。
Pig代码如下:

1
2
3
4
5
6
7
8
A = LOAD'1.txt' AS (col1:chararray, col2:int);
B = GROUPA BY col1;
C = FOREACH B {
  D = FILTER ABY col2 == 3;
  E = FILTER ABY col2 == 6;
  GENERATEgroup, COUNT(D),COUNT(E);
};
DUMP C;

输出结果为:

1
2
3
4
5
(a,0,0)
(b,0,0)
(c,2,0)
(d,1,1)
(e,0,0)

可见结果是正确的。
文章来源:http://www.codelast.com/
那么,如果我在上面的代码中,把“D = FILTER A BY col2 == 3”不小心写成了“D = FILTERB BY col2 == 3”,就肯定会得到“Scalars can be only used with projections”的错误提示。
说白了,还是要时刻注意你每一步生成的数据的结构,眼睛睁大,千万不要用错了relation。

(36)什么是嵌套的FOREACH/内部的FOREACH
嵌套的(nested)FOREACH和内部的(inner)FOREACH是一个意思,正如你在本文第(35)条中所见,一个FOREACH可以对每一条记录施以多种不同的关系操作,然后再GENERATE得到想要的结果,这就是嵌套的/内部的FOREACH。

(37)错误“Could not infer the matching function for org.apache.pig.builtin.CONCAT”的原因之一
如果你遇到这个错误,那么有可能是你在多级CONCAT嵌套的时候,没有写对语句,例如“CONCAT(CONCAT(CONCAT(a, b), c), d)”这样的嵌套,由于括号众多,所以写错了是一点也不奇怪的。我遇这个错误的时候,是由于CONCAT太多,自己多写了一个都没有发现。希望我的提醒能给你一点解决问题的提示。

转载:http://www.codelast.com/


原创粉丝点击