Apache spark 使用pyspark使用以前已知的良好值填充null

Apache spark 使用pyspark使用以前已知的良好值填充null,apache-spark,pyspark,apache-spark-sql,Apache Spark,Pyspark,Apache Spark Sql,有没有办法用最后一个有效值替换pyspark dataframe中的null值?如果您认为windows分区和排序需要添加timestamp和session列,则会添加这些列。更具体地说,我希望实现以下转换: +---------+-----------+-----------+ +---------+-----------+-----------+ | session | timestamp | id| | session | timestamp |

有没有办法用最后一个有效值替换pyspark dataframe中的
null
值?如果您认为windows分区和排序需要添加
timestamp
session
列,则会添加这些列。更具体地说,我希望实现以下转换:

+---------+-----------+-----------+      +---------+-----------+-----------+
| session | timestamp |         id|      | session | timestamp |         id|
+---------+-----------+-----------+      +---------+-----------+-----------+
|        1|          1|       null|      |        1|          1|       null|
|        1|          2|        109|      |        1|          2|        109|
|        1|          3|       null|      |        1|          3|        109|
|        1|          4|       null|      |        1|          4|        109|
|        1|          5|        109| =>   |        1|          5|        109|
|        1|          6|       null|      |        1|          6|        109|
|        1|          7|        110|      |        1|          7|        110|
|        1|          8|       null|      |        1|          8|        110|
|        1|          9|       null|      |        1|          9|        110|
|        1|         10|       null|      |        1|         10|        110|
+---------+-----------+-----------+      +---------+-----------+-----------+

这似乎是通过以下方式实现的:


这是完整的。

@Oleksiy
的答案很好,但没有完全满足我的要求。在一个会话中,如果观察到多个
null
s,则所有会话都将填充该会话的第一个非
null
。我需要最后一个非空值来向前传播

以下调整适用于我的用例:

def fill_forward(df, id_column, key_column, fill_column):

    # Fill null's with last *non null* value in the window
    ff = df.withColumn(
        'fill_fwd',
        func.last(fill_column, True) # True: fill with last non-null
        .over(
            Window.partitionBy(id_column)
            .orderBy(key_column)
            .rowsBetween(-sys.maxsize, 0))
        )

    # Drop the old column and rename the new column
    ff_out = ff.drop(fill_column).withColumnRenamed('fill_fwd', fill_column)

    return ff_out

我相信我有一个比公认的更简单的解决方案。它也使用函数,但使用名为“LAST”的函数并忽略空值

让我们重新创建与原始数据类似的内容:

import sys
from pyspark.sql.window import Window
import pyspark.sql.functions as func

d = [{'session': 1, 'ts': 1}, {'session': 1, 'ts': 2, 'id': 109}, {'session': 1, 'ts': 3}, {'session': 1, 'ts': 4, 'id': 110}, {'session': 1, 'ts': 5},  {'session': 1, 'ts': 6}]
df = spark.createDataFrame(d)
这张照片是:

+-------+---+----+
|session| ts|  id|
+-------+---+----+
|      1|  1|null|
|      1|  2| 109|
|      1|  3|null|
|      1|  4| 110|
|      1|  5|null|
|      1|  6|null|
+-------+---+----+
现在,如果我们最后使用window函数:

df.withColumn("id", func.last('id', True).over(Window.partitionBy('session').orderBy('ts').rowsBetween(-sys.maxsize, 0))).show()
我们刚刚得到:

+-------+---+----+
|session| ts|  id|
+-------+---+----+
|      1|  1|null|
|      1|  2| 109|
|      1|  3| 109|
|      1|  4| 110|
|      1|  5| 110|
|      1|  6| 110|
+-------+---+----+

希望有帮助

你不能。数据帧行之间没有顺序。如果我有一个按时间戳排序的顺序,怎么办?你不能按某种类型的寡妇进行分区吗?在这种情况下,你会怎么做,逐个手动处理条目并保持状态?@eliasah“不可能”是一个强有力的断言,我会谨慎使用。正如下面几个答案所表明的,这是可能的。(尽管这些解决方案可能并不适用于所有情况。)@lostsoul29我当时就问题的状态发表了评论,现在已经过时了。我把它取下来。谢谢@eliasah:你能复习一下答案吗?我现在正在看。增加了一个测试,如果它有帮助的话,会让我的生活更轻松。我正在写我的测试!谢谢我觉得答案很清楚。拥有会话是很重要的,这使得分区成为可能,从而使用窗口功能!很好的解决方案!我很惊讶Spark中还没有这个功能。警告一句:这个答案会将每个会话的所有行收集到某个执行器节点。如果某个会话中的行数大于执行器节点的内存,这将导致作业失败。
+-------+---+----+
|session| ts|  id|
+-------+---+----+
|      1|  1|null|
|      1|  2| 109|
|      1|  3| 109|
|      1|  4| 110|
|      1|  5| 110|
|      1|  6| 110|
+-------+---+----+