webentwicklung-frage-antwort-db.com.de

Spark Dataframe Nested Case When-Anweisung

Ich muss die unten stehende SQL-Logik in Spark DataFrame implementieren.

SELECT KEY,
    CASE WHEN tc in ('a','b') THEN 'Y'
         WHEN tc in ('a') AND amt > 0 THEN 'N'
         ELSE NULL END REASON,
FROM dataset1;

Meine Eingabe DataFrame lautet wie folgt:

val dataset1 = Seq((66, "a", "4"), (67, "a", "0"), (70, "b", "4"), (71, "d", "4")).toDF("KEY", "tc", "amt")

dataset1.show()
+---+---+---+
|KEY| tc|amt|
+---+---+---+
| 66|  a|  4|
| 67|  a|  0|
| 70|  b|  4|
| 71|  d|  4|
+---+---+---+

Ich habe den verschachtelten Fall implementiert, wenn:

dataset1.withColumn("REASON", when(col("tc").isin("a", "b"), "Y")
  .otherwise(when(col("tc").equalTo("a") && col("amt").geq(0), "N")
    .otherwise(null))).show()
+---+---+---+------+
|KEY| tc|amt|REASON|
+---+---+---+------+
| 66|  a|  4|     Y|
| 67|  a|  0|     Y|
| 70|  b|  4|     Y|
| 71|  d|  4|  null|
+---+---+---+------+

Die Lesbarkeit der obigen Logik mit der "else" -Anweisung ist wenig chaotisch, wenn die verschachtelten when-Anweisungen weiter gehen.

Gibt es eine bessere Möglichkeit, verschachtelte Fälle zu implementieren, wenn Anweisungen in Spark DataFrames verwendet werden? 

5
RaAm

Es gibt hier keine Verschachtelung, daher ist otherwise nicht erforderlich. Alles was Sie brauchen ist verkettet when:

import spark.implicits._

when($"tc" isin ("a", "b"), "Y")
  .when($"tc" === "a" && $"amt" >= 0, "N")

ELSE NULL ist implizit, so dass Sie ihn vollständig weglassen können.

Das Muster, das Sie verwenden, ist eher für folding über eine Datenstruktur anwendbar:

val cases = Seq(
  ($"tc" isin ("a", "b"), "Y"),
  ($"tc" === "a" && $"amt" >= 0, "N")
)

dabei folgt when - otherwise natürlich dem Rekursionsmuster und null liefert den Basisfall.

cases.foldLeft(lit(null)) {
  case (acc, (expr, value)) => when(expr, value).otherwise(acc)
}

Bitte beachten Sie, dass es mit dieser Kette von Bedingungen nicht möglich ist, das Ergebnis "N" zu erreichen. Wenn tc gleich "a" ist, wird sie von der ersten Klausel erfasst. Ist dies nicht der Fall, werden sowohl Prädikate als auch der Standardwert NULL nicht erfüllt. Sie sollten lieber:

when($"tc" === "a" && $"amt" >= 0, "N")
 .when($"tc" isin ("a", "b"), "Y")
12
user6910411

Für eine komplexere Logik ziehe ich zur besseren Lesbarkeit die Verwendung von UDFs vor.

val selectCase = udf((tc: String, amt: String) =>
  if (Seq("a", "b").contains(tc)) "Y"
  else if (tc == "a" && amt.toInt <= 0) "N"
  else null
)


dataset1.withColumn("REASON", selectCase(col("tc"), col("amt")))
  .show
2
Raphael Roth

sie können einfach selectExpr für Ihr Dataset verwenden

dataset1.selectExpr("*", "CASE WHEN tc in ('a') AND amt > 0 THEN 'N' WHEN tc in ('a','b') THEN 'Y' ELSE NULL END
REASON").show()

+---+---+---+------+
|KEY| tc|amt|REASON|
+---+---+---+------+
| 66|  a|  4|     N|
| 67|  a|  0|     Y|
| 70|  b|  4|     Y|
| 71|  d|  4|  null|
+---+---+---+------+

Die zweite Bedingung sollte vor der ersten gestellt werden, da die erste Bedingung allgemeiner ist.

WENN tc in ('a') UND amt> 0 DANN 'N'

0
PoojanKothari