python - trying - pandas replace null values in column
Reemplace cadenas vacĂas con ninguno/valores nulos en DataFrame (5)
Tengo un Spark 1.5.0 DataFrame con una mezcla de cadenas null
y vacías en la misma columna. Quiero convertir todas las cadenas vacías en todas las columnas a null
( None
, en Python). El DataFrame puede tener cientos de columnas, así que estoy tratando de evitar manipulaciones de cada columna.
Ver mi intento a continuación, lo que resulta en un error.
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|
## +----+----+
Es tan simple como esto:
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 desea rellenar varias columnas, por ejemplo, puede reducir:
to_convert = set([...]) # Some set of columns
reduce(lambda df, x: df.withColumn(x, blank_as_null(x)), to_convert, testDF)
o utilizar la comprensión:
exprs = [
blank_as_null(x).alias(x) if x in to_convert else x for x in testDF.columns]
testDF.select(*exprs)
Si desea operar específicamente en campos de cadenas, verifique la respuesta con robin-loxley .
Esta es una versión diferente de la solución de soulmachine, pero no creo que puedas traducir esto a Python tan fácilmente:
def emptyStringsToNone(df: DataFrame): DataFrame = {
df.schema.foldLeft(df)(
(current, field) =>
field.dataType match {
case DataTypes.StringType =>
current.withColumn(
field.name,
when(length(col(field.name)) === 0, lit(null: String)).otherwise(col(field.name))
)
case _ => current
}
)
}
Los UDF no son terriblemente eficientes. La forma correcta de hacerlo utilizando un método incorporado es:
df = df.withColumn(''myCol'', when(col(''myCol'') == '''', None).otherwise(col(''myCol'')))
Mi solución es mucho mejor que todas las soluciones que he visto hasta ahora, que pueden ocuparse de tantos campos como desee, vea la pequeña función como la siguiente:
// 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: _*)
}
Puede reescribir fácilmente la función anterior en Python.
Aprendí este truco de @liancheng
Simplemente agregue encima de las respuestas de zero323 y soulmachine. Para convertir para todos los campos 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)