我需要为给定的“ id”选择最后一个“ name”。可能的解决方案如下:
val channels = sessions
.select($"start_time", $"id", $"name")
.orderBy($"start_time")
.select($"id", $"name")
.groupBy($"id")
.agg(last("name"))
我不知道它是否正确,因为我不确定执行orderBy
后是否保留groupBy
。
但这当然不是一个高效的解决方案。可能我应该使用reduceByKey
。我在spark壳中尝试了以下方法,并且有效
val x = sc.parallelize(Array(("1", "T1"), ("2", "T2"), ("1", "T11"), ("1", "T111"), ("2", "T22"), ("1", "T100"), ("2", "T222"), ("2", "T200")), 3)
x.reduceByKey((acc,x) => x).collect
但是它不适用于我的数据框。
case class ChannelRecord(id: Long, name: String)
val channels = sessions
.select($"start_time", $"id", $"name")
.orderBy($"start_time")
.select($"id", $"name")
.as[ChannelRecord]
.reduceByKey((acc, x) => x) // take the last object
我遇到了一个编译错误:值reduceByKey不是org.apache.spark.sql.Dataset的成员
我认为我应该在进行map()
之前添加一个reduceByKey
通话,但是我不知道应该映射什么。
答案 0 :(得分:3)
例如,您可以使用窗口功能来实现。这将需要在id
列上进行洗牌,并在start_time
上进行排序。
有两个阶段:
示例数据框:
val rowsRdd: RDD[Row] = spark.sparkContext.parallelize(
Seq(
Row(1, "a", 1),
Row(1, "b", 2),
Row(1, "c", 3),
Row(2, "d", 4),
Row(2, "e", 5),
Row(2, "f", 6),
Row(3, "g", 7),
Row(3, "h", 8)
))
val schema: StructType = new StructType()
.add(StructField("id", IntegerType, false))
.add(StructField("name", StringType, false))
.add(StructField("start_time", IntegerType, false))
val df0: DataFrame = spark.createDataFrame(rowsRdd, schema)
定义一个窗口。请注意,我在这里按start_time
降序排列。这样便可以在下一步中选择第一行。
val w = Window.partitionBy("id").orderBy(col("start_time").desc)
然后
df0.withColumn("last_name", first("name").over(w)) // get first name for each id (first because of decreasing start_time)
.withColumn("row_number", row_number().over(w)) // get row number for each id sorted by start_time
.filter("row_number=1") // choose only first rows (first row = max start_time)
.drop("row_number") // get rid of row_number columns
.sort("id")
.show(10, false)
这将返回
+---+----+----------+---------+
|id |name|start_time|last_name|
+---+----+----------+---------+
|1 |c |3 |c |
|2 |f |6 |f |
|3 |h |8 |h |
+---+----+----------+---------+