pyspark使用与上次非空值相关的计算替换空值
嗨,我的问题与此有点相关() 但我的问题中有一个小小的要求变化,那就是:pyspark使用与上次非空值相关的计算替换空值,pyspark,pyspark-sql,pyspark-dataframes,Pyspark,Pyspark Sql,Pyspark Dataframes,嗨,我的问题与此有点相关() 但我的问题中有一个小小的要求变化,那就是: data: expected output: +------+-----+---------+---------+-----+ +------+-----+---------+---------+-----+ | item|store|timestamp|sales_qty|stock| | i
data: expected output:
+------+-----+---------+---------+-----+ +------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock| | item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+ +------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null| |673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110| |673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| null| |673895|35578| 20180103| 1| 109|
|673895|35578| 20180104| 0| null| |673895|35578| 20180104| 0| 109|
|673895|35578| 20180105| 0| 109| => |673895|35578| 20180105| 0| 109|
|673895|35578| 20180106| 1| null| |673895|35578| 20180106| 1| 108|
|673895|35578| 20180107| 0| 108| |673895|35578| 20180107| 0| 108|
|673895|35578| 20180108| 0| null| |673895|35578| 20180108| 0| 108|
|673895|35578| 20180109| 0| null| |673895|35578| 20180109| 0| 108|
|673895|35578| 20180110| 1| null| |673895|35578| 20180110| 1| 107|
+------+-----+---------+---------+-----+ +------+-----+---------+---------+-----+
我的预期输出基于上次已知的非空值和销售数量,如果有销售数量,则应根据该值调整库存值。
我尝试了以下逻辑
my_window = Window.partitionBy('item','store').orderBy('timestamp')
df = df.withColumn("stock", F.when((F.isnull(F.col('stock'))),F.lag(df.stock).over(my_window)-F.col('sales_qty')).otherwise(F.col('stock')))
但是它只对一个空值有效,有人能帮我达到预期的结果吗
注:数量不总是在连续减少,所以需要考虑最后一个非空值来计算新的一个 < P>你可以试试这个。我基本上先生成两列(第一个非空值=110)和stock2,这基本上是股票的增量总和,然后将它们相互减去,得到您想要的股票
from pyspark.sql.window import Window
from pyspark.sql import functions as F
w=Window().partitionBy("item","store").orderBy("timestamp")
w2=Window().partitionBy("item","store").orderBy("timestamp").rowsBetween(Window.unboundedPreceding,Window.unboundedFollowing)
df.withColumn("stock1", F.when(F.col("stock").isNull(), F.lit(0)).otherwise(F.col("stock")))\
.withColumn("stock2", F.sum("sales_qty").over(w)- F.lit(1))\
.withColumn("first", F.first("stock", True).over(w2))\
.withColumn("stock", F.col("first")-F.col("stock2"))\
.drop("stock1","stock2","first")\
.show()
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| 110|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| 109|
|673895|35578| 20180104| 0| 109|
|673895|35578| 20180105| 0| 109|
|673895|35578| 20180106| 1| 108|
|673895|35578| 20180107| 0| 108|
|673895|35578| 20180108| 0| 108|
|673895|35578| 20180109| 0| 108|
|673895|35578| 20180110| 1| 107|
+------+-----+---------+---------+-----+
如果要强制第一个值为null而不是110(如所需输出中所示),可以使用此选项(基本上使用rownumber将第一个110值替换为null):
其他数据输入和输出:
#input1
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| null|
|673895|35578| 20180104| 3| null|
|673895|35578| 20180105| 0| 109|
|673895|35578| 20180106| 1| null|
|673895|35578| 20180107| 0| 108|
|673895|35578| 20180108| 4| null|
|673895|35578| 20180109| 0| null|
|673895|35578| 20180110| 1| null|
+------+-----+---------+---------+-----+
#output1
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| 109|
|673895|35578| 20180104| 3| 106|
|673895|35578| 20180105| 0| 106|
|673895|35578| 20180106| 1| 105|
|673895|35578| 20180107| 0| 105|
|673895|35578| 20180108| 4| 101|
|673895|35578| 20180109| 0| 101|
|673895|35578| 20180110| 1| 100|
+------+-----+---------+---------+-----+
#input2
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| null|
|673895|35578| 20180104| 7| null|
|673895|35578| 20180105| 0| 102|
|673895|35578| 20180106| 0| null|
|673895|35578| 20180107| 4| 98|
|673895|35578| 20180108| 0| null|
|673895|35578| 20180109| 0| null|
|673895|35578| 20180110| 1| null|
+------+-----+---------+---------+-----+
#output2
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| 109|
|673895|35578| 20180104| 7| 102|
|673895|35578| 20180105| 0| 102|
|673895|35578| 20180106| 0| 102|
|673895|35578| 20180107| 4| 98|
|673895|35578| 20180108| 0| 98|
|673895|35578| 20180109| 0| 98|
|673895|35578| 20180110| 1| 97|
+------+-----+---------+---------+-----+
如果,库存
数量不是连续的如下:
df.show()
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| null|
|673895|35578| 20180104| 7| null|
|673895|35578| 20180105| 0| 112|
|673895|35578| 20180106| 2| null|
|673895|35578| 20180107| 0| 107|
|673895|35578| 20180108| 0| null|
|673895|35578| 20180109| 0| null|
|673895|35578| 20180110| 1| null|
+------+-----+---------+---------+-----+
您可以使用以下内容:(我基本上为每个非空值计算一个动态窗口)
你能写出<代码> MyOxWindows 的定义吗?只是一个提示-考虑最后一个+NealNeulules,用一个正确的窗口定义<代码> df.Fube(“Val1”),最后一个(“Val1,无知Neals=真”)。@Joseph先生我已经在question@mrjoseph我试过F.last函数问题是它将列的最后一个值保留在内存中,而下一个计算值没有被提取,因此它最终显示类似于110,109,110再说“数量不总是在持续减少”你是指销售数量还是股票?@ Muhammad murtaza谢谢你的回答,但是抱歉我忘了提到我不能认为数量总是在不断减少这就是为什么我不能使用这种方法。我必须使用最新的最后一个nul数量来计算下一个我编辑的问题来添加这个limitation@WasifTanveer我认为我的代码是动态的,任何增加或减少,它都会工作。。你可以提供额外的数据行来告诉我你在说什么吗。如果您能对我在更新后的答案中提供的附加数据测试发表意见,我将不胜感激。@Muhammad Murtaza感谢您如此明确的说明,我在我的代码中测试了这个问题。带有动态窗口和随机股票值的代码在这方面工作得非常好。再次感谢您的时间和投入,非常感谢:)
df.show()
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| null|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| null|
|673895|35578| 20180104| 7| null|
|673895|35578| 20180105| 0| 112|
|673895|35578| 20180106| 2| null|
|673895|35578| 20180107| 0| 107|
|673895|35578| 20180108| 0| null|
|673895|35578| 20180109| 0| null|
|673895|35578| 20180110| 1| null|
+------+-----+---------+---------+-----+
from pyspark.sql.window import Window
from pyspark.sql import functions as F
w=Window().partitionBy("item","store").orderBy("timestamp")
w3=Window().partitionBy("item","store","stock5").orderBy("timestamp")
df.withColumn("stock1", F.when(F.col("stock").isNull(), F.lit(0)).otherwise(F.col("stock")))\
.withColumn("stock4", F.when(F.col("stock1")!=0, F.rank().over(w)).otherwise(F.col("stock1")))\
.withColumn("stock5", F.sum("stock4").over(w))\
.withColumn("stock6", F.sum("stock1").over(w3))\
.withColumn("sum", F.sum(F.when(F.col("stock1")!=F.col("stock6"),F.col("sales_qty")).otherwise(F.lit(0))).over(w3))\
.withColumn("stock2", F.when(F.col("sales_qty")!=0, F.col("stock6")-F.col("sum")).otherwise(F.col("stock")))\
.withColumn("stock", F.when((F.col("stock2").isNull())&(F.col("sales_qty")==0),F.col("stock6")-F.col("sum")).otherwise(F.col("stock2")))\
.drop("stock1","stock4","stock5","stock6","sum","stock2")\
.show()
+------+-----+---------+---------+-----+
| item|store|timestamp|sales_qty|stock|
+------+-----+---------+---------+-----+
|673895|35578| 20180101| 1| 0|
|673895|35578| 20180102| 0| 110|
|673895|35578| 20180103| 1| 109|
|673895|35578| 20180104| 7| 102|
|673895|35578| 20180105| 0| 112|
|673895|35578| 20180106| 2| 110|
|673895|35578| 20180107| 0| 107|
|673895|35578| 20180108| 0| 107|
|673895|35578| 20180109| 0| 107|
|673895|35578| 20180110| 1| 106|
+------+-----+---------+---------+-----+