首页 > 解决方案 > SparkSql if value null 取前一个

问题描述

所以我有这个数据框

+---+-------------+-----+
| id|    timestamp|  num|
+---+-------------+-----+
| 10|1546300799000| 37.5|
| 10|1546300800000| null|
| 10|1546300801000| null|
| 10|1546300802000|37.51|
| 20|1546300804000| null|
| 10|1546300806000| 37.5|
| 10|1546300807000| null|
+---+-------------+-----+

我想要实现的是,num如果存在,则必须使用值本身更新,或者如果为 null,则从前一行获取的“最后一个”值(按时间戳排序并按 id 分组)

所以这应该是输出

+---+-------------+-----+
| id|    timestamp|  num|
+---+-------------+-----+
| 10|1546300799000| 37.5|
| 10|1546300800000| 37.5|
| 10|1546300801000| 37.5|
| 10|1546300802000|37.51|
| 20|1546300804000| null|
| 10|1546300806000| 37.5|
| 10|1546300807000| 37.5|
+---+-------------+-----+

我想出了这个解决方案

w = Window.partitionBy('id').orderBy('timestamp')
final = joined.withColumn('num2', when(col('num').isNull(), lag(col('num')).over(w)).otherwise(col('num')))

但这是我得到的输出

+---+-------------+-----+-----+
| id|    timestamp|  num| num2|
+---+-------------+-----+-----+
| 10|1546300799000| 37.5| 37.5|
| 10|1546300800000| null| 37.5|
| 10|1546300801000| null| null|
| 10|1546300802000|37.51|37.51|
| 20|1546300804000| null| null|
| 10|1546300806000| 37.5| 37.5|
| 10|1546300807000| null| 37.5|
+---+-------------+-----+-----+

如您所见,如果isNull,值将获取前一个值,但是如果您查看第三行,我会得到一个空值,并且我假设因为它获取了第二行的值,但是当它仍未更新时(所以仍然是原始数据帧中的空值)。

我有点迷茫我应该如何进行。有什么帮助吗?

标签: apache-sparkpysparkapache-spark-sql

解决方案


您正在期待填写一项措施,不幸的是,Pyspark 不像 Pandas 那样内置。但是有一个解决方法。

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

 window = Window.partitionBy('id')\
           .orderBy('timestamp')\
           .rowsBetween(Window.unboundedPreceding, Window.currentRow)

 final = joined.\
               withColumn('numFilled', F.last('num',ignorenulls = True).over(window)

所以这样做是它根据分区键和顺序列构造您的窗口。它还告诉窗口回顾以前的行和当前行。最后,在每一行,您返回最后一个不为空的值(根据您的窗口记住,这包括您的当前行)


推荐阅读