从后续项目聚合第一个分组项目

时间:2016-07-05 03:39:57

标签: apache-spark pyspark apache-spark-sql pyspark-sql

我有用户游戏会话,其中包含:玩游戏时的用户ID,游戏ID,分数和时间戳。

from pyspark import SparkContext
from pyspark.sql import HiveContext
from pyspark.sql import functions as F

sc = SparkContext("local")

sqlContext = HiveContext(sc)

df = sqlContext.createDataFrame([
    ("u1", "g1", 10, 0),
    ("u1", "g3", 2, 2),
    ("u1", "g3", 5, 3),
    ("u1", "g4", 5, 4),
    ("u2", "g2", 1, 1),
], ["UserID", "GameID", "Score", "Time"])

所需输出

+------+-------------+-------------+
|UserID|MaxScoreGame1|MaxScoreGame2|
+------+-------------+-------------+
|    u1|           10|            5|
|    u2|            1|         null|
+------+-------------+-------------+

我希望转换数据,以便获得用户玩的第一个游戏的最高分数以及第二个游戏的最高分数(如果我还可以获得所有后续游戏的最高分数,则获得奖励)。不幸的是,我不确定如何使用Spark SQL。

我知道我可以通过UserID,GameID和agg进行分组,以获得最高分和最短时间。不确定如何从那里开始。

澄清:注意MaxScoreGame1和MaxScoreGame2是指第一个和第二个游戏用户玩家;不是GameID。

2 个答案:

答案 0 :(得分:1)

您可以尝试使用Window函数和Pivot的组合。

  1. 获取按时间排序的UserID分区的每个游戏的行号。
  2. 过滤到GameNumber为1或2。
  3. 转动它以获得所需的输出形状。
  4. 不幸的是我使用的是scala而不是python,但下面应该可以很容易地转移到python库。

    import org.apache.spark.sql.expressions.Window
    
    // Use a window function to get row number
    val rowNumberWindow = Window.partitionBy(col("UserId")).orderBy(col("Time"))  
    
    val output = {
      df
        .select(
          col("*"),
          row_number().over(rowNumberWindow).alias("GameNumber")
        )
        .filter(col("GameNumber") <= lit(2))
        .groupBy(col("UserId"))
        .pivot("GameNumber")
        .agg(
          sum(col("Score"))
        )
    }
    
    output.show()
    
    +------+---+----+
    |UserId|  1|   2|
    +------+---+----+
    |    u1| 10|   2|
    |    u2|  1|null|
    +------+---+----+
    

答案 1 :(得分:1)

使用PySpark解决方案:

from pyspark.sql import Window

rowNumberWindow = Window.partitionBy("UserID").orderBy(F.col("Time"))

(df
 .groupBy("UserID", "GameID")
 .agg(F.max("Score").alias("Score"),
      F.min("Time").alias("Time"))
 .select(F.col("*"),
         F.row_number().over(rowNumberWindow).alias("GameNumber"))
 .filter(F.col("GameNumber") <= F.lit(2))
 .withColumn("GameMaxScoreCol", F.concat(F.lit("MaxScoreGame"), F.col("GameNumber")))
 .groupBy("UserID")
 .pivot("GameMaxScoreCol")  
 .agg(F.max("Score"))
).show()

+------+-------------+-------------+
|UserID|MaxScoreGame1|MaxScoreGame2|
+------+-------------+-------------+
|    u1|           10|            5|
|    u2|            1|         null|
+------+-------------+-------------+