我已经为此花了一段时间-非常感谢任何建议! 抱歉,我的长标题很抱歉,我希望我在下面构造的一个简短示例可以更好地说明这一点。
假设我们具有以下形式的RDD:
data = sc.parallelize([(1,[('k1',4),('k2',3),('k1',2)]),\
(2,[('k3',1),('k3',8),('k1',6)])])
data.collect()
输出:
[(1, [('k1', 4), ('k2', 3), ('k1', 2)]),
(2, [('k3', 1), ('k3', 8), ('k1', 6)])]
我希望对(key,val)RDD的最深列表进行以下操作
.reduceByKey(lambda a, b: a + b)
(即按键减少这些RDD的值以按键获取总和,同时保留与初始较高级别RDD的键所映射的结果,这将产生以下输出):
[(1, [('k1', 6), ('k2', 3)]),
(2, [('k3', 9), ('k1', 6)])]
我是PySpark的新手,可能这里缺少一些基本知识,但是我已经尝试了很多不同的方法,但是本质上找不到找到和减少列表中(key,val)RDD的reduceByKey的方法。 ,它本身就是另一个RDD的值。
非常感谢!
Denys
答案 0 :(得分:1)
您想要做的是:您的值(在输入K,V中)是一个 iterable (可迭代),您想在其上对内键求和并将结果返回==
(outer_key(例如1,2 )->列表(Inner_Key(例如“ K1”,“ K2” ),Summed_value))
如您所见,总和是根据内部键V 计算的, 我们可以通过
从每个列表项中首先剥离元素
=>新建一个(外键,内键)
=>对(outer_key,inner_key)->值求和
=>将数据格式更改回(outer_key->(inner_key,summed_value))
再次=>再次对外键进行分组
我不确定使用Python,但是相信仅用python替换Scala集合语法就足够了,这就是解决方案
SCALA版本
scala> val keySeq = Seq((1,List(("K1",4),("K2",3),("K1",2))),
| (2,List(("K3",1),("K3",8),("K1",6))))
keySeq: Seq[(Int, List[(String, Int)])] = List((1,List((K1,4), (K2,3), (K1,2))), (2,List((K3,1), (K3,8), (K1,6))))
scala> val inRdd = sc.parallelize(keySeq)
inRdd: org.apache.spark.rdd.RDD[(Int, List[(String, Int)])] = ParallelCollectionRDD[111] at parallelize at <console>:26
scala> inRdd.take(10)
res64: Array[(Int, List[(String, Int)])] = Array((1,List((K1,4), (K2,3), (K1,2))), (2,List((K3,1), (K3,8), (K1,6))))
// And solution :
scala> inRdd.flatMap { case (i,l) => l.map(l => ((i,l._1),l._2)) }.reduceByKey(_+_).map(x => (x._1._1 ->(x._1._2,x._2))).groupByKey.map(x => (x._1,x._2.toList.sortBy(x =>x))).collect()
// RESULT ::
res65: Array[(Int, List[(String, Int)])] = Array((1,List((K1,6), (K2,3))), (2,List((K1,6), (K3,9))))
UPDATE => Python解决方案
>>> data = sc.parallelize([(1,[('k1',4),('k2',3),('k1',2)]),\
... (2,[('k3',1),('k3',8),('k1',6)])])
>>> data.collect()
[(1, [('k1', 4), ('k2', 3), ('k1', 2)]), (2, [('k3', 1), ('k3', 8), ('k1', 6)])]
# Similar operation
>>> data.flatMap(lambda x : [ ((x[0],y[0]),y[1]) for y in x[1]]).reduceByKey(lambda a,b : (a+b)).map(lambda x : [x[0][0],(x[0][1],x[1])]).groupByKey().mapValues(list).collect()
# RESULT
[(1, [('k1', 6), ('k2', 3)]), (2, [('k3', 9), ('k1', 6)])]
答案 1 :(得分:0)
您应该映射数据集而不是减少数据集,因为示例中的行数与源数据集中的行数相同,因此在map内部您可以将值减少为python list
答案 2 :(得分:0)
使用 mapValues() + itertools.groupby():
from itertools import groupby
data.mapValues(lambda x: [ (k, sum(f[1] for f in g)) for (k,g) in groupby(sorted(x), key=lambda d: d[0]) ]) \
.collect()
#[(1, [('k1', 6), ('k2', 3)]), (2, [('k1', 6), ('k3', 9)])]
使用 itertools.groupby ,我们将元组的第一项用作分组键k
,然后将每个g
中元组的第二项相加。 >
编辑:对于大型数据集,使用itertools.groupby进行排序非常昂贵,只需编写一个不带排序功能的函数即可处理该问题:
def merge_tuples(x):
d = {}
for (k,v) in x:
d[k] = d.get(k,0) + v
return d.items()
data.mapValues(merge_tuples).collect()
#[(1, [('k2', 3), ('k1', 6)]), (2, [('k3', 9), ('k1', 6)])]