假设您有一个数据框,其中包含各种类型的列(字符串、 double ......)和一个特殊值“miss”,表示字符串类型列中的“缺失值”。
from pyspark.sql import SparkSession
import pandas as pd
spark = SparkSession.builder.getOrCreate()
pdf = pd.DataFrame([
[1, 'miss'],
[2, 'x'],
[None, 'y']
], columns=['intcol', 'strcol'])
df = spark.createDataFrame(data=pdf)
我正在尝试使用如下过滤来计算每列的非缺失值的数量:
col = df['strcol']
df.filter(col.isNotNull() & (col != 'miss')).show()
适用于字符串列:
+------+------+
|intcol|strcol|
+------+------+
| 2.0| x|
| NaN| y|
+------+------+
但是,对于数字列,它会过滤掉所有行:
col = df['intcol']
df.filter(col.isNotNull() & (col != 'miss')).show()
+------+------+
|intcol|strcol|
+------+------+
+------+------+
这似乎是因为数字列与字符串值的跨类型比较会产生全空值:
df.select(df['intcol'] != 'miss').show()
+---------------------+
|(NOT (intcol = miss))|
+---------------------+
| null|
| null|
| null|
+---------------------+
我发现这有点出乎意料(例如 1 != ''
是 True,在“正常”Python 中不是 null)
我的问题实际上是几个问题:
- 为什么交叉类型比较会导致空值?
- 以“预期方式”测试不同类型之间的相等/不相等的最佳方法是什么?或者(就我而言)我是否需要包含根据列类型进行切换的单独逻辑?
- 看起来
df.filter(~df['intcol'].isin(['miss']))
可以完成这项工作,但我想知道这是否效率较低?
最佳答案
让我们从原因开始。 DataFrame
API 是用于 SQL 的 DSL,并且适用 SQL 评估规则。每当您对不同类型的对象应用运算符时,都会根据预定义的规则对优先级较低的操作数应用 CAST
操作。一般数字类型,具有较高的优先级,因此(遵循执行计划df.select(df['intcol'] != 'miss').explain(True)
):
== Parsed Logical Plan ==
'Project [NOT (intcol#0 = miss) AS (NOT (intcol = miss))#12]
+- LogicalRDD [intcol#0, strcol#1], false
被重写为
== Analyzed Logical Plan ==
(NOT (intcol = miss)): boolean
Project [NOT (intcol#0 = cast(miss as double)) AS (NOT (intcol = miss))#12]
+- LogicalRDD [intcol#0, strcol#1], false
其中'miss'
是CASTED
到double
,然后转换为NULL
== Optimized Logical Plan ==
Project [null AS (NOT (intcol = miss))#22]
+- LogicalRDD [intcol#0, strcol#1], false
使用此操作数进行强制转换未定义。
由于与 NULL
的相等性也未定义 - Difference between === null and isNull in Spark DataDrame - filter
产生空结果。
现在如何解决这个问题。两者都是显式转换:
df.filter(df['intcol'].cast("string") != 'miss')
和空安全平等:
df.filter(~df['intcol'].cast("string").eqNullSafe('miss'))
应该可以解决问题。
另请注意,NaN
值不是 NULL
,并且通过 Pandas 进行转换是有损的 - Pandas dataframe to Spark dataframe, handling NaN conversions to actual null?
关于python - PySpark DataFrames - 使用不同类型的列之间的比较进行过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54458070/