减少袋子传递给udf

时间:2014-02-11 19:49:14

标签: hadoop apache-pig hadoop-streaming

在Hadoop集群上使用Pig,我有一个巨大的bag巨大的tuples我经常添加字段,因为我继续在这个项目上工作,以及几个使用它的各种字段的UDF 。我希望能够在每个tuple的几个字段上调用UDF,并且将结果重新连接到该特定元组。使用唯一ID进行连接以重新连接记录将永远占用数十亿条记录。

我认为在GENERATE语句中应该有一种方法可以做到这一点,但我找不到合适的语法。

以下是使用Python UDF获取想法的玩具代码。

Register 'jumper.py' using jython as myfuncs;

jumps = LOAD 'jumps.csv' USING PigStorage(',')
   AS (jumper:int, attempt:int, distance:double, location:chararray);

byJumper = GROUP jumps by jumper;

sigmas = FOREACH byJumper GENERATE
    jumps.jumper, jumps.attempt, jumps.distance, jumps.location,
    myfuncs.conv2sigma(jumps.distance);

rmf sigmas
STORE sigmas INTO 'sigmas' USING PigStorage(',');

这是在每个元组中生成包含单个字段的元组,而不是我期望的形式的元组。

输入数据

  • 人(具有唯一的整数ID),
  • 他们的跳远尝试(具有唯一的那个人整数ID),
  • 他们跳的距离,
  • 当时他们跳的位置。

对于每次跳跃,我们想要生成跳线与其平均值有多少标准差(sigma),然后我们将按位置关联sigma以查看跳线在哪里做得最好。我们需要计算每个人的平均值和标准差,然后计算每次跳转的“sigma”,并将数据与附加的新sigma字段一起存储。

问题是:

  

我们如何将其更改为输出(jumper:int, attempt:int, distance:double, location:chararray, sigma:double)等元组?

我已经以各种方式尝试过FLATTEN,它只会给我带来巨大的交叉产品。我可以更改我的UDF以接受跳线并尝试输出三元组然后执行JOIN,但在现实世界中,由于数据集的大小,此解决方案非常不切实际。

如果您想在家中试用,这是支持代码和数据:

jumper.py :(一个快速,不深思熟虑的实现 - 这里唯一重要的是它需要一个包输入并产生一个包输出,其中一个输出元组对应于每个输入元组)

#!/usr/local/bin/python
# we're forced to use python 2.5.2 :-(

from math import sqrt

@outputSchema("y:bag{t:tuple(sigma:double)}")
def conv2sigma(bag):
    s = 0.0
    n = 0
    dd = []
    print('conv2sigma input bag:')
    print(bag)
    for word in bag:
        d = float(word[0])
        dd.append(d)
        n += 1
        s += d
    a = s / n
    ss = 0
    for d in dd:
        ss += (d-a)**2
    sd = sqrt(ss)
    outputBag = []
    for d in dd:
        outputBag.append( ( (d-a)/sd, ) )
    print('conv2sigma output bag:')
    print(outputBag)
    return outputBag

输入文件jumps.csv

0,0,5,a
0,1,6,b
0,2,7,c
0,3,5,a
0,4,8,c
0,5,7,b
0,6,6,b
0,7,7,c
0,8,5,a
1,0,6,a
1,1,5,a
1,2,7,b
1,3,4,a
1,4,5,a
1,5,7,b
1,6,8,c
1,7,9,c
1,8,5,a
1,9,4,a
1,10,5,a
1,11,6,b
1,12,8,c
1,13,8,b
2,0,7,b
2,1,5,a
2,2,6,b
2,3,5,a
2,4,7,c
2,5,5,a
2,6,6,c
2,7,5,a
2,8,7,b
2,9,5,a
2,10,6,b

现在生成的输出:

{(0),(0),(0),(0),(0),(0),(0),(0),(0)},{(1),(2),(3),(4),(5),(6),(7),(8),(0)},{(6.0),(7.0),(5.0),(8.0),(7.0),(6.0),(7.0),(5.0),(5.0)},{(b),(c),(a),(c),(b),(b),(c),(a),(a)},{(-0.07188851546895898),(0.25160980414135625),(-0.39538683507927425),(0.5751081237516715),(0.25160980414135625),(-0.07188851546895898),(0.25160980414135625),(-0.39538683507927425),(-0.39538683507927425)}
{(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)},{(8),(0),(1),(2),(3),(4),(5),(6),(7),(9),(10),(11),(12),(13)},{(5.0),(6.0),(5.0),(7.0),(4.0),(5.0),(7.0),(8.0),(9.0),(4.0),(5.0),(6.0),(8.0),(8.0)},{(a),(a),(a),(b),(a),(a),(b),(c),(c),(a),(a),(b),(c),(b)},{(-0.20716308289978433),(-0.03655819109996196),(-0.20716308289978433),(0.1340467006998604),(-0.3777679746996067),(-0.20716308289978433),(0.1340467006998604),(0.30465159249968277),(0.4752564842995052),(-0.3777679746996067),(-0.20716308289978433),(-0.03655819109996196),(0.30465159249968277),(0.30465159249968277)}
{(2),(2),(2),(2),(2),(2),(2),(2),(2),(2),(2)},{(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)},{(7.0),(5.0),(6.0),(5.0),(7.0),(5.0),(6.0),(5.0),(7.0),(5.0),(6.0)},{(b),(a),(b),(a),(c),(a),(c),(a),(b),(a),(b)},{(0.4276686017238498),(-0.2960782627318961),(0.06579516949597684),(-0.2960782627318961),(0.4276686017238498),(-0.2960782627318961),(0.06579516949597684),(-0.2960782627318961),(0.4276686017238498),(-0.2960782627318961),(0.06579516949597684)}

每个输出元组都是一个行李集合,每个行李都包含来自一个字段的单个条目的元组,这不是我们想要的。

2 个答案:

答案 0 :(得分:1)

您需要分两步完成此操作。每个跳转都有自己的sigma值,因此为了将每个sigma与正确的跳转正确关联,您需要将ID传递给sigma计算UDF,然后将结果重新加入(坏主意),或者计算首先汇总统计数据(均值和标准差),然后从中推导出sigma。方法如下:

jumps = LOAD 'jumps.csv' USING PigStorage(',')
   AS (jumper:int, attempt:int, distance:double, location:chararray);

byJumper = GROUP jumps by jumper;

jumperSummaries =
    FOREACH byJumper
    GENERATE
        group AS jumper,
        FLATTEN(jumps.(attempt, distance, location)),
        myfuncs.mean(jumps.distance) AS mean,
        myfunds.stddev(jumps.distance) AS stddev;

sigmas =
    FOREACH jumperSummaries
    GENERATE
        jumper,
        attempt,
        distance,
        location,
        myfuncs.sigma(distance, mean, stddev) AS sigma;

FLATTEN取消所有跳转的组合并返回原始输入,但现在每个记录都复制了该跳线的均值和标准差,然后您可以使用它来计算每个跳跃行的sigma逐行。

请注意,虽然这在概念上是两个步骤,但它仍然只需要一个map-reduce作业。

答案 1 :(得分:0)

与WinnieNicklaus'进行比较回答,并提出意见,这是我提出的解决方案:

Register 'jumper.py' using jython as myfuncs;

jumps = LOAD 'jumps.csv' USING PigStorage(',')
   AS (jumper:int, attempt:int, distance:double, location:chararray);

byJumper = GROUP jumps by jumper;

sigmas0 = FOREACH byJumper
    GENERATE
        FLATTEN(jumps),
        FLATTEN(myfuncs.conv2sigma(jumps.(jumper,attempt,distance)));

sigmas1 = FILTER sigmas0 BY jumper == s_id AND attempt == s_att;

sigmas = FOREACH sigmas1
        GENERATE jumper, attempt, distance, location, sigma;

rmf sigmas
STORE sigmas INTO 'sigmas' USING PigStorage(',');

第一个FOREACH会创建一个(可能很大的)产品sigma0,过滤掉"错误的"产品的元素并生成所需的字段。 JOIN通常以学术方式描述。

这似乎可能很慢。

但它仍会导致单个Map-Reduce作业,并且似乎在实践中快速。

对我来说,一个巨大的胜利是它允许我的UD​​F做任意复杂的事情,并返回任意多个重新加入输入数据的元组。