繁体   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