Pyspark-是否为每个键添加缺少的值?

Pyspark-是否为每个键添加缺少的值?,pyspark,Pyspark,我有一个Pyspark数据框,其中包含一些非唯一的键键,以及一些列数字和值 对于大多数键,数字列从1到12,但对于其中一些键,数字(例如,我们有数字[1,2,5,9])。我想添加缺少的行,这样对于每个键,我们就可以用最后看到的值填充1-12范围内的所有数字 所以这张桌子 key number value a 1 6 a 2 10 a 5 20 a 9 25 我想去 key

我有一个Pyspark数据框,其中包含一些非唯一的键
,以及一些列
数字

对于大多数
数字
列从1到12,但对于其中一些键,
数字
(例如,我们有数字
[1,2,5,9]
)。我想添加缺少的行,这样对于每个
,我们就可以用最后看到的值填充1-12范围内的所有
数字

所以这张桌子

key    number    value
a      1         6
a      2         10
a      5         20
a      9         25
我想去

key    number    value
a      1         6
a      2         10
a      3         10
a      4         10
a      5         20
a      6         20
a      7         20
a      8         20
a      9         25
a      10        25
a      11        25
a      12        25

我考虑创建一个包含
a
的表和一个1-12的数组,分解该数组并与原始表连接,然后使用一个以当前行为边界的窗口函数分别用前一个值填充
列。但是,这似乎有点不雅观,我想知道是否有更好的方法来实现我的目标?

您可以在不加入的情况下完成此操作。我已经用不同的间隙对此进行了多个测试,只要数字1始终作为输入提供,它将始终有效(因为您需要从那里开始序列),并且它将始终在到12之间。我使用了一个耦合窗口s来获得一个列,我可以在序列中使用该列,然后使用表达式制作一个自定义序列,然后将其分解以获得所需的结果。如果出于某种原因,您的输入中没有数字1,请告诉我我将更新我的解决方案

from pyspark.sql.window import Window
from pyspark.sql import functions as F
from pyspark.sql.functions import when
w=Window().partitionBy("key").orderBy("number")
w2=Window().partitionBy("key").orderBy("number").rowsBetween(Window.unboundedPreceding,Window.unboundedFollowing)
df.withColumn("number2", F.lag("number").over(w)).withColumn("diff", F.when((F.col("number2").isNotNull()) & ((F.col("number")-F.col("number2")) > 1), (F.col("number")-F.col("number2"))).otherwise(F.lit(0)))\
.withColumn("diff2", F.lead("diff").over(w)).withColumn("diff2", F.when(F.col("diff2").isNull(), F.lit(0)).otherwise(F.col("diff2"))).withColumn("diff2", F.when(F.col("diff2")!=0, F.col("diff2")-1).otherwise(F.col("diff2"))).withColumn("max", F.max("number").over(w2))\
.withColumn("diff2", F.when((F.col("number")==F.col("max")) & (F.col("number")<F.lit(12)), F.lit(12)-F.col("number")).otherwise(F.col("diff2")))\
.withColumn("number2", F.when(F.col("diff2")!=0,F.expr("""sequence(number,number+diff2,1)""")).otherwise(F.expr("""sequence(number,number+diff2,0)""")))\
.drop("diff","diff2","max")\
.withColumn("number2", F.explode("number2")).drop("number")\
.select("key", F.col("number2").alias("number"), "value")\
.show()


+---+------+-----+
|key|number|value|
+---+------+-----+
|  a|     1|    6|
|  a|     2|   10|
|  a|     3|   10|
|  a|     4|   10|
|  a|     5|   20|
|  a|     6|   20|
|  a|     7|   20|
|  a|     8|   20|
|  a|     9|   25|
|  a|    10|   25|
|  a|    11|   25|
|  a|    12|   25|
+---+------+-----+
从pyspark.sql.window导入窗口
从pyspark.sql导入函数为F
从pyspark.sql.functions导入时
w=Window().partitionBy(“键”).orderBy(“数字”)
w2=Window().partitionBy(“key”).orderBy(“number”).rowsBetween(Window.unboundedreceiding,Window.unboundedFollowing)
df.withColumn(“number2”,F.lag(“number”).over(w)).withColumn(“diff”,F.when((F.col(“number2”).isNotNull())和((F.col(“number”)-F.col(“number2”)>1),(F.col(“number”)-F.col(“number2”)。否则(F.lit(0)))\
带列(“diff2”,F.lead(“diff”).over(w))。带列(“diff2”,F.when(F.col(“diff2”).isNull(),F.lit(0))。否则(F.col(“diff2”))。带列(“diff2”,F.when(F.col(“diff2”)!=0,F.col(“diff2”))。带列(“max”,F.max(“number”)。over(w2))\
.带列(“diff2”,F.when((F.col(“number”)==F.col(“max”))和(F.col(“number”)
我考虑过创建一个a表和一个1-12的数组,分解数组并与我的原始表连接,然后使用一个以当前行为边界的窗口函数分别用以前的值填充值列。但是,这似乎有点不雅观,我想知道是否有更好的方法来实现我想要的

我不认为你提出的方法是不雅观的,但你可以用同样的方法来代替

首先创建一个包含范围内所有数字的数据框。您还需要将此数据框与数据框中的distinct
key
列交叉连接

all_numbers = spark.range(1, 13).withColumnRenamed("id", "number")
all_numbers = all_numbers.crossJoin(df.select("key").distinct()).cache()
all_numbers.show()
#+------+---+
#|number|key|
#+------+---+
#|     1|  a|
#|     2|  a|
#|     3|  a|
#|     4|  a|
#|     5|  a|
#|     6|  a|
#|     7|  a|
#|     8|  a|
#|     9|  a|
#|    10|  a|
#|    11|  a|
#|    12|  a|
#+------+---+
现在,您可以将其连接到原始数据帧和。如果密钥数量足够小,您可以广播

from pyspark.sql.functions import broadcast, last
from pyspark.sql import Window

df.join(broadcast(all_numbers), on=["number", "key"], how="outer")\
    .withColumn(
        "value", 
        last(
            "value", 
            ignorenulls=True
        ).over(
            Window.partitionBy("key").orderBy("number")\
                .rowsBetween(Window.unboundedPreceding, 0)
        )
    )\
    .show()
#+------+---+-----+
#|number|key|value|
#+------+---+-----+
#|     1|  a|    6|
#|     2|  a|   10|
#|     3|  a|   10|
#|     4|  a|   10|
#|     5|  a|   20|
#|     6|  a|   20|
#|     7|  a|   20|
#|     8|  a|   20|
#|     9|  a|   25|
#|    10|  a|   25|
#|    11|  a|   25|
#|    12|  a|   25|
#+------+---+-----+

我认为在pyspark环境中,您需要的是技巧。我的理解是,spark最适合以列方式工作,添加、筛选、删除列等。我可以假定您事先不知道数字列中有哪些键的间隙吗?您可以使用groupBy()和agg()来提取这一点然后过滤,只保留<12行的键。但这只是乐趣的开始。。。。