J'ai un énorme fichier en HDFS contenant des points de données de séries chronologiques (prix Yahoo Stock).
Je veux trouver la moyenne mobile de la série chronologique, comment dois-je faire pour écrire le travail Apache Spark pour le faire).
Vous pouvez utiliser la fonction coulissante de MLLIB qui fait probablement la même chose que la réponse de Daniel. Vous devrez trier les données par heure avant d'utiliser la fonction coulissante.
import org.Apache.spark.mllib.rdd.RDDFunctions._
sc.parallelize(1 to 100, 10)
.sliding(3)
.map(curSlice => (curSlice.sum / curSlice.size))
.collect()
La moyenne mobile est un problème délicat pour Spark et tout système distribué. Lorsque les données sont réparties sur plusieurs machines, certaines fenêtres temporelles traversent les partitions. Nous devons dupliquer les données au début des partitions, afin que le calcul de la moyenne mobile par partition donne une couverture complète.
Voici une façon de le faire dans Spark. Les données d'exemple:
val ts = sc.parallelize(0 to 100, 10)
val window = 3
Un simple partitionneur qui place chaque ligne dans la partition que nous spécifions par la clé:
class StraightPartitioner(p: Int) extends org.Apache.spark.Partitioner {
def numPartitions = p
def getPartition(key: Any) = key.asInstanceOf[Int]
}
Créez les données avec le premier window - 1
lignes copiées dans la partition précédente:
val partitioned = ts.mapPartitionsWithIndex((i, p) => {
val overlap = p.take(window - 1).toArray
val spill = overlap.iterator.map((i - 1, _))
val keep = (overlap.iterator ++ p).map((i, _))
if (i == 0) keep else keep ++ spill
}).partitionBy(new StraightPartitioner(ts.partitions.length)).values
Il suffit de calculer la moyenne mobile sur chaque partition:
val movingAverage = partitioned.mapPartitions(p => {
val sorted = p.toSeq.sorted
val olds = sorted.iterator
val news = sorted.iterator
var sum = news.take(window - 1).sum
(olds Zip news).map({ case (o, n) => {
sum += n
val v = sum
sum -= o
v
}})
})
En raison des segments en double, il n'y aura pas de lacunes dans la couverture.
scala> movingAverage.collect.sameElements(3 to 297 by 3)
res0: Boolean = true
Spark 1.4 a introduit les fonctions de fenêtrage , ce qui signifie que vous pouvez faire la moyenne mobile comme suit ajuster le fenêtrage avec des lignesEntre :
val schema = Seq("id", "cykle", "value")
val data = Seq(
(1, 1, 1),
(1, 2, 11),
(1, 3, 1),
(1, 4, 11),
(1, 5, 1),
(1, 6, 11),
(2, 1, 1),
(2, 2, 11),
(2, 3, 1),
(2, 4, 11),
(2, 5, 1),
(2, 6, 11)
)
val dft = sc.parallelize(data).toDF(schema: _*)
dft.select('*).show
// PARTITION BY id ORDER BY cykle ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING (5)
val w = Window.partitionBy("id").orderBy("cykle").rowsBetween(-2, 2)
val x = dft.select($"id",$"cykle",avg($"value").over(w))
x.show
Sortie (en zeppelin):
schema: Seq[String] = List(id, cykle, value)
data: Seq[(Int, Int, Int)] = List((1,1,1), (1,2,11), (1,3,1), (1,4,11), (1,5,1), (1,6,11), (2,1,1), (2,2,11), (2,3,1), (2,4,11), (2,5,1), (2,6,11))
dft: org.Apache.spark.sql.DataFrame = [id: int, cykle: int, value: int]
+---+-----+-----+
| id|cykle|value|
+---+-----+-----+
| 1| 1| 1|
| 1| 2| 11|
| 1| 3| 1|
| 1| 4| 11|
| 1| 5| 1|
| 1| 6| 11|
| 2| 1| 1|
| 2| 2| 11|
| 2| 3| 1|
| 2| 4| 11|
| 2| 5| 1|
| 2| 6| 11|
+---+-----+-----+
w: org.Apache.spark.sql.expressions.WindowSpec = org.Apache.spark.sql.expressions.WindowSpec@55cd666f
x: org.Apache.spark.sql.DataFrame = [id: int, cykle: int, 'avg(value) WindowSpecDefinition ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING: double]
+---+-----+-------------------------------------------------------------------------+
| id|cykle|'avg(value) WindowSpecDefinition ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING|
+---+-----+-------------------------------------------------------------------------+
| 1| 1| 4.333333333333333|
| 1| 2| 6.0|
| 1| 3| 5.0|
| 1| 4| 7.0|
| 1| 5| 6.0|
| 1| 6| 7.666666666666667|
| 2| 1| 4.333333333333333|
| 2| 2| 6.0|
| 2| 3| 5.0|
| 2| 4| 7.0|
| 2| 5| 6.0|
| 2| 6| 7.666666666666667|
+---+-----+————————————————————————————————————+