web-dev-qa-db-ja.com

Spark SQLで時間間隔でグループ化する方法

私のデータセットは次のようになります。

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

これを検証するキーがあればすべてを取得したい:

「特定のイベントのメトリックの合計」> threshold during 5 minutes

これはSliding Windows Functionsを使用するための完璧な候補のように見えます。

Spark SQLでこれを行うにはどうすればよいですか?

ありがとうございました。

18
Nabil

Spark> = 2.0

window を使用できます(ウィンドウ関数と間違えないでください)。バリアントに応じて、タイムスタンプを1つ以上の潜在的に重複するバケットに割り当てます。

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        |
// +---+---------------------------------------------+-----------+

スパーク<2.0

サンプルデータから始めましょう。

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")

イベントはKEYで識別されると仮定します。そうでない場合は、要件に応じてGROUP BY/PARTITION BY句を調整できます。

データに依存しない静的ウィンドウを使用した集計に関心がある場合は、タイムスタンプを数値データ型に変換して丸めます

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        |
// +---+---------------------+-----------+

現在の行に関連するウィンドウに関心がある場合は、ウィンドウ関数を使用します。

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       |
// +---+----------+------+-------------------+----------+----------+

パフォーマンス上の理由から、このアプローチは、データを複数の個別のグループに分割できる場合にのみ役立ちます。 Spark <2.0.0では、HiveContextも必要です。

34
zero323

静的境界の場合、次のことができます。

1)変換(マップ、mapPartitionsなど)時刻値YYYY-MM-DD-hh-mmを形成します。mmは5分レベルでロールアップされます。例えば01、02、03、05は05になります。 16,17,18,19,20は20になります

2)event_typeとtimeを使用してgroupByまたはreduceByを実行し、メトリックで集計(合計)を実行します

3)フィルター変換を実行して、メトリックをフィルター処理> 5

上記と同様にspark rddまたはdataframe(sql)で記述できます。

00-05、01-06、02-07である他のタイプの境界については、スライディングウィンドウの概念を検討してください。データ取り込みのユースケースがストリーミングパターンに適合する場合、Spark St​​reaming APIは完璧です。それ以外の場合は、次のようなカスタムソリューションを見つけることができます。 Apache Spark-対処テンポラリRDDでのウィンドウのスライド

0
nir