PySpark UDF功能与数据帧查询?

时间:2019-02-02 11:55:51

标签: pyspark pyspark-sql

我有另一个解决方案,但是我更喜欢使用PySpark 2.3来实现。

我有一个二维的PySpark数据框,如下所示:

Date       | ID
---------- | ----
08/31/2018 | 10
09/31/2018 | 10
09/01/2018 | null
09/01/2018 | null
09/01/2018 | 12

我想通过查找过去最接近的值来替换ID空值,或者向前看(如果该值再次为空,则设置默认值)来替换该值。

我曾想过用.withColumn添加一个新列并使用UDF函数来查询数据框本身。

类似于伪代码的东西(虽然不完美,但这是主要思想):

from pyspark.sql.types import StringType
from pyspark.sql.functions import udf

def return_value(value,date):

    if value is not null:
        return val

    value1 = df.filter(df['date']<= date).select(df['value']).collect()

    if (value1)[0][0] is not null:
        return (value1)[0][0]

    value2 = df.filter(tdf['date']>= date).select(df['value']).collect()
        return (value2)[0][0]


value_udf = udf(return_value,StringType())
new_df = tr.withColumn("new_value", value_udf(df.value,df.date))

但是它不起作用。我是完全错误地做到了吗?是否只能在UDF函数中查询Spark数据帧?我错过了一个更简单的解决方案吗?

1 个答案:

答案 0 :(得分:-1)

创建有一列新的数据框 - 所有日期的唯一列表:

datesDF = yourDF.select('Date').distinct()

创建另一个将由日期和ID组成的标识符,但仅包含没有空值的标识符。并且还允许仅保留第一(无论将是第一个)每个日期ID的发生(从你的例子来看可以有每日期多行)

noNullsDF = yourDF.dropna().dropDuplicates(subset='Date')

现在,让我们加入这两个让我们有什么价值,我们有这方面(或空)的所有日期的名单

joinedDF = datesDF.join(noNullsDF, 'Date', 'left')

现在对于每个日期,都可以使用窗口函数从上一个日期和下一个日期获取ID的值,还可以重命名ID列,以便以后使用join的问题更少:

from pyspark.sql.window import Window
from pyspark.sql import functions as f
w = Window.orderBy('Date')

joinedDF = joinedDF.withColumn('previousID',f.lag('ID').over(w)) 
                   .withColumn('nextID',f.lead('ID').over(w))
                   .withColumnRenamed('ID','newID') 

现在,让它按日期加入到我们的原始数据框中

yourDF = yourDF.join(joinedDF, 'Date', 'left')

现在我们的数据框具有4个ID列:

  1. 原始ID
  2. newID-给定日期的任何非空值的ID(如果有)或为空
  3. previousID - 从先前日期ID(非空,如果任何或空)
  4. nextID - 从下一个日期ID(非空,如果任何或空)

现在,我们需要按顺序将它们组合成finalID:

  1. 原始值(如果不为空)
  2. 如果结果不为空,则
  3. 当前日期的值(如果存在任何非空值(与您的问题相反,但您的熊猫代码建议您进行<=进行日期检查)
  4. 上一个日期的值(如果其不为空)
  5. 下一个日期的值(如果其不为空)
  6. 一些默认值

我们只是通过合并来做到这一点:

default = 0
finalDF = yourDF.select('Date', 
                        'ID',
                        f.coalesce('ID',
                                   'newID',
                                   'previousID',
                                   'nextID',
                                   f.lit(default)).alias('finalID')
                       )