将时间序列数据映射到先前的数据点和平均值

时间:2015-09-11 14:51:33

标签: scala apache-spark

如果我有每分钟卷的RDD,例如

(("12:00" -> 124), ("12:01" -> 543), ("12:02" -> 102), ... )

我想将其映射到此分钟的音量数据集,前一分钟的音量,前5分钟的平均音量。 E.g。

(("12:00" -> (124, 300, 245.3)),
("12:01" -> (543, 124, 230.2)),
("12:02" -> (102, 543, 287.1)))

输入RDD可以是RDD[(DateTime, Int)],输出RDD[(DateTime, (Int, Int, Float))]

有什么好方法可以做到这一点?

1 个答案:

答案 0 :(得分:4)

转换为数据框并使用窗口函数可以涵盖滞后,平均和可能的间隙:

import com.github.nscala_time.time.Imports._
import org.apache.spark.sql.Row
import org.apache.spark.sql.functions.{lag, avg, when}
import org.apache.spark.sql.expressions.Window

val fmt = DateTimeFormat.forPattern("HH:mm:ss")

val rdd = sc.parallelize(Seq(
  ("12:00:00" -> 124), ("12:01:00" -> 543), ("12:02:00" -> 102),
  ("12:30:00" -> 100), ("12:31:00" -> 101)
).map{case (ds, vol) => (fmt.parseDateTime(ds), vol)})

val df = rdd
  // Convert to millis for window range
  .map{case (dt, vol) => (dt.getMillis, vol)} 
  .toDF("ts", "volume")

val w = Window.orderBy($"ts")

val transformed = df.select(
  $"ts", $"volume",
  when(
    // Check if we have data from the previous minute
    (lag($"ts", 1).over(w) - $"ts").equalTo(-60000), 
    // If so get lag otherwise 0
    lag($"volume", 1).over(w)).otherwise(0).alias("previous_volume"),
  // Average over window 
  avg($"volume").over(w.rangeBetween(-300000, 0)).alias("average"))

// Optionally go to back to RDD
transformed.map{
  case Row(ts: Long, volume: Int, previousVolume: Int, average: Double) =>
    (new DateTime(ts) -> (volume, previousVolume, average))
}

请注意,没有窗口分区的窗口函数效率很低。