联合两个RDD Spark scala,保持右侧

时间:2015-10-21 20:07:37

标签: scala apache-spark apache-spark-sql spark-dataframe

我有两个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]

所以,我想要一个带有明显itens的结果(关于复合键),但是当我在两个rdd中找到一个具有相同键的记录时,我想要保留rdd2中的记录。

有人有这样的要求吗?

我正在使用spark和scala。

最诚挚的问候 拉斐尔。

3 个答案:

答案 0 :(得分:0)

我对Spark很新,所以可能有更好的方法来实现这一点,但是你可能会映射到一对RDD(基于你的复合键),然后执行一个fullOuterJoin,只使用& #34;右"结果数据中的元素,其中" left"和"对"两侧?

粗伪代码:

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)
}

如果我早上有时间,我会试着拼凑一个有效的例子。希望有所帮助!

答案 1 :(得分:0)

@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)
}

这将为您提供您正在寻找的结果。

答案 2 :(得分:0)

看起来@Steven的回答在逻辑上很好,但如果你的数据没有很多交叉元素,那么可能会遇到问题(即一个完整的外连接会产生一个巨大的数据集)。您还使用了DataFrame,因此转换为RDD然后返回DataFrames对于可以使用DataFrames API完成的任务来说似乎过多。我将在下面描述如何执行此操作。

让我们从一些示例数据开始(取自您的示例):

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)))

接下来,我们可以在单独的列别名下将它们转换为DataFrame。我们在df1df2使用不同的别名,因为当我们最终加入这两个DataFrame时,后续的选择可以更容易编写(如果有一种方法可以在连接后识别列的来源,这是不必要的)。请注意,两个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|
// +-----------+-------+------+-----+

在这里,我们首先加入关键字上的两个DataFrame,即df1df2的前两个元素。然后,我们通过选择行(主要来自df1)来创建另一个DataFrame,其中存在来自df2的具有相同连接键的行。之后,我们在df1上运行除以从之前创建的DataFrame中删除所有行。这可以看作是补充,因为我们基本上已经完成了删除df1("id_location", "id_item")df2中存在相同df2的所有行。最后,我们将补码与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| // +-----------+-------+------+-----+ 结合在一起以生成输出DataFrame。

subtractByKey()

再次,就像@Steven建议的那样,您可以通过将DataFrame转换为RDD并使用它来运行RDD API。如果这是你想要做的,以下是使用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) 和上面的输入RDD完成你想要的东西的另一种方式:

String username=edittext1.getText().toString();
if(username.matches("")) {
     edittext1.setError("Invalid ");
}