如何合并pyspark中的行?

如何合并pyspark中的行?,pyspark,Pyspark,在PySpark中,有一个coalesce(colA,colB,…)的概念,它每行从这些列中获取它遇到的第一个非空值。但是,我希望合并(rowA,rowB,…)即,每列能够从这些行中获取它遇到的第一个非空值。我想合并一组行或行窗口中的所有行 例如,给定以下数据集,我希望按类别合并行,并按日期升序排列 我应该得到的输出是 +---------+-----------+------+------+ | category| date| val1| val2| +---------+--

在PySpark中,有一个
coalesce(colA,colB,…)
的概念,它每行从这些列中获取它遇到的第一个非空值。但是,我希望
合并(rowA,rowB,…)
即,每列能够从这些行中获取它遇到的第一个非空值。我想合并一组行或行窗口中的所有行

例如,给定以下数据集,我希望按类别合并行,并按日期升序排列

我应该得到的输出是

+---------+-----------+------+------+
| category|       date|  val1|  val2|
+---------+-----------+------+------+
|        A| 2020-05-01|     2|     1|
|        B| 2020-05-01|     4|  null|
|        C| 2020-05-01|     5|     2|
|        D| 2020-05-01|  null|     4|
+---------+-----------+------+------+

首先,我会给出答案。然后,我将指出重要的部分

from pyspark.sql import Window
from pyspark.sql.functions import col, dense_rank, first

df = ...  # dataframe from question description

window = (
    Window
    .partitionBy("category")
    .orderBy(col("date").asc())
)
window_unbounded = (
    window
    .rangeBetween(Window.unboundedPreceding, Window.unboundedFollowing)
)

cols_to_merge = [col for col in df.columns if col not in ["category", "date"]]
merged_cols = [first(col, True).over(window_unbounded).alias(col) for col in cols_to_merge]
df_merged = (
    df
    .select([col("category"), col("date")] + merged_cols)
    .withColumn("rank_col", dense_rank().over(window))
    .filter(col("rank_col") == 1)
    .drop("rank_col")
)
合并的行方式类似于聚合函数。具体来说,我们将first与
ignorenulls=True一起使用,以便找到第一个非null值

当我们使用first时,我们必须注意它应用到的行的顺序。因为,我们用的是一个

窗口本身在两端必须是无界的,而不是默认值,否则我们将在组的子集上运行
first
聚合

在对窗口进行聚合后,我们将列重新命名为其原始名称,以保持列名的一致性

我们使用cols的单个select语句,而不是带有
df.withColumn(col,…)
的for循环,因为select语句大大降低了查询计划的深度。如果使用looped withColumn,如果列太多,可能会出现堆栈溢出错误

最后,我们在窗口上运行一个
densite_-rank
——这次使用默认范围的窗口——并只筛选排名第一的行。我们在这里使用密集排名,但我们可以使用任何排名函数,任何适合我们需要的函数

from pyspark.sql import Window
from pyspark.sql.functions import col, dense_rank, first

df = ...  # dataframe from question description

window = (
    Window
    .partitionBy("category")
    .orderBy(col("date").asc())
)
window_unbounded = (
    window
    .rangeBetween(Window.unboundedPreceding, Window.unboundedFollowing)
)

cols_to_merge = [col for col in df.columns if col not in ["category", "date"]]
merged_cols = [first(col, True).over(window_unbounded).alias(col) for col in cols_to_merge]
df_merged = (
    df
    .select([col("category"), col("date")] + merged_cols)
    .withColumn("rank_col", dense_rank().over(window))
    .filter(col("rank_col") == 1)
    .drop("rank_col")
)