Spark DataFrame转Pandas时None变回NaN(仅Python3.10存在)
问题描述
在Python 3.10环境下,把Spark DataFrame里的NaN替换成None后,转成Pandas DataFrame时,None又变回了NaN;但同样的操作在Python 3.6里不会出现这个问题。
复现代码及现象
环境信息
import sys print(sys.executable) print(sys.version) print(sys.version_info)
输出:
3.10.11 (main, Apr 20 2023, 19:02:41) [GCC 11.2.0] sys.version_info(major=3, minor=10, micro=11, releaselevel='final', serial=0)
创建Spark DataFrame
another_df = spark.createDataFrame([(float('nan'), 0.0),(0.0,float('nan'))]) another_df.show()
输出:
+---+---+ | _1| _2| +---+---+ |NaN|0.0| |0.0|NaN| +---+---+
替换NaN为None
another_df = another_df.replace(float('nan'), None, subset=['_2']) another_df.show()
输出:
+---+----+ | _1| _2| +---+----+ |NaN| 0.0| |0.0|null| +---+----+
转换为Pandas DataFrame
another_df.toPandas()
输出:
_1 _2 0 NaN 0.0 1 0.0 NaN
原因分析
核心原因很直接:
Spark里的None对应SQL的NULL,但如果列是浮点型(DoubleType/FloatType),Pandas在Python 3.10及更高版本中,会把Spark浮点列的NULL自动转成NaN——因为Pandas的数值类型列(比如float64)没法存储None,只能用NaN来表示空值。而Python 3.6环境用的PySpark或Pandas版本较低,当时的类型转换逻辑不同,所以None能被保留下来。
解决方案
方案1:先转字符串类型再转Pandas
把目标浮点列先转成字符串类型,替换NaN为None后再转Pandas,这样NULL会保留成None:
from pyspark.sql.types import StringType # 将_2列转为字符串类型,再替换NaN为None another_df = another_df.withColumn("_2", another_df["_2"].cast(StringType())) \ .replace(float('nan'), None, subset=['_2']) # 转成Pandas DataFrame pd_df = another_df.toPandas() print(pd_df)
输出:
_1 _2 0 NaN 0.0 1 0.0 None
如果后续需要数值类型,再把列转回浮点型即可:
pd_df['_2'] = pd.to_numeric(pd_df['_2'], errors='coerce')
方案2:转Pandas后替换(转为object类型)
先转成Pandas DataFrame,再把目标列转为object类型,然后把NaN替换成None:
pd_df = another_df.toPandas() # 转成object类型后替换NaN为None pd_df['_2'] = pd_df['_2'].astype(object).where(pd_df['_2'].notna(), None) print(pd_df)
输出:
_1 _2 0 NaN 0.0 1 0.0 None
注意:转为object类型后,列的数值运算性能会下降,适合小数据集场景。
方案3:用Spark UDF处理空值
自定义UDF把浮点列的NaN转为None,逻辑更灵活:
from pyspark.sql.functions import udf def replace_nan_with_none(val): return None if val != val else val # 通过val != val判断是否为NaN replace_udf = udf(replace_nan_with_none) another_df = another_df.withColumn("_2", replace_udf("_2")) pd_df = another_df.toPandas() print(pd_df)
这个方案和直接replace效果类似,但能应对更复杂的空值判断逻辑,最终转Pandas时同样需要配合类型转换来保留None。
内容的提问来源于stack exchange,提问作者hsekol
相关产品推荐
相关产品推荐

