Pyspark-是否为每个键添加缺少的值?
我有一个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
键
,以及一些列数字
和值
对于大多数键
,数字
列从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的数组,分解数组并与我的原始表连接,然后使用一个以当前行为边界的窗口函数分别用以前的值填充值列。但是,这似乎有点不雅观,我想知道是否有更好的方法来实现我想要的
我不认为你提出的方法是不雅观的,但你可以用同样的方法来代替
首先创建一个包含范围内所有数字的数据框。您还需要将此数据框与数据框中的distinctkey
列交叉连接
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行的键。但这只是乐趣的开始。。。。