我的数据集如下:
+-------+-------------------+
| id| ts|
+-------+-------------------+
| b|2017-01-01 00:00:01|
| b|2017-01-01 00:00:02|
| b|2017-01-01 00:00:03|
| b|2017-01-01 00:00:04|
| b|2017-01-01 00:00:06|
| b|2017-01-01 00:00:07|
| d|2017-01-01 00:01:07|
| d|2017-01-01 00:01:09|
| d|2017-01-01 00:01:10|
| d|2017-01-01 00:01:11|
| d|2017-01-01 00:01:13|
| d|2017-01-01 00:01:14|
+-------+-------------------+
我想在具有相同id的时间戳上应用聚合,并以递增的顺序对ts数据应用agg。 我所做的是使用udaf:
abstract class TsGroupAgg[OUT](f: (List[Long]) => OUT) extends
Aggregator[Row, String, OUT] {
def zero: String = ""
def reduce(buffer: String, dataInGroup: Row): String =
buffer + s";${dataInGroup.getString(1)}"
def merge(b1: String, b2: String): String = s"$b1;$b2"
def finish(r: String): OUT = {
val list = r.split(";").toList
f(list.filter(_.length > 0).map(DateUtils.getTimestamp))
}
def bufferEncoder: Encoder[String] = Encoders.STRING
}
和
def tsGrpCal: TypedColumn[Row, Int] =
new TsGroupCnt(calculateGroupTs).toColumn.name("tsGrpCal")
df.groupBy("id").agg(tsGrpCal)
如您所见,我通过“id”将数据分组到数据框中,并应用我自己的聚合器。在我的聚合器中,我收集字符串中的所有ts数据,在最后一步中,我将字符串中的所有ts数据转换为列表,对其进行排序,并在列表中应用calculateGroupTs
方法。在calculateGroupTs
中,我可以按照asc的顺序应用聚合。
有一个问题,收集字符串中的所有ts数据不是一个好方法,它很难看。当数据量非常大,如1m时,就会导致OOM。
那么,有没有办法按顺序对分组数据应用聚合方法?
答案 0 :(得分:1)
我想知道你为什么不使用开箱即用的Spark SQL中可用的窗口聚合函数,这会给你带来最佳性能?
有没有办法按顺序对分组数据应用聚合方法?
我是这么认为的。见下文。订单保证是输入订单,因此请根据您的需要对其进行排序并应用汇总。
val timeseries = spark.read.option("header", true).csv("timeseries.csv")
scala> timeseries.show
+---+-------------------+
| id| ts|
+---+-------------------+
| b|2017-01-01 00:00:01|
| b|2017-01-01 00:00:02|
| b|2017-01-01 00:00:03|
| b|2017-01-01 00:00:04|
| b|2017-01-01 00:00:06|
| b|2017-01-01 00:00:07|
| d|2017-01-01 00:01:07|
| d|2017-01-01 00:01:09|
| d|2017-01-01 00:01:10|
| d|2017-01-01 00:01:11|
| d|2017-01-01 00:01:13|
| d|2017-01-01 00:01:14|
+---+-------------------+
val tss = timeseries.groupBy("id").agg(collect_list("ts") as "tss")
scala> tss.show(false)
+---+------------------------------------------------------------------------------------------------------------------------------+
|id |tss |
+---+------------------------------------------------------------------------------------------------------------------------------+
|d |[2017-01-01 00:01:07, 2017-01-01 00:01:09, 2017-01-01 00:01:10, 2017-01-01 00:01:11, 2017-01-01 00:01:13, 2017-01-01 00:01:14]|
|b |[2017-01-01 00:00:01, 2017-01-01 00:00:02, 2017-01-01 00:00:03, 2017-01-01 00:00:04, 2017-01-01 00:00:06, 2017-01-01 00:00:07]|
+---+------------------------------------------------------------------------------------------------------------------------------+
val merged = tss.select($"id", concat_ws(";", $"tss") as "merge")
scala> merged.show(false)
+---+-----------------------------------------------------------------------------------------------------------------------+
|id |merge |
+---+-----------------------------------------------------------------------------------------------------------------------+
|d |2017-01-01 00:01:07;2017-01-01 00:01:09;2017-01-01 00:01:10;2017-01-01 00:01:11;2017-01-01 00:01:13;2017-01-01 00:01:14|
|b |2017-01-01 00:00:01;2017-01-01 00:00:02;2017-01-01 00:00:03;2017-01-01 00:00:04;2017-01-01 00:00:06;2017-01-01 00:00:07|
+---+-----------------------------------------------------------------------------------------------------------------------+
来自键入的API或使用自定义Aggregator
的任何内容通常会导致较差的性能,而且我现在经常声称您使用的内置函数越多,性能就越好。
只需检查实际计划。
由于groupBy
,我并不是说它是最好的实际计划,但在用例中使用自定义Scala代码可能会带来更糟糕的计划。