web-dev-qa-db-fra.com

Convertir des valeurs nulles en tableau vide dans Spark DataFrame

J'ai un Spark trame de données où une colonne est un tableau d'entiers. La colonne est nullable car elle provient d'une jointure externe gauche. Je veux convertir toutes les valeurs nulles en un tableau vide afin Je n'ai pas à m'occuper des null plus tard.

Je pensais que je pouvais le faire comme ça:

val myCol = df("myCol")
df.withColumn( "myCol", when(myCol.isNull, Array[Int]()).otherwise(myCol) )

Cependant, cela entraîne l'exception suivante:

Java.lang.RuntimeException: Unsupported literal type class [I [I@5ed25612
at org.Apache.spark.sql.catalyst.expressions.Literal$.apply(literals.scala:49)
at org.Apache.spark.sql.functions$.lit(functions.scala:89)
at org.Apache.spark.sql.functions$.when(functions.scala:778)

Apparemment, les types de tableaux ne sont pas pris en charge par la fonction when. Existe-t-il un autre moyen simple de convertir les valeurs nulles?

Dans le cas où cela est pertinent, voici le schéma de cette colonne:

|-- myCol: array (nullable = true)
|    |-- element: integer (containsNull = false)
16
Daniel Siegmann

Vous pouvez utiliser un UDF:

import org.Apache.spark.sql.functions.udf

val array_ = udf(() => Array.empty[Int])

combiné avec WHEN ou COALESCE:

df.withColumn("myCol", when(myCol.isNull, array_()).otherwise(myCol))
df.withColumn("myCol", coalesce(myCol, array_())).show

Dans la versions récentes vous pouvez utiliser la fonction array:

import org.Apache.spark.sql.functions.{array, lit}

df.withColumn("foo", array().cast("array<integer>"))

Veuillez noter que cela ne fonctionnera que si la conversion de string vers le type souhaité est autorisée.

17
zero323

Avec une légère modification de l'approche de zero323, j'ai pu le faire sans utiliser un udf dans Spark 2.3.1.

val df = Seq("a" -> Array(1,2,3), "b" -> null, "c" -> Array(7,8,9)).toDF("id","numbers")
df.show
+---+---------+
| id|  numbers|
+---+---------+
|  a|[1, 2, 3]|
|  b|     null|
|  c|[7, 8, 9]|
+---+---------+

val df2 = df.withColumn("numbers", coalesce($"numbers", array()))
df2.show
+---+---------+
| id|  numbers|
+---+---------+
|  a|[1, 2, 3]|
|  b|       []|
|  c|[7, 8, 9]|
+---+---------+
5
Jeremy

Une alternative sans UDF à utiliser lorsque le type de données dans lequel vous voulez que vos éléments de tableau ne puissent pas être convertis à partir de StringType est la suivante:

import pyspark.sql.types as T
import pyspark.sql.functions as F

df.withColumn(
    "myCol",
    F.coalesce(
        F.col("myCol"),
        F.from_json(F.lit("[]"), T.ArrayType(T.IntegerType()))
    )
)

Vous pouvez remplacer IntegerType() par n'importe quel type de données, également complexes.

2
harppu