[英]union two RDDs Spark scala, keeping the right side
我有两个Spark数据框,结构如下。 如使用sqlContext之前阅读的。
itens.columns (scala command)
Array[String] = Array(id_location,id_item, name, price)
rdd1
[1,1,item A,10]
[1,2,item b,12]
[1,3,item c,12]
rdd2
[1,2,item b,50]
[1,4,item c,12]
[1,5,item c,12]
我想要基于组合键(id_location,id_item)的以下结果
[1,1,item A,10]
[1,2,item b,50]
[1,3,item c,12]
[1,4,item c,12]
[1,5,item c,12]
因此,我想要一个具有不同iten的结果(关于组合键),但是当我在两个rdds中找到具有相同键的记录时,我只想保留rdd2中的记录。
有人有这种要求吗?
我正在使用Spark和Scala。
最好的问候拉斐尔。
我是Spark的新手,所以可能有更好的方法,但是您是否可以映射到一对RDD(基于您的复合键),然后执行fullOuterJoin,仅使用结果数据中“左”边和“右”边都有数据吗?
粗糙的伪代码:
val pairRdd1 = rdd1 map {
line =>
(line(0)+line(1), line)
}
val pairRdd2 = rdd2 map {
line =>
(line(0)+line(1), line)
}
val joined = pairRdd1.fullOuterJoin(pairRdd2)
joined map {
(id, left, right) =>
right.getOrElse(left.get)
}
如果我早上有时间,我将尝试结合一个可行的例子。 希望有帮助!
@Steven有正确的想法。 您需要将数据集映射到键值对,然后执行外部outerjoin
val rdd1 = sc.parallelize(List((1,1,"item A",10),(1,2,"item b",12),(1,3,"item c",12)))
val rdd2 = sc.parallelize(List((1,2,"item b",50),(1,4,"item c",12),(1,5,"item c",12)))
val rdd1KV = rdd1.map{case(id_location,id_item, name, price) => ((id_location, id_item), (name, price))}
val rdd2KV = rdd2.map{case(id_location,id_item, name, price) => ((id_location, id_item), (name, price))}
val joined = rdd1KV.fullOuterJoin(rdd2KV)
val res = joined.map{case((id_location, id_item),(leftOption, rightOption)) =>
val values = rightOption.getOrElse(leftOption.get)
(id_location, id_item, values._1, values._2)
}
这将为您提供所需的结果。
看起来@Steven的回答在逻辑上是不错的,但是如果您的数据没有很多相交的元素(即完整的外部联接将产生巨大的数据集),则可能会遇到问题。 您还使用了DataFrames,因此对于可以通过DataFrames API完成的任务而言,转换为RDD然后再转换为DataFrames似乎过多。 我将在下面介绍如何执行此操作。
让我们从一些示例数据开始(从您的示例中获取):
val rdd1 = sc.parallelize(Array((1,1,"item A",10), (1,2,"item b",12), (1,3,"item c",12)))
val rdd2 = sc.parallelize(Array((1,2,"item b",50), (1,4,"item c",12), (1,5,"item c",12)))
接下来,我们可以在单独的列别名下将它们转换为DataFrames。 在这里,我们在df1
和df2
使用了不同的别名,因为当我们最终连接这两个DataFrame时,可以更容易地编写后续的select(如果有一种方法可以在连接后标识列的来源,则没有必要)。 请注意,两个DataFrame的并集都包含要过滤的行。
val df1 = rdd1.toDF("id_location", "id_item", "name", "price")
val df2 = rdd2.toDF("id_location_2", "id_item_2", "name_2", "price_2")
// df1.unionAll(df2).show()
// +-----------+-------+------+-----+
// |id_location|id_item| name|price|
// +-----------+-------+------+-----+
// | 1| 1|item A| 10|
// | 1| 2|item b| 12|
// | 1| 3|item c| 12|
// | 1| 2|item b| 50|
// | 1| 4|item c| 12|
// | 1| 5|item c| 12|
// +-----------+-------+------+-----+
在这里,我们首先将键上的两个DataFrames连接在一起,它们是df1
和df2
的前两个元素。 然后,我们通过选择行(基本上来自df1
)来创建另一个DataFrame,其中存在来自df2
具有相同联接键的行。 之后,我们在df1
上运行except,以从先前创建的DataFrame中删除所有行。 这可以看作是一个补充,因为我们基本上要做的是从df1
中删除所有行,而df2
存在相同的行("id_location", "id_item")
。 最后,我们将补码与df2
结合在一起以生成输出DataFrame。
val df_joined = df1.join(df2, (df1("id_location") === df2("id_location_2")) && (df1("id_item") === df2("id_item_2")))
val df1_common_keyed = df_joined.select($"id_location", $"id_item", $"name", $"price")
val df1_complement = df1.except(df1_common_keyed)
val df_union = df1_complement.unionAll(df2)
// df_union.show()
// +-----------+-------+------+-----+
// |id_location|id_item| name|price|
// +-----------+-------+------+-----+
// | 1| 3|item c| 12|
// | 1| 1|item A| 10|
// | 1| 2|item b| 50|
// | 1| 4|item c| 12|
// | 1| 5|item c| 12|
// +-----------+-------+------+-----+
同样,就像@Steven所建议的那样,您可以通过将DataFrames转换为RDD并与其一起运行来使用RDD API。 如果这是您要执行的操作,则以下是使用上面的subtractByKey()
和输入RDD来完成所需操作的另一种方法:
val keyed1 = rdd1.keyBy { case (id_location, id_item, _, _) => (id_location, id_item) }
val keyed2 = rdd2.keyBy { case (id_location, id_item, _, _) => (id_location, id_item) }
val unionRDD = keyed1.subtractByKey(keyed2).values.union(rdd2)
// unionRDD.collect().foreach(println)
// (1,1,item A,10)
// (1,3,item c,12)
// (1,2,item b,50)
// (1,4,item c,12)
// (1,5,item c,12)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.