PySpark DataFrames-使用不同类型的列之间的比较进行过滤

时间:2019-01-31 10:07:35

标签: python apache-spark pyspark apache-spark-sql

假设您有一个数据框,其中包含各种类型的列(字符串,双精度型...)和一个特殊值“ miss”,该值代表字符串类型的列中的“ missing value”。

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']))可以完成这项工作,但我想知道这是否效率较低吗?

1 个答案:

答案 0 :(得分:2)

让我们从为什么开始。 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'CASTEDdouble,后来又转换成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')

和null安全相等:

df.filter(~df['intcol'].cast("string").eqNullSafe('miss'))

应该可以解决问题。

还请注意,NaN的值不是NULL,通过熊猫进行的转换是有损的-Pandas dataframe to Spark dataframe, handling NaN conversions to actual null?