pyspark:自动填充隐式缺失值

pyspark:自动填充隐式缺失值,pyspark,Pyspark,我有一个数据帧 user day amount a 2 10 a 1 14 a 4 5 b 1 4 您可以看到,day的最大值是4,最小值是1。我想在所有用户的所有缺失天数中填写0的amount列,因此上面的数据框将变为 user day amount a 2 10 a 1 14 a 4 5 a 3 0 b 1 4 b 2 0 b 3 0 b 4 0 我怎么能在Pypark中做到这一点?非常感谢。这里有一种方法。您可以先获取最小

我有一个数据帧

user day amount
a 2 10
a 1 14
a 4 5
b 1 4
您可以看到,
day
的最大值是4,最小值是1。我想在所有用户的所有缺失天数中填写
0
amount
列,因此上面的数据框将变为

user day amount
    a 2 10
    a 1 14
    a 4 5
    a 3 0
    b 1 4
    b 2 0
    b 3 0
    b 4 0

我怎么能在Pypark中做到这一点?非常感谢。

这里有一种方法。您可以先获取最小值和最大值,然后在
user
column和pivot上分组,然后填充缺少的列并将所有空值填充为0,然后将它们重新堆叠:

min_max = df.agg(F.min("day"),F.max("day")).collect()[0]
df1 = df.groupBy("user").pivot("day").agg(F.first("amount").alias("amount")).na.fill(0)

missing_cols = [F.lit(0).alias(str(i)) for i in range(min_max[0],min_max[1]+1) 
                                                if str(i) not in df1.columns ]
df1 = df1.select("*",*missing_cols)

#+----+---+---+---+---+
#|user|  1|  2|  4|  3|
#+----+---+---+---+---+
#|   b|  4|  0|  0|  0|
#|   a| 14| 10|  5|  0|
#+----+---+---+---+---+

#the next step is inspired from https://stackoverflow.com/a/37865645/9840637
arr = F.explode(F.array([F.struct(F.lit(c).alias("day"), F.col(c).alias("amount"))
                                           for c in df1.columns[1:]])).alias("kvs")
(df1.select(["user"] + [arr])
    .select(["user"]+ ["kvs.day", "kvs.amount"]).orderBy("user")).show()


注意,由于列日是透视的,数据类型可能已更改,因此您可能必须将它们转换回原始数据类型。另一种方法是使用序列、数组函数和分解
(spark2.4+

from pyspark.sql import functions as F
from pyspark.sql.window import Window

w=Window().partitionBy(F.lit(0))

df.withColumn("boundaries", F.sequence(F.min("day").over(w),F.max("day").over(w),F.lit(1)))\
  .groupBy("user").agg(F.collect_list("day").alias('day'),F.collect_list("amount").alias('amount')\
   ,F.first("boundaries").alias("boundaries")).withColumn("boundaries", F.array_except("boundaries","day"))\
  .withColumn("day",F.flatten(F.array("day","boundaries"))).drop("boundaries")\
  .withColumn("zip", F.explode(F.arrays_zip("day","amount")))\
  .select("user","zip.day", F.when(F.col("zip.amount").isNull(),\
                                   F.lit(0)).otherwise(F.col("zip.amount")).alias("amount")).show()
#+----+---+------+
#|user|day|amount|
#+----+---+------+
#|   a|  2|    10|
#|   a|  1|    14|
#|   a|  4|     5|
#|   a|  3|     0|
#|   b|  1|     4|
#|   b|  2|     0|
#|   b|  3|     0|
#|   b|  4|     0|
#+----+---+------+

让我们说不,但我们可以很容易地让他们正确。我正在使用spark 3.0.0。
from pyspark.sql import functions as F
from pyspark.sql.window import Window

w=Window().partitionBy(F.lit(0))

df.withColumn("boundaries", F.sequence(F.min("day").over(w),F.max("day").over(w),F.lit(1)))\
  .groupBy("user").agg(F.collect_list("day").alias('day'),F.collect_list("amount").alias('amount')\
   ,F.first("boundaries").alias("boundaries")).withColumn("boundaries", F.array_except("boundaries","day"))\
  .withColumn("day",F.flatten(F.array("day","boundaries"))).drop("boundaries")\
  .withColumn("zip", F.explode(F.arrays_zip("day","amount")))\
  .select("user","zip.day", F.when(F.col("zip.amount").isNull(),\
                                   F.lit(0)).otherwise(F.col("zip.amount")).alias("amount")).show()
#+----+---+------+
#|user|day|amount|
#+----+---+------+
#|   a|  2|    10|
#|   a|  1|    14|
#|   a|  4|     5|
#|   a|  3|     0|
#|   b|  1|     4|
#|   b|  2|     0|
#|   b|  3|     0|
#|   b|  4|     0|
#+----+---+------+