Spark DF的转换结构。为列中的每个值创建一个列或行。估算值

时间:2018-07-30 21:13:05

标签: scala apache-spark dataframe apache-spark-sql

我有一个具有以下结构的Spark DF:

+--------------------------------------+
| user| time                  | counts |
+--------------------------------------+
|   1 | 2018-06-04 16:00:00.0 | 5      |
|   1 | 2018-06-04 17:00:00.0 | 7      |
|   1 | 2018-06-04 17:30:00.0 | 7      |
|   1 | 2018-06-04 18:00:00.0 | 8      |
|   1 | 2018-06-04 18:30:00.0 | 10     |
|   1 | 2018-06-04 19:00:00.0 | 9      |
|   1 | 2018-06-04 20:00:00.0 | 7      |
|   2 | 2018-06-04 17:00:00.0 | 4      |
|   2 | 2018-06-04 18:00:00.0 | 4      |
|   2 | 2018-06-04 18:30:00.0 | 5      |
|   2 | 2018-06-04 19:30:00.0 | 7      |
|   3 | 2018-06-04 16:00:00.0 | 6      |
+--------------------------------------+

它是使用以下代码从事件日志表中获得的:

ranked.groupBy($"user", sql.functions.window($"timestamp", "30 minutes"))
.agg(sum("id").as("counts"))
.withColumn("time", $"window.start")

从查看time列可以看出,并不是每个用户都注册了所有30分钟间隔的事件,即,并非所有user组帧都具有相同的长度。我想估算(可能带有NA0的时间值,并创建一个如下表(或RDD):

+-----------------------------------------------------------------------------+ 
| user| 2018-06-04 16:00:00 | 2018-06-04 16:30:00 | 2018-06-04 17:00:00 | ... |
+-----------------------------------------------------------------------------+
|   1 | 5                   | NA (or 0)           | 7                   | ... |
|   2 | NA (or 0)           | NA (or 0)           | 4                   | ... |
|   3 | 6                   | NA (or 0)           | NA (or 0)           | ... |
+-----------------------------------------------------------------------------+

上表的转置(带有time,列和每个user的计数列)在理论上也可以工作,但是我不确定这是否是最佳的火花-明智的做法是,我有近一百万个不同的用户。

如何执行如上所述的表重组?

1 个答案:

答案 0 :(得分:1)

如果每个时间窗口至少为一个用户显示,那么一个简单的枢轴就可以解决问题(并为缺少的值置空)。对于数百万行,情况应该如此。

val reshaped_df = df.groupBy("user").pivot("time").agg(sum('counts))

万一仍然缺少列,您可以使用reshaped_df.columns访问列的列表,然后添加缺少的列。您将需要生成所需列的列表(expected_columns),然后生成缺少的列,如下所示:

val expected_columns = ???
var result = reshaped_df
expected_columns
    .foreach{ c => 
        if(! result.columns.contains(c)) 
            result = result.withColumn(c, lit(null))
    }