将键值元组包转换为 Apache Pig 中的映射

Posted

技术标签:

【中文标题】将键值元组包转换为 Apache Pig 中的映射【英文标题】:Transform bag of key-value tuples to map in Apache Pig 【发布时间】:2013-07-25 02:23:28 【问题描述】:

我是 Pig 新手,我想将一袋元组转换为一个映射,其中每个元组中的特定值作为键。基本上我想改变:

(id1, value1),(id2, value2), ... 转为[id1#value1, id2#value2]

我在网上搜索了一段时间,但似乎找不到解决方案。我试过了:

bigQMap = FOREACH bigQFields GENERATE TOMAP(queryId, queryStart);

但我最终得到了一袋地图(例如[id1#value1], [id2#value2], ...),这不是我想要的。如何从一袋键值元组中构建地图?

以下是我尝试运行的特定脚本,以防相关

rawlines = LOAD '...' USING PigStorage('`');
bigQFields = FOREACH bigQLogs GENERATE GFV(*,'queryId')
   as queryId, GFV(*, 'queryStart')
   as queryStart;
bigQMap = ?? how to make a map with queryId as key and queryStart as value ?? ;

【问题讨论】:

我以前从未见过GFV 函数,所以我不确定它输出什么,但(id1, value1),(id2, value2), ...bigQFields 生成架构的一部分吗? 实际上,您只需发布 bigQFields 的架构,因为这就是您想要转换为地图的内容,对吗? 是的。 GFV 只是我使用的 UDF。 请注意:我只是提出了对现有 Pig TOMAP 功能的增强:issues.apache.org/jira/browse/PIG-4638 【参考方案1】:

TOMAP 采用一系列对并将它们转换为地图,因此它的使用方式如下:

-- Schema: A:foo:chararray, bar:int, bing:chararray, bang:int
-- Data:     (John,          27,      Joe,            30)
B = FOREACH A GENERATE TOMAP(foo, bar, bing, bang) AS m ;
-- Schema: B:m: map[]
-- Data:     (John#27,Joe#30)

所以你可以看到语法不支持将包转换为地图。据我所知,没有办法将包转换为您必须在纯猪中映射的格式。但是,您可以明确地写一个java UDF 来执行此操作。

注意:我对 java 不太熟悉,所以这个 UDF 可以很容易地改进(添加异常处理,如果一个键添加两次会发生什么等)。但是,它确实可以完成您需要的工作。

package myudfs;
import java.io.IOException;
import org.apache.pig.EvalFunc;

import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.pig.data.Tuple;
import org.apache.pig.data.DataBag;

public class ConvertToMap extends EvalFunc<Map>

    public Map exec(Tuple input) throws IOException 
        DataBag values = (DataBag)input.get(0);
        Map<Object, Object> m = new HashMap<Object, Object>();
        for (Iterator<Tuple> it = values.iterator(); it.hasNext();) 
            Tuple t = it.next();
            m.put(t.get(0), t.get(1));
        
        return m;
    

将脚本编译成 jar 后,可以像这样使用它:

REGISTER myudfs.jar ;
-- A is loading some sample data I made
A = LOAD 'foo.in' AS (foo:T:(id:chararray, value:chararray)) ;
B = FOREACH A GENERATE myudfs.ConvertToMap(foo) AS bar;

foo.in的内容:

(open,apache),(apache,hadoop)
(foo,bar),(bar,foo),(open,what)

B 的输出:

([open#apache,apache#hadoop])
([bar#foo,open#what,foo#bar])

另一种方法是使用python to create the UDF:

myudfs.py

#!/usr/bin/python

@outputSchema("foo:map[]")
def BagtoMap(bag):
    d = 
    for key, value in bag:
        d[key] = value
    return d

这样使用:

Register 'myudfs.py' using jython as myfuncs;
-- A is still just loading some of my test data
A = LOAD 'foo.in' AS (foo:T:(key:chararray, value:chararray)) ;
B = FOREACH A GENERATE myfuncs.BagtoMap(foo) ;

并产生与 Java UDF 相同的输出。


奖励: 由于我不太喜欢地图,here 是一个链接,它解释了如何仅使用键值对来复制地图的功能。由于您的键值对在一个包中,因此您需要在嵌套的FOREACH 中执行类似映射的操作:

-- A is a schema that contains kv_pairs, a bag in the form (id, value)
B = FOREACH A 
    temp = FOREACH kv_pairs GENERATE (key=='foo'?value:NULL) ;
    -- Output is like: ((),(thevalue),(),())

    -- MAX will pull the maximum value from the filtered bag, which is 
    -- value (the chararray) if the key matched. Otherwise it will return NULL.
    GENERATE MAX(temp) as kv_pairs_filtered ;

【讨论】:

嗯,奖励部分看起来很容易实现,但这不会比只使用地图效率低得多吗? 是的,但我不确定多少。但是对于您在效率上的损失,您可以通过灵活性来弥补。您可以同时提取键和值,一次性提取多个值,FLATTEN 它,在行中使用变量而不是带引号的字符串来获取值等。同时使您不必编写任何 Java (这可能是一个加号,也可能不是一个加号,具体取决于您对 Java 的喜爱程度)。 @Penguinator 但是,如果我必须将包转换为地图,那么我将使用 python UDF(我刚刚更新了我的答案以包含一个示例)。 +1 这很棒。我认为地图在概念上更好,但我不想创建对 jar 的依赖。【参考方案2】:

我遇到了同样的情况,所以我提交了一个刚刚被接受的补丁:https://issues.apache.org/jira/browse/PIG-4638

这意味着你想要的是从 pig 0.16 开始的核心部分。

【讨论】:

以上是关于将键值元组包转换为 Apache Pig 中的映射的主要内容,如果未能解决你的问题,请参考以下文章

如何将键值映射转换为 int 切片 [关闭]

为什么我需要将方法强制转换为`Action`或`Func`以在值元组中使用它?

pig 新手,如何使用 pig 中的键值对子集将 JSON 转换为另一个 JSON?

使用 Apache Spark 将键值对缩减为键列表对

apache pig Java UDF - 更改属性中的值似乎并没有坚持

检查值元组是不是为默认值