web-dev-qa-db-fra.com

Comment regrouper par intervalle de temps dans Spark SQL

Mon jeu de données ressemble à ceci: 

KEY |Event_Type | metric | Time 
001 |event1     | 10     | 2016-05-01 10:50:51
002 |event2     | 100    | 2016-05-01 10:50:53
001 |event3     | 20     | 2016-05-01 10:50:55
001 |event1     | 15     | 2016-05-01 10:51:50
003 |event1     | 13     | 2016-05-01 10:55:30
001 |event2     | 12     | 2016-05-01 10:57:00
001 |event3     | 11     | 2016-05-01 11:00:01

Je veux tout avoir quand les clés qui vérifient ceci: 

"Somme de la métrique pour un événement spécifique" > seuil pendant 5 minutes

Cela me semble un candidat idéal pour utiliser le Fonctions coulissantes de Windows

Comment puis-je faire cela avec Spark SQL? 

Je vous remercie.

15
Nabil

Spark> = 2.0

Vous pouvez utiliser window (à ne pas confondre avec les fonctions de fenêtre). Selon la variante, il attribue un horodatage à un ou plusieurs compartiments potentiellement chevauchants:

df.groupBy($"KEY", window($"time", "5 minutes")).sum("metric")

// +---+---------------------------------------------+-----------+
// |KEY|window                                       |sum(metric)|
// +---+---------------------------------------------+-----------+
// |001|[2016-05-01 10:50:00.0,2016-05-01 10:55:00.0]|45         |
// |001|[2016-05-01 10:55:00.0,2016-05-01 11:00:00.0]|12         |
// |003|[2016-05-01 10:55:00.0,2016-05-01 11:00:00.0]|13         |
// |001|[2016-05-01 11:00:00.0,2016-05-01 11:05:00.0]|11         |
// |002|[2016-05-01 10:50:00.0,2016-05-01 10:55:00.0]|100        |
// +---+---------------------------------------------+-----------+

Spark <2.0

Commençons par des exemples de données:

import spark.implicits._  // import sqlContext.implicits._ in Spark < 2.0

val df = Seq(
  ("001", "event1", 10, "2016-05-01 10:50:51"),
  ("002", "event2", 100, "2016-05-01 10:50:53"),
  ("001", "event3", 20, "2016-05-01 10:50:55"),
  ("001", "event1", 15, "2016-05-01 10:51:50"),
  ("003", "event1", 13, "2016-05-01 10:55:30"),
  ("001", "event2", 12, "2016-05-01 10:57:00"),
  ("001", "event3", 11, "2016-05-01 11:00:01")
).toDF("KEY", "Event_Type", "metric", "Time")

Je suppose que cet événement est identifié par KEY. Si ce n'est pas le cas, vous pouvez ajuster les clauses GROUP BY/PARTITION BY en fonction de vos besoins.

Si vous êtes intéressé par une agrégation avec une fenêtre statique indépendante des données, convertissez les horodatages en un type de données numérique

import org.Apache.spark.sql.functions.{round, sum}

// cast string to timestamp
val ts = $"Time".cast("timestamp").cast("long")

// Round to 300 seconds interval
val interval = (round(ts / 300L) * 300.0).cast("timestamp").alias("interval")

df.groupBy($"KEY", interval).sum("metric")

// +---+---------------------+-----------+
// |KEY|interval             |sum(metric)|
// +---+---------------------+-----------+
// |001|2016-05-01 11:00:00.0|11         |
// |001|2016-05-01 10:55:00.0|12         |
// |001|2016-05-01 10:50:00.0|45         |
// |003|2016-05-01 10:55:00.0|13         |
// |002|2016-05-01 10:50:00.0|100        |
// +---+---------------------+-----------+

Si vous êtes intéressé par une fenêtre par rapport à la ligne actuelle, utilisez les fonctions de la fenêtre:

import org.Apache.spark.sql.expressions.Window

// Partition by KEY
// Order by timestamp 
// Consider window of -150 seconds to + 150 seconds relative to the current row
val w = Window.partitionBy($"KEY").orderBy("ts").rangeBetween(-150, 150)
df.withColumn("ts", ts).withColumn("window_sum", sum($"metric").over(w))

// +---+----------+------+-------------------+----------+----------+
// |KEY|Event_Type|metric|Time               |ts        |window_sum|
// +---+----------+------+-------------------+----------+----------+
// |003|event1    |13    |2016-05-01 10:55:30|1462092930|13        |
// |001|event1    |10    |2016-05-01 10:50:51|1462092651|45        |
// |001|event3    |20    |2016-05-01 10:50:55|1462092655|45        |
// |001|event1    |15    |2016-05-01 10:51:50|1462092710|45        |
// |001|event2    |12    |2016-05-01 10:57:00|1462093020|12        |
// |001|event3    |11    |2016-05-01 11:00:01|1462093201|11        |
// |002|event2    |100   |2016-05-01 10:50:53|1462092653|100       |
// +---+----------+------+-------------------+----------+----------+

Pour des raisons de performances, cette approche est utile uniquement si les données peuvent être partitionnées en plusieurs groupes distincts. Dans Spark <2.0.0, vous aurez également besoin de HiveContext pour que cela fonctionne.

28
zero323

Pour la limite statique, vous pouvez faire ce qui suit:

1) Transformer (map, mapPartitions, etc.) Valeur de temps pour former AAAA-MM-JJ-hh-mm, où mm est additionné au niveau 5 minutes. par exemple. 01, 02, 03, 05 devient 05; 16,17,18,19,20 devient 20

2) Effectuez un groupBy ou une reduction avec event_type et time et effectuez votre agrégation (Sum) sur des métriques

3) Effectuer une transformation de filtre pour filtrer les métriques> 5

Vous pouvez écrire ci-dessus dans spark rdd ou dataframe (sql) de la même manière.

Pour les autres types de limites, où 00-05, 01-06, 02-07, vous devriez essayer de regarder dans le concept de window window. Si le scénario d'utilisation de votre ingestion de données convient au modèle de diffusion en continu, alors l'API Spark Streaming sera parfaite, sinon vous pouvez trouver une solution personnalisée telle que celle-ci: Apache Spark - Traitement des fenêtres glissantes sur des RDD temporels

0
nir