Pandas dataframe a Spark dataframe, ¿manejando las conversiones de NaN a null real?

Quiero convertir el dataframe de pandas en chispa y estoy usando el método spark_context.createDataFrame() para crear el dataframe. También estoy especificando el esquema en el método createDataFrame() .

Lo que quiero saber es cómo manejar casos especiales. Por ejemplo, NaN en pandas cuando se convierte a un dataframe Spark termina siendo la cadena “NaN”. Estoy buscando formas de obtener nulos reales en lugar de “NaN”.

TL; DR Su mejor opción por ahora es omitir Pandas completamente.

La fuente del problema es que los pandas son menos expresivos que Spark SQL. Spark proporciona tanto NULL (en sentido SQL, como valor perdido) como NaN (numérico No es un número).

Las pandas de la otra mano no tienen un valor nativo que se puede usar para representar valores perdidos. Como resultado, utiliza marcadores de posición como NaN / NaT o Inf , que son indistinguibles a Spark de los NaNs e Infs y las reglas de conversión dependen del tipo de columna. La única excepción son las columnas de object (normalmente cadenas) que pueden contener valores None . Puede obtener más información sobre el manejo de los valores perdidos de Pandas en la documentación .

Por ejemplo, NaN en pandas cuando se convierte a un dataframe Spark termina siendo la cadena “NaN”.

Esto en realidad no es correcto. Dependiendo del tipo de columna de entrada. Si la columna muestra NaN lo más probable es que no sea un valor numérico, no una cadena simple:

 from pyspark.sql.functions import isnan, isnull pdf = pd.DataFrame({ "x": [1, None], "y": [None, "foo"], "z": [pd.Timestamp("20120101"), pd.Timestamp("NaT")] }) sdf = spark.createDataFrame(pdf) sdf.show() 
 +---+----+-------------------+ | x| y| z| +---+----+-------------------+ |1.0|null|2012-01-01 00:00:00| |NaN| foo| null| +---+----+-------------------+ 
 sdf.select([ f(c) for c in sdf.columns for f in [isnan, isnull] if (f, c) != (isnan, "z") # isnan cannot be applied to timestamp ]).show() 
 +--------+-----------+--------+-----------+-----------+ |isnan(x)|(x IS NULL)|isnan(y)|(y IS NULL)|(z IS NULL)| +--------+-----------+--------+-----------+-----------+ | false| false| false| true| false| | true| false| false| false| true| +--------+-----------+--------+-----------+-----------+ 

En la práctica, las colecciones locales paralelizadas (incluidos los objetos Pandas) tienen una importancia insignificante más allá de las pruebas simples y los ejemplos de juguetes para que siempre pueda convertir los datos manualmente (omitiendo posibles optimizaciones de flecha):

 import numpy as np spark.createDataFrame([ tuple( None if isinstance(x, (float, int)) and np.isnan(x) else x for x in record.tolist()) for record in pdf.to_records(index=False) ], pdf.columns.tolist()).show() 
 +----+----+-------------------+ | x| y| z| +----+----+-------------------+ | 1.0|null|1325376000000000000| |null| foo| null| +----+----+-------------------+ 

Si falta la ambigüedad de / not-a-number no es un problema, simplemente cargue los datos normalmente y sustitúyalos en Spark.

 from pyspark.sql.functions import col, when sdf.select([ when(~isnan(c), col(c)).alias(c) if t in ("double", "float") else c for c, t in sdf.dtypes ]).show() 
 +----+----+-------------------+ | x| y| z| +----+----+-------------------+ | 1.0|null|2012-01-01 00:00:00| |null| foo| null| +----+----+-------------------+