我搜索了很长时间的解决方案,但没有得到任何正确的算法。
在scala中使用Spark RDD,我怎么能将RDD[(Key, Value)]
转换为Map[key, RDD[Value]]
,因为我知道我不能使用可能会将数据加载到内存中的collect或其他方法?
事实上,我的最终目标是按键循环Map[Key, RDD[Value]]
并为每个saveAsNewAPIHadoopFile
致电RDD[Value]
例如,如果我得到:
RDD[("A", 1), ("A", 2), ("A", 3), ("B", 4), ("B", 5), ("C", 6)]
我想:
Map[("A" -> RDD[1, 2, 3]), ("B" -> RDD[4, 5]), ("C" -> RDD[6])]
我想知道在filter
的每个密钥A,B,C上使用RDD[(Key, Value)]
进行此操作是否花费不会太多,但我不知道是否多次调用过滤器有不同的键会有效吗? (当然不是,但可能使用cache
?)
谢谢
答案 0 :(得分:2)
你应该使用像这样的代码(Python):
rdd = sc.parallelize( [("A", 1), ("A", 2), ("A", 3), ("B", 4), ("B", 5), ("C", 6)] ).cache()
keys = rdd.keys().distinct().collect()
for key in keys:
out = rdd.filter(lambda x: x[0] == key).map(lambda (x,y): y)
out.saveAsNewAPIHadoopFile (...)
一个RDD不能成为另一个RDD的一部分,您无法只收集密钥并将其相关值转换为单独的RDD。在我的示例中,您将遍历缓存的RDD,这是正常的,并且可以快速工作
答案 1 :(得分:0)
听起来你真正想要的是将KV RDD保存到每个键的单独文件中。而不是创建Map[Key, RDD[Value]]
考虑使用MultipleTextOutputFormat
similar to the example here.代码就是示例中的所有内容。
这种方法的好处是,您可以保证在随机播放后仅通过RDD一次,并获得您想要的相同结果。如果您通过过滤并按照另一个答案中的建议创建多个ID(除非您的源支持下推过滤器),您最终会为每个单独的键对数据集进行一次传递,这会更慢。
答案 2 :(得分:-1)
这是我的简单测试代码。
val test_RDD = sc.parallelize(List(("A",1),("A",2), ("A",3),("B",4),("B",5),("C",6)))
val groupby_RDD = test_RDD.groupByKey()
val result_RDD = groupby_RDD.map{v =>
var result_list:List[Int] = Nil
for (i <- v._2) {
result_list ::= i
}
(v._1, result_list)
}
结果如下
result_RDD.take(3)
>> res86: Array[(String, List[Int])] = Array((A,List(1, 3, 2)), (B,List(5, 4)), (C,List(6)))
或者你可以这样做
val test_RDD = sc.parallelize(List(("A",1),("A",2), ("A",3),("B",4),("B",5),("C",6)))
val nil_list:List[Int] = Nil
val result2 = test_RDD.aggregateByKey(nil_list)(
(acc, value) => value :: acc,
(acc1, acc2) => acc1 ::: acc2 )
结果就是这个
result2.take(3)
>> res209: Array[(String, List[Int])] = Array((A,List(3, 2, 1)), (B,List(5, 4)), (C,List(6)))