#scala #dataframe #apache-spark #apache-spark-sql
#scala #фрейм данных #apache-spark #apache-spark-sql
Вопрос:
У меня есть следующий фрейм данных
val input = Seq(("ZZ","a","a","b","b"),
("ZZ","a","b","c","d"),
("YY","b","e",null,"f"),
("YY","b","b",null,"f"),
("VV","a",null,"",""))
.toDF("main","value1","value2","value3","value4")
input.show()
---- ------ ------ ------ ------
|main|value1|value2|value3|value4|
---- ------ ------ ------ ------
| ZZ| a| a| b| b|
| ZZ| a| b| c| d|
| YY| b| e| null| f|
| YY| b| b| null| f|
| VV| a| null| | |
---- ------ ------ ------ ------
Я сделал следующее, чтобы выровнять данные
val newdf = input.select('main,array('value1,'value2,'value3,'value4).alias("values"))
.groupBy('main).agg(collect_set('values).alias("values"))
.select('main, flatten($"values").alias("values"))
newdf.show()
---- --------------------
|main| values|
---- --------------------
| ZZ|[a, a, b, b, a, b...|
| YY|[b, e,, f, b, b,, f]|
| VV| [a,, , ]|
---- --------------------
Теперь мне нужно выбрать каждую уникальную комбинацию элементов в виде пары из массива и иметь их в виде отдельных строк данных. Таким образом, для фрейма данных, приведенного выше, результат будет
---- ------ ------
|main|value1|value2|
---- ------ ------
| ZZ| a| b|
| ZZ| a| c|
| ZZ| a| d|
| ZZ| b| c|
| ZZ| d| c|
| YY| b| f|
| YY| b| e|
| YY| e| f|
| VV| a| |
---- ------ ------
Как мне свести к столбцу уникальные элементы, которые я могу разделить как отдельные строки?
Ответ №1:
Используйте explode
два раза и фильтруйте.
val newdf = input.select('main,array('value1,'value2,'value3,'value4).alias("values"))
.groupBy('main).agg(flatten(collect_set('values)).alias("values"))
.withColumn("value1", explode(array_distinct('values)))
.withColumn("value2", explode(array_distinct('values)))
.filter("value1 < value2")
.select('main, 'value1, 'value2)
newdf.show()
---- ------ ------
|main|value1|value2|
---- ------ ------
| ZZ| a| b|
| ZZ| a| c|
| ZZ| a| d|
| ZZ| b| c|
| ZZ| b| d|
| ZZ| c| d|
| YY| b| e|
| YY| b| f|
| YY| e| f|
| VV| | a|
---- ------ ------
Комментарии:
1. Эта логика работает отлично, но когда для одной строки значения являются
("XX", "a", null, null)
, фильтр отфильтровывает всю строку, и мы теряем значение «a». Я обошел это, заменив null пустой строкой.
Ответ №2:
I need to pick every unique combination of items as a pair
например, если ZZ имеет значения [a, b, c, d], то, по сути, вы создадите 6 пар (4 выбирают 2)? В таком случае вы можете захотеть создать UDAF (определяемую пользователем агрегатную функцию).
input
.select('main,array('value1,'value2,'value3,'value4).alias("values"))
.groupBy('main).agg(<here comes your UDAF>)
Этот UDAF должен быть таким, чтобы он собирал значения в виде набора (или списка .distinct
) и создавал все пары комбинаций (можно выполнить с помощью 2 циклов for).
После этого ваш фрейм данных должен выглядеть следующим образом
---- ------------------------------------
|main| values |
---- ------------------------------------
| ZZ|[(a,b),(a,c),(a,d),(b,c),(b,d),(c,d)|
---- ------------------------------------
Затем вы можете .explode()
получить фрейм данных, подобный
---- -------
|main|values |
---- -------
| ZZ|(a,b) |
---- -------
| ZZ|(a,c) |
---- -------
| ZZ|(a,d) |
---- -------
| ZZ|(b,c) |
---- -------
| ZZ|(b,d) |
---- -------
| ZZ|(c,d) |
---- -------
Затем вы можете сделать столбец value1
первым значением этого кортежа, а столбец value2
вторым значением.