根据行

时间:2017-08-18 14:04:41

标签: scala apache-spark

如何将Rows插入已存在的数据集中。 显示的表格是我的数据的一个愚蠢的版本,我可以作为数据集使用。

每当两个连续行之间的时差跳过下一个整整一小时时,我想插入一个新行。除新时间之外的所有列都应与上面的行相同。

日期时间或日期&时间充足。我只是把它们放在我的例子中,以表明我可以访问所有这些。

我的一般想法是计算两行之间的小时跳过量,创建一个新数据集,将其与原始数据集连接,然后对其进行排序。

+--------+-------------------+----------+--------+
|  status|           datetime|      date|    time|
+--------+-------------------+----------+--------+
|   start|2017-01-01 07:15:12|2017-01-01|07:15:12|
|    init|2017-01-01 07:22:12|2017-01-01|07:22:12|
|A_status|2017-01-01 07:31:12|2017-01-01|07:31:12|
|B_status|2017-01-01 10:30:12|2017-01-01|10:30:12|
|C_Status|2017-01-01 11:15:12|2017-01-01|11:15:12|
+--------+-------------------+----------+--------+

+--------+-------------------+----------+--------+
|  status|           datetime|      date|    time|
+--------+-------------------+----------+--------+
|   start|2017-01-01 07:15:12|2017-01-01|07:15:12|
|    init|2017-01-01 07:22:12|2017-01-01|07:22:12|
|A_status|2017-01-01 07:31:12|2017-01-01|07:31:12|
|A_status|2017-01-01 08:00:00|2017-01-01|08:00:00|
|A_status|2017-01-01 09:00:00|2017-01-01|09:00:00|
|A_status|2017-01-01 10:00:00|2017-01-01|10:00:00|
|B_status|2017-01-01 10:30:12|2017-01-01|10:30:12|
|B_status|2017-01-01 11:00:00|2017-01-01|11:00:00|
|C_Status|2017-01-01 11:15:12|2017-01-01|11:15:12|
+--------+-------------------+----------+--------+

我的第一个想法是以小时计算时差,如果是> = 1,则在新数据集中生成该行数并将其连接到原始数据集。 问题是它没有检测到从B_status到C_status的跳过,因为它只有3/4小时。

val df9 = df3.withColumn("time_diff", ((unix_timestamp(lead($"datetime", 1).over(Window.orderBy("datetime"))) - unix_timestamp($"datetime"))/60/60)).show

我的下一个想法是提取时间字段的小时部分并减去它们。这将导致正确的线数,但需要额外处理从HH24到HH00的跳转。

我还读到了爆炸函数,因为这可能会生成具有相同数据的新行,但我还不知道这个函数是否适用于这种情况。

有人有任何提示或实施来帮助我吗?也许有一种更简单的方法来实现这一目标。祝周末愉快。

1 个答案:

答案 0 :(得分:1)

您可以使用explode功能获得所需的结果,但为此您需要hour, lead, Window, udf, unix_timestamp, select, SimpleDateFormat和更多功能的复杂组合,这些功能将在下面说明。

鉴于dataframe

+--------+-------------------+----------+--------+
|status  |datetime           |date      |time    |
+--------+-------------------+----------+--------+
|start   |2017-01-01 07:15:12|2017-01-01|07:15:12|
|init    |2017-01-01 07:22:12|2017-01-01|07:22:12|
|A_status|2017-01-01 07:31:12|2017-01-01|07:31:12|
|B_status|2017-01-01 10:30:12|2017-01-01|10:30:12|
|C_Status|2017-01-01 11:15:12|2017-01-01|11:15:12|
+--------+-------------------+----------+--------+

只有statusdatetime列非常重要,因为datetime columna可以从要更改的datetime列中派生出来。所以你必须只选择两个,并将上一行的小时差异作为

val df2 = df.select($"status", unix_timestamp($"datetime").cast(TimestampType).as("datetime"), (hour(lead($"datetime", 1).over(Window.orderBy("datetime"))) - hour($"datetime")).as("hour"))

应该给你

+--------+---------------------+----+
|status  |datetime             |hour|
+--------+---------------------+----+
|start   |2017-01-01 07:15:12.0|0   |
|init    |2017-01-01 07:22:12.0|0   |
|A_status|2017-01-01 07:31:12.0|3   |
|B_status|2017-01-01 10:30:12.0|1   |
|C_Status|2017-01-01 11:15:12.0|null|
+--------+---------------------+----+

既然您有小时差异,就可以从datetime值到小时差异日期时间获取日期时间数组,这可以通过以下方式完成将udf函数定义为

def getDiffDateArray = udf((date : String, hour: Int, value : Int) => {
  if((value - 1) > 0) {
    var array = Array.empty[String]
    for(time <- 0 to value){
      val format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
      val originalDate = format.parse(date)
      val calendar = Calendar.getInstance
      calendar.setTimeInMillis(originalDate.getTime)
      calendar.set(Calendar.HOUR_OF_DAY, hour+time)
      if(time != 0){
        calendar.set(Calendar.MINUTE, 0)
        calendar.set(Calendar.SECOND, 0)
      }
      array = array ++ Array(format.format(calendar.getTime))
    }
    array}
  else Array(date)
})

您可以使用日期时间数组上的explode函数,并通过执行以下操作获取datetime

def getTimeFromeDateTime = udf((date: String) =>{
  val parseFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  val timeFormat = new SimpleDateFormat("HH:mm:ss")
  val time = parseFormat.parse(date)
  timeFormat.format(time)
})

df2.withColumn("datetime", explode(getDiffDateArray($"datetime", hour($"datetime"), when($"hour".isNotNull, $"hour").otherwise(lit(0)))))
    .drop("hour")
    .withColumn("date", $"datetime".cast(DateType))
    .withColumn("time", getTimeFromeDateTime($"datetime"))
    .show(false)

这可以为您提供所需的结果

+--------+-------------------+----------+--------+
|status  |datetime           |date      |time    |
+--------+-------------------+----------+--------+
|start   |2017-01-01 07:15:12|2017-01-01|07:15:12|
|init    |2017-01-01 07:22:12|2017-01-01|07:22:12|
|A_status|2017-01-01 07:31:12|2017-01-01|07:31:12|
|A_status|2017-01-01 08:00:00|2017-01-01|08:00:00|
|A_status|2017-01-01 09:00:00|2017-01-01|09:00:00|
|A_status|2017-01-01 10:00:00|2017-01-01|10:00:00|
|B_status|2017-01-01 10:30:12|2017-01-01|10:30:12|
|C_Status|2017-01-01 11:15:12|2017-01-01|11:15:12|
+--------+-------------------+----------+--------+

我希望答案很有帮助