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?
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")
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
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'