如何遍历数据集以创建摘要数据集

时间:2018-02-07 05:10:31

标签: apache-spark spark-dataframe

我刚开始学习和使用Spark,目前面临一个问题。任何建议或提示将不胜感激。

基本上我有一个数据集,其中包含不同用户的所有类型的事件,如AppLaunch,GameStart,GameEnd等。我想创建每个用户每次启动应用程序时的操作摘要。

例如:我有以下数据集:
UserId | Event Type | Time | GameType | Event Id|
11111 | AppLauch | 11:01:53| null | 101 |
11111 | GameStart | 11:01:59| Puzzle | 102 |
11111 | GameEnd | 11:05:31| Puzzle | 103 |
11111 | GameStart | 11:05:58| Word | 104 |
11111 | GameEnd | 11:09:13| Word | 105 |
11111 | AppEnd | 11:09:24| null | 106 |
11111 | AppLauch | 12:03:43| null | 107 |
22222 | AppLauch | 12:03:52| null | 108 |
22222 | GameStart | 12:03:59| Puzzle | 109 |
11111 | GameStart | 12:04:01| Puzzle | 110 |
22222 | GameEnd | 12:06:11| Puzzle | 111 |
11111 | GameEnd | 12:06:13| Puzzle | 112 |
11111 | AppEnd | 12:06:23| null | 113 |
22222 | AppEnd | 12:06:33| null | 114 |

我想要的是一个与此类似的数据集:
EventId | USerId| Event Type | Time | FirstGamePlayed| LastGamePlayed|
101 |11111 | AppLauch | 11:01:53| Puzzle | Word |
107 |11111 | AppLauch | 12:03:43| Puzzle | Puzzle |
108 |22222 | AppLauch | 12:03:52| Puzzle | Puzzle |

只需要知道第一场比赛和最后一场比赛,即使在一次应用发布中有超过3场比赛。

我最初的想法是按用户ID和时间范围窗口(AppLaunch到AppEnd)对它们进行分组,然后找到一种扫描数据集的方法,如果有gameStart事件并且它落入任何窗口,它将是FirstGamePlayed,在AppEnd时间之前的最后一个GameStart事件将是LastGamePlayed。但我找不到实现这个目标的方法。

任何提示/建议都会很好。

由于

1 个答案:

答案 0 :(得分:1)

我认为这可以通过使用窗口函数来解决,然后是这样的聚合:

df
   // enumerate AppLaunches 
   .withColumn("AppLauchNr", sum(when($"EventType" === "AppLauch", 1)).over(Window.partitionBy($"UserId").orderBy($"Time".asc)))
   // get first last game per AppLaunch
   .withColumn("firstGamePlayed", first($"GameType", true).over(Window.partitionBy($"UserId", $"AppLauchNr").orderBy($"Time".asc)))
   .withColumn("lastGamePlayed", first($"GameType", true).over(Window.partitionBy($"UserId", $"AppLauchNr").orderBy($"Time".desc)))
    // now aggregate
   .groupBy($"AppLauchNr")
   .agg(
        first($"UserId").as("UserId"),
        min($"EventId").as("EventId"),
        lit("AppLauch").as("EventType"), // this is always AppLauch
        min($"Time").as("Time"),
        first($"firstGamePlayed", true).as("firstGamePlayed"),
        first($"lastGamePlayed", true).as("lastGamePlayed")
   )
  .drop($"AppLauchNr")

也可以使用orderBy().groupBy()而不是窗口函数来确定播放的第一个和最后一个游戏,但是我仍然不确定spark会在聚合期间保留排序(这在文档中没有提及,请参阅例如Spark DataFrame: does groupBy after orderBy maintain that order?以及https://issues.apache.org/jira/browse/SPARK-16207

中的讨论
 df
   .withColumn("AppLauchNr", sum(when($"EventType" === "AppLauch", 1)).over(Window.partitionBy($"UserId").orderBy($"Time".asc)))
   .orderBy($"UserId",$"AppLauchNr",$"Time")
   .groupBy($"UserId",$"AppLauchNr")
   .agg(
        first($"EventId").as("EventId"),
        first($"EventType").as("EventType"),
        first($"Time").as("Time"),
        first($"GameType", true).as("firstGamePlayed"),
        last($"GameType", true).as("lastGamePlayed")
   )
   .drop($"AppLauchNr")