配对数据集中数据字段的 Pig 方法

Posted

技术标签:

【中文标题】配对数据集中数据字段的 Pig 方法【英文标题】:Pig approach to pairing data fields in a data set 【发布时间】:2012-12-02 14:09:12 【问题描述】:

我是 Pig 的新手,我正在尝试正确实现一种有些通用的算法,在该算法中,我需要将一组记录中的每个匹配记录配对。为了将问题提炼成最简单的形式并避免讨论一些特定于业务的敏感性,这里有一个模拟问题:

假设我有一个数据集代表大学课程和参加这些课程的学生:

Philosophy,John
English,Mary
English,Sue
History,Jack
Philosophy,David
English,Mark
English,Larry

我想配对上同一门课的学生之间的每个关联;所以输出将包括这个,显示四个“英语”行爆炸成六个关联:

Philosphy   John,David
English    Mary,Sue
English    Mary,Mark
English    Mary,Larry
English    Sue,Mark
English    Sue,Larry
English    Mark,Larry

此页面:http://ofps.oreilly.com/titles/9781449302641/advanced_pig_latin.html 指的是使用 flatten() 来实现叉积。我已经尝试了几种方法并对此进行了广泛的研究,并会发布我的尝试,但老实说,我正在摇摆不定,我认为这只会让读者感到困惑并且不会提供任何价值。但这是样板:

s = load 'classes' using PigStorage(',') as (class:chararray, student:chararray);
grp = group s by class;
...

(我相信我面临的问题与需要多个袋子而不是多个字段的扁平化有关,而且我不知道如何让我的分组生成多个袋子......)

感谢您的帮助!

【问题讨论】:

【参考方案1】:

您可以使用 LinkedIn 的 Datafu project 中的 UnorderedPairs UDF。从here 下载包并发出以下命令 (在 Pig v0.10.0 上测试):

register '/home/user/datafu/dist/datafu-0.0.4.jar'
define UnorderedPairs datafu.pig.bags.UnorderedPairs();
A = load 'classes' using PigStorage(',') as (class:chararray, student:chararray);
B = GROUP A BY class;
C = FOREACH B GENERATE group, FLATTEN(UnorderedPairs(A.student));

当进一步展平结果时:

D = FOREACH C generate FLATTEN($0) as (class:chararray), 
      FLATTEN($1) as (student1:chararray), FLATTEN($2) as (student2:chararray);

你最终会得到想要的结果:

dump D;

(English,Mary,Sue)
(English,Mary,Mark)
(English,Mary,Larry)
(English,Sue,Mark)
(English,Sue,Larry)
(English,Mark,Larry)
(Philosophy,John,David)

【讨论】:

感谢 Lorand,这看起来非常好,我要测试一下。 一个后续问题:在用 Java 编写的传统 map-reduce 作业中尝试过之后,我最初求助于 Pig 来解决这个问题。我相信这在中途失败了,因为其中一个 reducer 必须处理大约 20,000 条记录——这会爆发到大约 1E8 对,这需要很长时间,以至于 Job 放弃了等待该 reducer 的消息。我将用你的解决方案尝试这个,但我想知道结果是否会有所不同,因为我有必须配对 60K 记录的情况。谢谢。 在创建结果包时,此 UDF 将每百万对溢出到磁盘,因此希望您不会遇到内存问题。【参考方案2】:

我认为有两种方法。我很长一段时间都没有尝试过,所以请跟进并让我们知道它们是否运作良好。


第一种方法是自加入

s1 = load 'classes' using PigStorage(',') as (class:chararray, student:chararray);
s2 = load 'classes' using PigStorage(',') as (class:chararray, student:chararray);
b = JOIN s1 BY class, s2 BY class;
...

这样做的缺点是您必须加载数据两次。那里有is some discussion on why this sucks,但这就是你必须这样做的方式。


另一种选择是在GROUP 之后使用嵌套在FOREACH 中的CROSS

注意:我完全不确定这是否可行,或者我的语法是否正确(我现在不在一个可以测试它的环境中)。也许有人可以确认。

B = GROUP s BY class;
C = FOREACH B                           
   DA = CROSS s, s;                       
   GENERATE FLATTEN(DA);

【讨论】:

【参考方案3】:

这可以通过自连接和一些简单的过滤来完成。

classes1 = load 'classes' using PigStorage(',') as (class:chararray, student:chararray);
classes2 = load 'classes' using PigStorage(',') as (class:chararray, student:chararray);
joined = JOIN classes1 BY class, classes2 BY class;
filtered = FILTER joined BY classes1.student < classes2.student;
pairs = FOREACH filtered GENERATE classes1.student AS student1, classes2.student AS student2;

请注意,按 student1

【讨论】:

以上是关于配对数据集中数据字段的 Pig 方法的主要内容,如果未能解决你的问题,请参考以下文章

在 pig 中加载由双冒号 :: 分隔的文件

机器学习实战笔记-K近邻算法2(改进约会网站的配对效果)

MAX(Count) 函数 apache pig latin

将字符串连接到 pig 中的字段

pig - 将数据从行转换为列,同时为特定行中不存在的字段插入占位符

Apache Pig:在 Pig 中处理数据类型时面临问题