簡體   English   中英

如何根據Apache Spark中的條件將數據拆分為系列

[英]How to split data into series based on conditions in Apache Spark

我有以下格式的數據,按時間戳排序,每行代表一個事件:

+----------+--------+---------+
|event_type|  data  |timestamp|
+----------+--------+---------+
|     A    |    d1  |    1    |
|     B    |    d2  |    2    |
|     C    |    d3  |    3    |
|     C    |    d4  |    4    |
|     C    |    d5  |    5    |
|     A    |    d6  |    6    |
|     A    |    d7  |    7    |
|     B    |    d8  |    8    |
|     C    |    d9  |    9    |
|     B    |    d10 |    12   |
|     C    |    d11 |    20   |
+----------+--------+---------+

我需要將這些事件收集成系列,如下所示:
1. C類事件標志着系列的結束
2. 如果有多個連續的 C 類事件,它們屬於同一個系列,最后一個標志着該系列的結束
3.每個系列的產品可跨越7天,即使沒有C事件結束它

另請注意,一天內可以有多個系列。 實際上,時間戳列是標准的 UNIX 時間戳,為了簡單起見,這里讓數字表示天數。

因此所需的輸出如下所示:

+---------------------+--------------------------------------------------------------------+
|first_event_timestamp|                events: List[(event_type, data,  timestamp)]        |
+---------------------+--------------------------------------------------------------------+
|          1          | List((A, d1, 1), (B, d2, 2), (C, d3, 3),  (C, d4, 4), (C, d5, 5))  |
|          6          | List((A, d6, 6), (A, d7, 7), (B, d8, 8),  (C, d9, 9))              |
|          12         | List((B, d10, 12))                                                 |
|          20         | List((C, d11, 20))                                                 |
+---------------------+--------------------------------------------------------------------+

我嘗試使用 Window 函數來解決這個問題,我將在其中添加 2 列,如下所示:
1. 種子列標記事件直接在類型 C 的事件之后使用一些唯一的 id
2. SeriesId 由來自 Seed 列的值填充,使用 last() 將一個系列中的所有事件標記為具有相同的 id
3. 然后我會按 SeriesId 對事件進行分組

不幸的是,這似乎不可能:

+----------+--------+---------+------+-----------+
|event_type|  data  |timestamp| seed | series_id |
+----------+--------+---------+------+-----------+
|     A    |    d1  |    1    | null |    null   |
|     B    |    d2  |    2    | null |    null   |
|     C    |    d3  |    3    | null |    null   |
|     C    |    d4  |    4    |   0  |     0     |     
|     C    |    d5  |    5    |   1  |     1     |
|     A    |    d6  |    6    |   2  |     2     |
|     A    |    d7  |    7    | null |     2     |
|     B    |    d8  |    8    | null |     2     |
|     C    |    d9  |    9    | null |     2     |
|     B    |    d10 |    12   |   3  |     3     |
|     C    |    d11 |    20   | null |     3     |
+----------+--------+---------+------+-----------+
  1. 我似乎無法使用 lag() 測試前一行是否相等,即以下代碼:
df.withColumn(
    "seed",
    when(
        (lag($"eventType", 1) === ventType.Conversion).over(w), 
        typedLit(DigestUtils.sha256Hex("some fields").substring(0, 32))
    )
)

投擲

org.apache.spark.sql.AnalysisException: 窗口函數不支持表達式 '(lag(eventType#76, 1, null) = C)'。

  1. 如表所示,它在有多個連續 C 事件的情況下失敗,並且也不適用於第一個和最后一個系列。

我有點卡在這里,任何幫助將不勝感激(使用 Dataframe/dataset api 是首選)。

這是方法

  1. 根據條件確定事件系列的開始
  2. 將記錄標記為開始事件
  3. 選擇開始事件的記錄
  4. 獲取記錄結束日期(如果我們對開始事件記錄進行排序,則之前的開始時間將是當前結束系列時間)
  5. 加入原始數據,與上述數據集

這是 udf 將記錄標記為“開始”

//tag the starting event, based on the conditions
 def tagStartEvent : (String,String,Int,Int) => String = (prevEvent:String,currEvent:String,prevTimeStamp:Int,currTimeStamp:Int)=>{
   //very first event is tagged as "start"
   if (prevEvent == "start")
     "start"
   else if ((currTimeStamp - prevTimeStamp) > 7 )
     "start"
   else {
     prevEvent match {
       case "C" =>
         if (currEvent == "A")
           "start"
         else if (currEvent == "B")
           "start"
         else // if current event C
           ""
       case _ => ""
     }
   }
 }
val tagStartEventUdf = udf(tagStartEvent)

數據.csv

event_type,data,timestamp
A,d1,1
B,d2,2
C,d3,3
C,d4,4
C,d5,5
A,d6,6
A,d7,7
B,d8,8
C,d9,9
B,d10,12
C,d11,20
val df = spark.read.format("csv")
                  .option("header", "true")
                  .option("inferSchema", "true")
                  .load("data.csv")

    val window = Window.partitionBy("all").orderBy("timestamp")

    //tag the starting event
    val dfStart =
        df.withColumn("all", lit(1))
          .withColumn("series_start",
            tagStartEventUdf(
              lag($"event_type",1, "start").over(window), df("event_type"),
              lag($"timestamp",1,1).over(window),df("timestamp")))

    val dfStartSeries = dfStart.filter($"series_start" === "start").select(($"timestamp").as("series_start_time"),$"all")

    val window2 = Window.partitionBy("all").orderBy($"series_start_time".desc)
    //get the series end times
    val dfSeriesTimes = dfStartSeries.withColumn("series_end_time",lag($"series_start_time",1,null).over(window2)).drop($"all")

    val dfSeries =
          df.join(dfSeriesTimes).withColumn("timestamp_series",
              // if series_end_time is null and  timestamp >= series_start_time, then series_start_time
              when(col("series_end_time").isNull && col("timestamp") >= col("series_start_time"), col("series_start_time"))
                // if record greater or equal to series_start_time, and less than series_end_time, then series_start_time
                .otherwise(when((col("timestamp") >= col("series_start_time") && col("timestamp") < col("series_end_time")), col("series_start_time")).otherwise(null)))
                .filter($"timestamp_series".isNotNull)

   dfSeries.show()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM