В ходе изучения разворота в Spark Sql я нашел простой пример с подсчетом, что
привело к строкам с нулями.
Из оболочки искры, если вы сделаете это ->
val visits = Seq(
(0, "Warsaw", 2015),
(1, "Warsaw", 2016),
(2, "Boston", 2017)
).toDF("id", "city", "year")
val withNulls = visits.groupBy("city").pivot("year", Seq("2015", "2016", "2017")).count()
withNulls.show()
вы получите этот вывод:
+------+----+----+----+
| city|2015|2016|2017|
+------+----+----+----+
|Warsaw| 1| 1|null|
|Boston|null|null| 1|
+------+----+----+----+
Я могу преобразовать нули в нули с помощью еще одного шага с помощью функции 'na', например:
val noNulls = withNulls .na.fill(0)
noNulls.show()
И результат - то, что я хочу:
+------+----+----+----+
| city|2015|2016|2017|
+------+----+----+----+
|Warsaw| 1| 1| 0|
|Boston| 0| 0| 1|
+------+----+----+----+
Я бы предположил, что было бы более эффективно, если бы я мог сделать это за один шаг. Но я не смог
придумать хорошее одношаговое решение. Ниже приведена одна неудачная попытка, которая оставила нулевые значения на месте и изменила мои (действительные) значения 1 на нули (что вовсе не правильно!).
visits.groupBy("city").pivot("year", Seq("2015", "2016", "2017")).agg(expr("coalesce(count(),0)")).show()
Может быть, один из экспертов Spark Sql мог бы подсказать мне, как сделать это правильно? Я почти уверен, что coalesce будет полезен, но любое одношаговое решение, даже без coalesce , будет приветствоваться!
Обновление:
Я собираюсь принять ответ Чандана (спасибо, Чандан!) И заключу, что самый ясный и эффективный способ сделать это с помощью na.fill (0)
Глядя вглубь принятого решения, мне кажется, что было бы дополнительное
задание запущено, чтобы получить список столбцов (в форме одного оператора case на столбец), который используется в select
это решение.
Я проверил это через пользовательский интерфейс spark. Когда я выполнил это заявление:
scala> val cols = visits.groupBy("city").pivot("year").count.columns.map(i => when(col(i).isNull,0).otherwise(col(i)).alias(i))
Я видел статистику для дополнительной работы в пользовательском интерфейсе .... это имеет смысл, так как в решении Чандана есть два «счета»,
каждое из которых является действием, которое может привести к запуску работы. Еще одна интересная вещь, которую я отметил о принятых
Решение заключается в том, что значение cols представляет собой список операторов case, по одному на каждый столбец, и именно эти операторы case
фактически применяет логику преобразования «if null-> then zero».
cols: Array[org.apache.spark.sql.Column] = Array(CASE WHEN (city IS NULL) THEN 0 ELSE city END AS `city`, CASE WHEN (2015 IS NULL) THEN 0 ELSE 2015 END AS `2015`, CASE WHEN (2016 IS NULL) THEN 0 ELSE 2016 END AS `2016`, CASE WHEN (2017 IS NULL) THEN 0 ELSE 2017 END AS `2017