Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala Spark:基于前几行中的开始时间和持续时间值,以30分钟为间隔计算事件结束时间_Scala_Apache Spark_Dataframe_Hadoop_Apache Spark Sql - Fatal编程技术网

Scala Spark:基于前几行中的开始时间和持续时间值,以30分钟为间隔计算事件结束时间

Scala Spark:基于前几行中的开始时间和持续时间值,以30分钟为间隔计算事件结束时间,scala,apache-spark,dataframe,hadoop,apache-spark-sql,Scala,Apache Spark,Dataframe,Hadoop,Apache Spark Sql,我有一个带有event_time字段的文件,每个记录每30分钟生成一次,并指示事件持续了多少秒。 例如: 我需要将连续事件转换为只有持续时间的事件。输出文件应如下所示: Event_time_start | event_time_end | event_duration_seconds 09:00 | 11:00 | 5300 12:00 | 12:30 | 1000 13:00 | 13:30

我有一个带有event_time字段的文件,每个记录每30分钟生成一次,并指示事件持续了多少秒。 例如:

我需要将连续事件转换为只有持续时间的事件。输出文件应如下所示:

Event_time_start | event_time_end | event_duration_seconds
09:00            | 11:00          | 5300
12:00            | 12:30          | 1000
13:00            | 13:30          | 1000
在Scala Spark中是否有方法将数据帧记录与下一个数据帧记录进行比较


我尝试使用
foreach
循环,但这不是一个好的选择,因为它需要处理大量的数据

这不是一个小问题,但这里有一个解决方案,其步骤如下:

  • 创建一个UDF,使用
    java.time
    API计算下一个最近的30分钟事件结束时间
    event\ts\u end
  • 对前一行的事件时间使用窗口功能
    lag
  • 如果与前一行的事件时间差为30分钟,则使用
    when/other
    生成带有
    null
    值的列
    event\u tsu\u start
  • 使用窗口函数
    last(event\u ts\u start,ignoreNulls=true)
    null
    s与上次
    event\u start
    值回填
  • 事件开始
    对数据进行分组,以聚合
    事件持续时间
    事件结束
  • 首先,让我们组装一个示例数据集:

    import org.apache.spark.sql.functions._
    import org.apache.spark.sql.expressions.Window
    import spark.implicits._
    
    val df = Seq(
      (101, "2019-04-01 09:00", 800),
      (101, "2019-04-01 09:30", 1800),
      (101, "2019-04-01 10:00", 2700),
      (101, "2019-04-01 12:00", 1000),
      (101, "2019-04-01 13:00", 1000),
      (220, "2019-04-02 10:00", 1500),
      (220, "2019-04-02 10:30", 2400)
    ).toDF("event_id", "event_time", "event_duration")
    
    请注意,示例数据集已略微泛化为包含多个事件,并使事件时间包含
    date
    info以涵盖跨越给定日期的事件的情况

    步骤
    1

    import java.sql.Timestamp
    
    def get_next_closest(seconds: Int) = udf{ (ts: Timestamp, duration: Int) =>
      import java.time.LocalDateTime
      import java.time.format.DateTimeFormatter
    
      val iter = Iterator.iterate(ts.toLocalDateTime)(_.plusSeconds(seconds)).
        dropWhile(_.isBefore(ts.toLocalDateTime.plusSeconds(duration)))
    
      iter.next.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
    }
    
    val winSpec = Window.partitionBy("event_id").orderBy("event_time")
    
    val seconds = 30 * 60
    
    df.
      withColumn("event_ts", to_timestamp($"event_time", "yyyy-MM-dd HH:mm")).
      withColumn("event_ts_end", get_next_closest(seconds)($"event_ts", $"event_duration")).
      withColumn("prev_event_ts", lag($"event_ts", 1).over(winSpec)).
      withColumn("event_ts_start",  when($"prev_event_ts".isNull ||
        unix_timestamp($"event_ts") - unix_timestamp($"prev_event_ts") =!= seconds, $"event_ts"
      )).
      withColumn("event_ts_start", last($"event_ts_start", ignoreNulls=true).over(winSpec)).
      groupBy($"event_id", $"event_ts_start").agg(
        sum($"event_duration").as("event_duration"), max($"event_ts_end").as("event_ts_end")
      ).show
    // +--------+-------------------+--------------+-------------------+
    // |event_id|     event_ts_start|event_duration|       event_ts_end|
    // +--------+-------------------+--------------+-------------------+
    // |     101|2019-04-01 09:00:00|          5300|2019-04-01 11:00:00|
    // |     101|2019-04-01 12:00:00|          1000|2019-04-01 12:30:00|
    // |     101|2019-04-01 13:00:00|          1000|2019-04-01 13:30:00|
    // |     220|2019-04-02 10:00:00|          3900|2019-04-02 11:30:00|
    // +--------+-------------------+--------------+-------------------+
    
    步骤
    2-5

    import java.sql.Timestamp
    
    def get_next_closest(seconds: Int) = udf{ (ts: Timestamp, duration: Int) =>
      import java.time.LocalDateTime
      import java.time.format.DateTimeFormatter
    
      val iter = Iterator.iterate(ts.toLocalDateTime)(_.plusSeconds(seconds)).
        dropWhile(_.isBefore(ts.toLocalDateTime.plusSeconds(duration)))
    
      iter.next.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
    }
    
    val winSpec = Window.partitionBy("event_id").orderBy("event_time")
    
    val seconds = 30 * 60
    
    df.
      withColumn("event_ts", to_timestamp($"event_time", "yyyy-MM-dd HH:mm")).
      withColumn("event_ts_end", get_next_closest(seconds)($"event_ts", $"event_duration")).
      withColumn("prev_event_ts", lag($"event_ts", 1).over(winSpec)).
      withColumn("event_ts_start",  when($"prev_event_ts".isNull ||
        unix_timestamp($"event_ts") - unix_timestamp($"prev_event_ts") =!= seconds, $"event_ts"
      )).
      withColumn("event_ts_start", last($"event_ts_start", ignoreNulls=true).over(winSpec)).
      groupBy($"event_id", $"event_ts_start").agg(
        sum($"event_duration").as("event_duration"), max($"event_ts_end").as("event_ts_end")
      ).show
    // +--------+-------------------+--------------+-------------------+
    // |event_id|     event_ts_start|event_duration|       event_ts_end|
    // +--------+-------------------+--------------+-------------------+
    // |     101|2019-04-01 09:00:00|          5300|2019-04-01 11:00:00|
    // |     101|2019-04-01 12:00:00|          1000|2019-04-01 12:30:00|
    // |     101|2019-04-01 13:00:00|          1000|2019-04-01 13:30:00|
    // |     220|2019-04-02 10:00:00|          3900|2019-04-02 11:30:00|
    // +--------+-------------------+--------------+-------------------+
    

    我想您正在寻找scala中的窗口函数:
    event\u time\u end
    是如何计算的?谢谢您的帮助,我正在尝试这个解决方案,但有一些问题,我想这是因为我们的scala版本是1。6@mabe在Spark 2.2之前,
    到_时间戳
    不可用。您可以将
    替换为\u时间戳($“event\u time”,“yyyy-MM-dd HH:MM”)
    替换为\u unixtime(unix\u时间戳($“event\u time”,“yyyy-MM-dd HH:MM”)。嘿@Leo,我尝试了您的解决方案,将其调整为spark 1.6.0,我就快到了!!但是最后一个问题是函数last。我正在使用的sql库不支持ignoreNulls参数,因此在尝试设置事件开始值时无法筛选空值。你能帮我选择这个步骤吗。我想这是目前为止我唯一缺少的东西。非常感谢。你好我终于可以解决这个问题了。我不必编写UDF来计算结束时间,我可以在配置单元上下文中的SQLText中运行日期计算。最困难的部分是聚合记录,但要识别连续块。感谢您的帮助。这是代码`val newDF:DataFrame=records.withColumn(“dtu事件”start),when((lag(records(“dtu real”).over(winSpec”).isNull)或(lag(records(“dt\u real”).over(winSpec)!==records(“dt\u prv\u calc”)、records(“dt\u real”))。withColumn(“dt_event_start”,org.apache.spark.sql.functions.max(“dt_event_start”).over(winSpec))。groupBy(“dia”、“eutrancellfdd”、“dt\U事件开始”).agg(“dt\U nxt\U计算”->“最大”、“注册”->“总和”)`