熊猫数据框到Spark数据框,是否将NaN转换为实际的null?


问题内容

我想将数据框从熊猫转换为Spark,并且正在使用spark_context.createDataFrame()创建数据框的方法。我还在方法中指定了架构createDataFrame()

我想知道的是如何处理特殊情况。例如,当转换为Spark数据帧时,熊猫中的 NaN 最终为字符串“
NaN”。我正在寻找如何获取实际的空值而不是“ NaN”的方法。


问题答案:

TL; DR 现在最好的选择是完全跳过熊猫。

问题的根源是熊猫不如Spark SQL富有表现力。Spark提供NULL(从SQL的意义上说是缺失值)和NaN(数字不是数字)。

另一只手的熊猫没有可用于表示缺失值的本机值。因此,它使用占位符(例如NaN/NaT或)Inf,这些占位符与Spark的实际区别不大NaNsInfs并且转换规则取决于列类型。唯一的例外是object可以包含None值的列(通常是字符串)。您可以从文档中了解有关处理缺失值的更多信息。

例如,当转换为Spark数据帧时,熊猫中的NaN最终为字符串“ NaN”。

这实际上是不正确的。取决于输入列的类型。如果column显示NaN它很可能不是数字值,则不是纯字符串:

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|
+--------+-----------+--------+-----------+-----------+

实际上,并行化的本地集合(包括Pandas对象)在简单的测试和玩具示例之外的重要性可以忽略不计,因此您始终可以手动转换数据(跳过可能的Arrow优化):

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|
+----+----+-------------------+

如果缺少/不是数字的歧义不是问题,那么只需照常加载数据并替换为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|
+----+----+-------------------+