J'ai un Spark 1.5.0 DataFrame avec un mélange de null
et de chaînes vides dans la même colonne. Je veux convertir toutes les chaînes vides de toutes les colonnes en null
(None
, en Python). Le DataFrame peut avoir des centaines de colonnes, donc j'essaie d'éviter les manipulations codées en dur de chaque colonne.
Voir ma tentative ci-dessous, ce qui entraîne une erreur.
from pyspark.sql import SQLContext
sqlContext = SQLContext(sc)
## Create a test DataFrame
testDF = sqlContext.createDataFrame([Row(col1='foo', col2=1), Row(col1='', col2=2), Row(col1=None, col2='')])
testDF.show()
## +----+----+
## |col1|col2|
## +----+----+
## | foo| 1|
## | | 2|
## |null|null|
## +----+----+
## Try to replace an empty string with None/null
testDF.replace('', None).show()
## ValueError: value should be a float, int, long, string, list, or Tuple
## A string value of null (obviously) doesn't work...
testDF.replace('', 'null').na.drop(subset='col1').show()
## +----+----+
## |col1|col2|
## +----+----+
## | foo| 1|
## |null| 2|
## +----+----+
C'est aussi simple que cela:
from pyspark.sql.functions import col, when
def blank_as_null(x):
return when(col(x) != "", col(x)).otherwise(None)
dfWithEmptyReplaced = testDF.withColumn("col1", blank_as_null("col1"))
dfWithEmptyReplaced.show()
## +----+----+
## |col1|col2|
## +----+----+
## | foo| 1|
## |null| 2|
## |null|null|
## +----+----+
dfWithEmptyReplaced.na.drop().show()
## +----+----+
## |col1|col2|
## +----+----+
## | foo| 1|
## +----+----+
Si vous souhaitez remplir plusieurs colonnes, vous pouvez par exemple réduire:
to_convert = set([...]) # Some set of columns
reduce(lambda df, x: df.withColumn(x, blank_as_null(x)), to_convert, testDF)
ou utilisez la compréhension:
exprs = [
blank_as_null(x).alias(x) if x in to_convert else x for x in testDF.columns]
testDF.select(*exprs)
Si vous souhaitez opérer spécifiquement sur les champs de chaîne, veuillez vérifier la réponse par robin-loxley .
Ma solution est bien meilleure que toutes les solutions que j'ai vues jusqu'à présent, qui peuvent traiter autant de champs que vous le souhaitez, voyez la petite fonction comme suit:
// Replace empty Strings with null values
private def setEmptyToNull(df: DataFrame): DataFrame = {
val exprs = df.schema.map { f =>
f.dataType match {
case StringType => when(length(col(f.name)) === 0, lit(null: String).cast(StringType)).otherwise(col(f.name)).as(f.name)
case _ => col(f.name)
}
}
df.select(exprs: _*)
}
Vous pouvez facilement réécrire la fonction ci-dessus en Python.
J'ai appris cette astuce de @ liancheng
Ajoutez simplement en plus des réponses de zero323 et de soulmachine. Pour convertir pour tous les champs StringType.
from pyspark.sql.types import StringType
string_fields = []
for i, f in enumerate(test_df.schema.fields):
if isinstance(f.dataType, StringType):
string_fields.append(f.name)
Les FDU ne sont pas terriblement efficaces. La façon correcte de le faire à l'aide d'une méthode intégrée est la suivante:
df = df.withColumn('myCol', when(col('myCol') == '', None).otherwise(col('myCol')))