我正在Spark Streaming中编写一个应用程序,我需要计算double值的指数移动平均值并将该平均值添加到行中。 这个平均值计算如下:
EMA(t)= EMA(t-1)* 0.75 + Value(t)* 0.25
每个时间间隔我都有一行数据来源:
(name1-24 / 04/2015 15:31;观察(名称1; 24/04/2015 15:31; 132.45))
(name2-24 / 04/2015 15:31;观察(名称2; 24/04/2015 15:31; 20.5))
我的唯一键是粘贴在一起的名称和时间戳。然后我将名称和时间戳分开,然后我的双倍值。我将跟踪每个不同名称的指数移动平均值。
我正在使用updateStateByKey()完成此操作: (名称将是此操作中的关键,因为我需要每个名称的平均值)
case class Observation(name: String, time: Timestamp, outcome: Double)
val outcomeDstream: DStream[(String, Double)] =
parsedstream.map { case (k: String, obs: Observation) => (obs.name, obs.close) }
def updateEMA(newValues: Seq[Double],oldCount: Option[Double]): Option[Double] = {
if (oldCount.isEmpty) newValues(0)
else Some((newValues(0)*0.25) + (oldCount.get*(0.75)))
}
val ema = outcomeDstream.updateStateByKey[Double](updateEMA _)
我遇到的问题是:如果我使用此函数来跟踪我的指数移动平均线,它将返回我:(name,expMovAvg)。但是我将丢失我的唯一密钥和时间戳。这个问题是我无法将这个ema-Dstream加入到我的原始流中,因为我的密钥现在只是不唯一的名称。
在updateStateByKey转换过程中是否有可能保留唯一键或时间戳?
答案 0 :(得分:1)
如果我正确理解您的问题,您可以使用Option[Double]
作为名称作为键的状态,而不是将updateStateByKey
保持为Option[Observation]
状态,其中包含您需要的所有独特数据:
val outcomeDstream: DStream[(String, Observation)] =
parsedstream.map { case (k: String, obs: Observation) => (obs.name, obs) }
def updateEMA(newValues: Seq[Observation],
oldCount: Option[Observation]): Option[Observation] = {
if (oldCount.isEmpty) newValues(0)
else Some((newValues(0).outcome * 0.25) + (oldCount.get.outcome * (0.75)))
}
作为旁注,如果您使用的是Spark 1.6.0,请考虑查看PairDStreamFunctions.mapWithState
。虽然语义稍有不同(它不会处理没有收到新值的键)但仍然是实验性的,it is superior in performance。