Apache PIG 非常适合此类问题。可以用一个 GROUP BY 和一个嵌套 FOREACH 解决
inpt = load '~/pig/data/group_pivot.csv' using PigStorage(',') as (val : chararray, cat : chararray);
grp = group inpt by (val);
final = foreach grp {
rBag = filter inpt by cat == 'R';
yBag = filter inpt by cat == 'Y';
cBag = filter inpt by cat == 'C';
generate flatten(group) as val, SIZE(rBag) as R, SIZE(yBag) as Y, SIZE(cBag) as C;
};
dump final;
--(bar,0,0,1)
--(baz,1,2,0)
--(foo,3,1,0)
bool = foreach final generate val, (R == 0 ? 0 : 1) as R, (Y == 0 ? 0 : 1) as Y, (C == 0 ? 0 : 1) as C;
dump bool;
--(bar,0,0,1)
--(baz,1,1,0)
--(foo,1,1,0)
我已经在您的示例中进行了尝试,并得到了预期的结果。这个想法是,在 GROUP BY 之后,每个值都有一个 BAG,其中包含所有具有 R、Y、C 类别的行。使用 FOREACH 中的 FILTER,我们创建了 3 个单独的 BAG(每个 R、Y、C 一个),并且 GENERATE 中的 SIZE(bag) 计算每个包中的行数。
您可能遇到的唯一问题是当 val 列中具有相同值的行太多时,因为嵌套的 FOREACH 依赖于内存操作,并且生成的中间 BAG 可能会变得非常大。如果您开始获得与内存相关的异常,那么您可以从How to handle splash memory in pig 中获得启发。想法是使用 2 个 GROUP BY 操作,第一个获取每个 (val, cat) 的计数,第二个以 val 为中心旋转 R、Y、C,从而避免昂贵的 JOIN 操作(请参阅Pivoting in Pig)。
关于 BOOLEAN 的问题:我使用了 bincond 运算符。如果您不需要计数,您可以使用 IsEmpty(bag) 而不是 SIZE(bag),它会稍微快一些并且 bincond 来获得您的 0 和 1 转换。