PySpark DataFrames - фильтрация с использованием сравнений между столбцами разных типов - PullRequest
0 голосов
/ 31 января 2019

Предположим, у вас есть фрейм данных со столбцами различных типов (string, 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 != '' Истина, но не ноль в "нормальном" Python)

Мой вопрос на самом деле состоит из нескольких вопросов:

  • почему сравнение перекрестных типов приводит к нулям?
  • каков наилучший способ проверки на равенство / неравенство между различными типами"ожидаемым образом"?Или (в моем случае) мне нужно включить отдельную логику, которая переключается в зависимости от типа столбца?
  • Кажется, что df.filter(~df['intcol'].isin(['miss'])) справляется, но мне интересно, если это менее эффективно?

1 Ответ

0 голосов
/ 31 января 2019

Давайте начнем с почему.DataFrame API - это DSL для SQL и применяются правила оценки 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 также не определено - Разница между=== null и isNull в Spark DataDrame - filter дает пустой результат.

Теперь, как решить эту проблему.И явное приведение:

df.filter(df['intcol'].cast("string") != 'miss')

и нулевое безопасное равенство:

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

должны помочь.

Также обратите внимание, что значения NaN не NULL и преобразование через Pandas с потерями - Фрейм данных Pandas в фрейм данных Spark, обрабатывающий преобразования NaN в фактический ноль?

...