Вы можете достичь этого, используя pyspark.sql.Window
, который упорядочивает по clientDateTime
, pyspark.sql.functions.concat_ws
и pyspark.sql.functions.collect_list
:
import pyspark.sql.functions as f
from pyspark.sql import Window
w = Window.orderBy("DateTime") # define Window for ordering
df.drop("Seq", "sessionCount", "row_number").select(
"*",
f.concat_ws(
"",
f.collect_list(f.col("name")).over(w)
).alias("effective_name")
).show(truncate=False)
#+---------------+--------------+-------------------------+
#|name | DateTime|effective_name |
#+---------------+--------------+-------------------------+
#|abc |1521572913344 |abc |
#|xyz |1521572916109 |abcxyz |
#|rafa |1521572916118 |abcxyzrafa |
#|{} |1521572916129 |abcxyzrafa{} |
#|experience |1521572917816 |abcxyzrafa{}experience |
#+---------------+--------------+-------------------------+
Я опустил "Seq"
, "sessionCount"
, "row_number"
, чтобы сделать вывод на экран более дружественным.
Если вам нужно сделать это для каждой группы, вы можете добавить partitionBy
к Window
. Скажем, в этом случае вы хотите сгруппировать по sessionSeq
, вы можете сделать следующее:
w = Window.partitionBy("Seq").orderBy("DateTime")
df.drop("sessionCount", "row_number").select(
"*",
f.concat_ws(
"",
f.collect_list(f.col("name")).over(w)
).alias("effective_name")
).show(truncate=False)
#+---------------+--------------+----------+-------------------------+
#|name | DateTime|sessionSeq|effective_name |
#+---------------+--------------+----------+-------------------------+
#|abc |1521572913344 |17 |abc |
#|xyz |1521572916109 |17 |abcxyz |
#|rafa |1521572916118 |17 |abcxyzrafa |
#|{} |1521572916129 |17 |abcxyzrafa{} |
#|experience |1521572917816 |17 |abcxyzrafa{}experience |
#+---------------+--------------+----------+-------------------------+
Если вы предпочитаете использовать withColumn
, вышеуказанное эквивалентно:
df.drop("sessionCount", "row_number").withColumn(
"effective_name",
f.concat_ws(
"",
f.collect_list(f.col("name")).over(w)
)
).show(truncate=False)
Объяснение
Вы хотите применить функцию к нескольким строкам, которая называется агрегацией. При любой агрегации вам нужно определить, какие строки агрегировать и в каком порядке. Мы делаем это, используя Window
. В этом случае w = Window.partitionBy("Seq").orderBy("DateTime")
разделит данные по Seq
и отсортирует по DateTime
.
Сначала мы применяем агрегатную функцию collect_list("name")
к окну. Это собирает все значения из столбца name
и помещает их в список. Порядок вставки определяется порядком окна.
Например, промежуточный результат этого шага будет:
df.select(
f.collect_list("name").over(w).alias("collected")
).show()
#+--------------------------------+
#|collected |
#+--------------------------------+
#|[abc] |
#|[abc, xyz] |
#|[abc, xyz, rafa] |
#|[abc, xyz, rafa, {}] |
#|[abc, xyz, rafa, {}, experience]|
#+--------------------------------+
Теперь, когда соответствующие значения находятся в списке, мы можем объединить их вместе с пустой строкой в качестве разделителя.
df.select(
f.concat_ws(
"",
f.collect_list("name").over(w)
).alias("concatenated")
).show()
#+-----------------------+
#|concatenated |
#+-----------------------+
#|abc |
#|abcxyz |
#|abcxyzrafa |
#|abcxyzrafa{} |
#|abcxyzrafa{}experience |
#+-----------------------+