Pyspark:用最新的行值填充缺少的日期

时间:2020-08-14 09:07:19

标签: python dataframe apache-spark pyspark

我发现了各种类似的问题,但没有一个能回答我的具体问题。

我需要使用基于日期列的最新行值来填充pyspark数据框中的缺失日期行。

我当前的解决方案是计算直到今天为止的缺失日期列表,与原始df合并,并用最新有效值一一填充所有列:

# get the maximum date from the df
max_date = df.select(F.max('date')).first()['max(date)')

today = datetime.date.today()
delta = today - max_date
dates_list = [(today - datetime.timedelta(days=x),) for x in range(delta.days)]

# if there are missing rows
if dates_list:
    # create df with one column 'date'
    dates_df = spark.createDataFrame(dates_list, schema=date_schema)

    # join with original df
    df = df.join(F.broadcast(dates_df), ['date'], 'outer')

    w = Window.orderBy('date').rangeBetween(Window.unboundedPreceding, 0)
    
    # fill all columns with latest non null col value
    for c in df.columns:
        if c != 'date':
            df = df.withColumn(c, F.last(c, ignorenulls=True).over(w))

此代码的问题在于,原始df包含大量列,并且针对每一个spark计算一个窗口以获取最后的非null值,并且这种方法的效率很低,导致制定了庞大的逻辑计划。 / p>

我想以一种简单的方式来实现它,即简单地获取具有最大日期的行内容(因为它不包含空值),并使用直到今天的计算日期列表来更改日期。

关于如何实施这种方法的任何建议?

示例输入:

date       | col_one | col_two | col_three | .. | col_n
-------------------------------------------------------
2020-08-15 | 0.1     | 6.5     | 9.8       | .. | 0.7
2020-08-14 | 0.2     | 5.5     | 1.8       | .. | 3.7
2020-08-13 | 0.4     | 7.5     | 1.3       | .. | 0.5
2020-08-12 | 3.1     | 8.5     | 9.8       | .. | 1.7
2020-08-11 | 0.15    | 6.9     | 9.7       | .. | 0.2

示例输出:

date       | col_one | col_two | col_three | .. | col_n
-------------------------------------------------------
2020-08-18 | 0.1     | 6.5     | 9.8       | .. | 0.7
2020-08-17 | 0.1     | 6.5     | 9.8       | .. | 0.7
2020-08-16 | 0.1     | 6.5     | 9.8       | .. | 0.7
2020-08-15 | 0.1     | 6.5     | 9.8       | .. | 0.7
2020-08-14 | 0.2     | 5.5     | 1.8       | .. | 3.7
2020-08-13 | 0.4     | 7.5     | 1.3       | .. | 0.5
2020-08-12 | 3.1     | 8.5     | 9.8       | .. | 1.7
2020-08-11 | 0.15    | 6.9     | 9.7       | .. | 0.2

1 个答案:

答案 0 :(得分:0)

一个可能的解决方案是过滤具有最大日期的原始数据框,使用第一个缺少的日期填充日期列,并对每个剩余的缺少的日期将相同的行内容与缺少的日期合并。然后最后使用原始df重新加入:

# compute the list of all dates from maximum date available till today
max_date = df.select(F.max('date')).first()['max(date)']
today = datetime.date.today()
dates_list = [today - datetime.timedelta(days=x) for x in range((today - max_date).days)]

# take the row with latest date values
max_date_values = df.filter(F.col('date') == max_date)
missing_dates_values = None

# duplicate latest values for the dates we are missing till today
for missing_date in dates_list:
    if missing_dates_values is None:
        missing_dates_values = max_date_values.withColumn('date', F.lit(missing_date))
    else:
       missing_dates_values = missing_dates_values.unionByName(max_date_values.withColumn('date',
                                                                                        F.lit(missing_date)))

# union with the original df
if dates_list:
    df = df.unionByName(missing_dates_values)

在我的用例中,关于火花查询计划,在获得更好的性能之前要简单得多。