我有一个具有以下结构的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
组帧都具有相同的长度。我想估算(可能带有NA
或0
的时间值,并创建一个如下表(或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
的计数列)在理论上也可以工作,但是我不确定这是否是最佳的火花-明智的做法是,我有近一百万个不同的用户。
如何执行如上所述的表重组?
答案 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))
}