У меня есть два фрейма данных:

 RegionValues:
+-----------+----------+----------------------+
|marketplace|primary_id|values                |
+-----------+----------+----------------------+
|xyz        |0000000001|[cat, dog, cow]       |
|reg        |PRT0000001|[hippo, dragon, moose]|
|asz        |0000001333|[mouse, rhino, lion]  |
+-----------+----------+----------------------+

Marketplace:
 
+----------+-----------+----------+
|primary_id|marketplace|parent_id |
+----------+-----------+----------+
|0000000001|xyz        |PRT0000001|
|0000000002|wrt        |PRT0000001|
|PRT0000001|reg        |PRT0000001|
|PRT00MISS0|asz        |PRT00MISS0|
|000000000B|823        |PRT0000002|
+----------+-----------+----------+

Когда я объединяю фреймы данных вместе, я хочу присоединиться к ним на основе значения primary_id, но если поле primary_id отсутствует в фрейме данных RegionValues, то я хочу вернуться к присоединению по parent_id === primary_id. Итак, мой желаемый результат будет:

+----------+--------------+-----------+-------------------------------------+
|primary_id|marketplace   |parent_id  |values                               |
+----------+--------------+-----------+-------------------------------------+
|0000000001|...           |PRT0000001 |[cat, dog, cow]                      |
|0000000002|...           |PRT0000001 |[hippo, dragon, moose]               |
|PRT0000001|...           |PRT0000001 |[hippo, dragon, moose]               |
|PRT00MISS0|              |PRT00MISS0 |null                                 |
|0000001333|              |0000001333 |[mouse, rhino, lion]                 |
|000000000B|              |PRT0000002 |null                                 |
+----------+--------------+-----------+-------------------------------------+

Обратите внимание, что 0000000001 сохранил свой исходный values, но что 0000000002 взял свой parent_id values, поскольку его нет в RegionValues. Возможно ли реализовать эту логику в операторе соединения? Я использую Scala и Spark.

Я пробовал использовать такой оператор соединения, но это приводит к нулевому значению для значений 0000000002:

val parentIdJoinCondition = when(
    (regionValuesDf.col("primary_id") === marketplaceDf.col("primary_id")).isNull,
    marketplaceDf.col("parent_id") === regionValuesDf.col("primary_id")
  ).otherwise(regionValuesDf.col("primary_id") === marketplaceDf.col("primary_id"))

val joinedDf = regionDf.join(
    marketplaceDf,
    parentIdJoinCondition,
    "outer"
)

Я думаю, что мог бы получить желаемый результат, используя 3 различных соединения, но это кажется ненужным и трудным для чтения.

1
219CID 23 Ноя 2021 в 09:21

2 ответа

Лучший ответ

Создание настраиваемых условий приведет к тому, что Spark выполнит перекрестное соединение, что является очень неэффективным способом соединения. Более того, Spark не может узнать, что столбец не соответствует, до выполнения фактического соединения, поэтому ваше условие regionValuesDf.col("primary_id") === marketplaceDf.col("primary_id")).isNull всегда будет возвращать false.

Итак, как вы правильно догадались, лучшее решение - выполнить несколько стыковок. Вы можете закончить двумя соединениями. Сначала соединение, чтобы определить, следует ли использовать значение primary_id или parent_id для внешнего соединения и фактического внешнего соединения. Затем вы можете объединить primary_id, marketplace и parent_id и удалить ненужные столбцы

Итак, код будет:

import org.apache.spark.sql.functions.{coalesce, col, when}

val joinedDf = marketplaceDf.join(regionDf.drop("marketPlace"), Seq("primary_id"), "left")
  .withColumn("join_key", when(col("values").isNotNull, col("primary_id")).otherwise(col("parent_id")))
  .drop("values")
  .join(
    regionDf
      .withColumnRenamed("primary_id", "join_key")
      .withColumnRenamed("marketplace", "region_marketplace"),
    Seq("join_key"),
    "outer"
  )
  .withColumn("primary_id", coalesce(col("primary_id"), col("join_key")))
  .withColumn("parent_id", coalesce(col("parent_id"), col("join_key")))
  .withColumn("marketplace", coalesce(col("marketplace"), col("region_marketplace")))
  .drop("join_key", "region_marketplace")

Это дает вам следующий фрейм данных joinDf:

+----------+-----------+----------+----------------------+
|primary_id|marketplace|parent_id |values                |
+----------+-----------+----------+----------------------+
|0000000001|xyz        |PRT0000001|[cat, dog, cow]       |
|0000001333|asz        |0000001333|[mouse, rhino, lion]  |
|0000000002|wrt        |PRT0000001|[hippo, dragon, moose]|
|PRT0000001|reg        |PRT0000001|[hippo, dragon, moose]|
|000000000B|823        |PRT0000002|null                  |
|PRT00MISS0|asz        |PRT00MISS0|null                  |
+----------+-----------+----------+----------------------+
2
Vincent Doba 23 Ноя 2021 в 16:04

Не следует ли regionValuesDf.col("primary_id") =!= marketplaceDf.col("primary_id")) вместо regionValuesDf.col("primary_id") === marketplaceDf.col("primary_id")).isNull в справке по оператору соединения?

0
Sanket9394 23 Ноя 2021 в 12:57