Найти все нули с помощью SQL-запроса поверх кадра данных pyspark

У меня есть кадр данных StructField со смешанной схемой (DoubleType, StringType, LongType и т. д.).

Я хочу «перебрать» все столбцы, чтобы вернуть сводную статистику. Например:

set_min = df.select([
        fn.min(self.df[c]).alias(c) for c in self.df.columns
    ]).collect()

Это то, что я использую, чтобы найти минимальное значение в каждом столбце. Это прекрасно работает. Но когда я пытаюсь найти что-то подобное, чтобы найти Nulls:

set_null = df.filter(
       (lambda x: self.df[x]).isNull().count()
).collect()

Я получаю TypeError: condition should be string or Column, что имеет смысл, я передаю функцию.

или с пониманием списка:

set_null = self.df[c].alias(c).isNull() for c in self.df.columns

Затем я пытаюсь передать ему SQL-запрос в виде строки:

set_null = df.filter('SELECT fields FROM table WHERE column = NUL').collect()

Я получил:

ParseException: "\nmismatched input 'FROM' expecting <EOF>(line 1, pos 14)\n\n== SQL ==\nSELECT fields FROM table WHERE column = NULL\n--------------^^^\n"

Как я могу передать свою функцию как «строку или столбец», чтобы я мог использовать filter или where в качестве альтернативы, почему чистый оператор SQL не работает?


person too_many_questions    schedule 23.02.2017    source источник


Ответы (2)


В некоторых частях ваших попыток есть ошибки:

  • Вам не хватает квадратных скобок в вашем примере понимания списка
  • Вы пропустили L в NUL
  • Ваш чистый SQL не работает, потому что filter/where ожидает предложение where, а не полный оператор SQL; это просто псевдонимы, и я предпочитаю использовать where, чтобы было понятнее, вам просто нужно указать такой пункт

В конце концов, вам не нужно использовать where, как показывает Карлсон. Но вычитание из общего количества означает, что вам нужно дважды оценить фрейм данных (что можно облегчить с помощью кэширования, но все же не идеально). Есть более прямой способ:

>>> df.select([fn.sum(fn.isnull(c).cast('int')).alias(c) for c in df.columns]).show()
+---+---+
|  A|  B|
+---+---+
|  2|  3|
+---+---+

Это работает, потому что приведение логического значения к целому числу дает 1 вместо True и 0 вместо False. Если вы предпочитаете SQL, эквивалент:

df.select([fn.expr('SUM(CAST(({c} IS NULL) AS INT)) AS {c}'.format(c=c)) for c in df.columns]).show()

или лучше, без приведения:

df.select([fn.expr('SUM(IF({c} IS NULL, 1, 0)) AS {c}'.format(c=c)) for c in df.columns]).show()
person sgvd    schedule 23.02.2017
comment
спасибо за вашу помощь, ваши решения и решения Карлсона действительно помогли пролить свет на ошибки, которые я совершал в отношении моего подхода - person too_many_questions; 24.02.2017

Если вы хотите подсчитать NULL значений в столбце, вы можете подсчитать ненулевые значения и вычесть их из общего количества.

Например:

from pyspark.sql import SparkSession
from pyspark.sql import functions as fn

spark = SparkSession.builder.master("local").getOrCreate()


df = spark.createDataFrame(
    data=[
        (1, None),
        (1, 1),
        (None, None),
        (1, 1),
        (None, 1),
        (1, None),
    ],
    schema=("A", "B")
)

total = df.count()
missing_counts = df.select(
    *[(total - fn.count(col)).alias("missing(%s)" % col) for col in df.columns]
)

missing_counts.show()
>>> +----------+----------+
... |missing(A)|missing(B)|
... +----------+----------+
... |         2|         3|
... +----------+----------+
person karlson    schedule 23.02.2017
comment
Спасибо за предоставленный отзыв; использование суммы, а затем вычитание из нее может быть весьма полезным для некоторых других моих методов проверки. * имеет значение в этом параметре? - person too_many_questions; 24.02.2017
comment
Нет, это не должно иметь значения. Однако у @sgvd есть хорошее замечание по поводу того, что это приводит к двойной оценке. - person karlson; 24.02.2017