【问题标题】:Split one row into multiple rows of dataframe将一行拆分为多行数据框
【发布时间】:2019-07-03 20:17:09
【问题描述】:

我想将数据帧中的一行转换为多行。如果小时数相同,则行不会被拆分,但如果小时数不同,则行将拆分为多行 wrt 小时之间的差异。我擅长使用数据框函数或配置单元查询的解决方案。

输入表或数据框


预期的输出表或数据框


请帮助我找到预期输出的解决方法。

【问题讨论】:

  • 你能分享你已经尝试过的吗?
  • 使用 scala 准备数据样本,而不是屏幕截图,有人会使用它并尝试给出答案。如果你把excel或屏幕截图放在回答者身上,它会加倍努力。希望你能理解。

标签: scala apache-spark dataframe


【解决方案1】:

对于这种简单模式,最简单的解决方案是在为输入和输出模式定义案例类之后使用Dataset.flatMap

一个简单的 UDF 解决方案将返回一个序列,然后您可以使用 functions.explode。远不如使用flatMap 干净和高效。

最后但并非最不重要的一点是,您可以创建自己的表生成 UDF,但这对于这个问题来说太过分了。

【讨论】:

    【解决方案2】:

    您可以在map 操作中实现自己的逻辑,并使用flatMap 来实现。

    以下是粗略的方式,我已经实现了解决方案,你可以根据需要即兴发挥。

    import java.time.format.DateTimeFormatter
    import java.time.temporal.ChronoUnit
    import java.time.{Duration, LocalDateTime}
    
    import org.apache.spark.sql.Row
    
    import scala.collection.mutable.ArrayBuffer
    
    import sparkSession.sqlContext.implicits._
    
    val df = Seq(("john", "2/9/2018", "2/9/2018 5:02", "2/9/2018 5:12"),
        ("smit", "3/9/2018", "3/9/2018 6:12", "3/9/2018 8:52"),
        ("rick", "4/9/2018", "4/9/2018 23:02", "5/9/2018 2:12")
      ).toDF("UserName", "Date", "start_time", "end_time")
    
    val rdd = df.rdd.map(row => {
      val result = new ArrayBuffer[Row]()
      val formatter1 = DateTimeFormatter.ofPattern("d/M/yyyy H:m")
      val formatter2 = DateTimeFormatter.ofPattern("d/M/yyyy H:mm")
    
      val d1 = LocalDateTime.parse(row.getAs[String]("start_time"), formatter1)
      val d2 = LocalDateTime.parse(row.getAs[String]("end_time"), formatter1)
    
      if (d1.getHour == d2.getHour) result += row
      else {
        val hoursDiff = Duration.between(d1, d2).toHours.toInt
    
        result += Row.fromSeq(Seq(
          row.getAs[String]("UserName"),
          row.getAs[String]("Date"),
          row.getAs[String]("start_time"),
          d1.plus(1, ChronoUnit.HOURS).withMinute(0).format(formatter2)))
    
        for (index <- 1 until hoursDiff) {
          result += Row.fromSeq(Seq(
            row.getAs[String]("UserName"),
            row.getAs[String]("Date"),
            d1.plus(index, ChronoUnit.HOURS).withMinute(0).format(formatter1),
            d1.plus(1 + index, ChronoUnit.HOURS).withMinute(0).format(formatter2)))
        }
    
        result += Row.fromSeq(Seq(
          row.getAs[String]("UserName"),
          row.getAs[String]("Date"),
          d2.withMinute(0).format(formatter2),
          row.getAs[String]("end_time")))
      }
      result
    }).flatMap(_.toIterator)
    
    rdd.collect.foreach(println)
    

    最后,你的结果如下:

    [john,2/9/2018,2/9/2018 5:02,2/9/2018 5:12]
    [smit,3/9/2018,3/9/2018 6:12,3/9/2018 7:00]
    [smit,3/9/2018,3/9/2018 7:0,3/9/2018 8:00]
    [smit,3/9/2018,3/9/2018 8:00,3/9/2018 8:52]
    [rick,4/9/2018,4/9/2018 23:02,5/9/2018 0:00]
    [rick,4/9/2018,5/9/2018 0:0,5/9/2018 1:00]
    [rick,4/9/2018,5/9/2018 1:0,5/9/2018 2:00]
    [rick,4/9/2018,5/9/2018 2:00,5/9/2018 2:12]
    

    【讨论】:

    • 非常感谢。它只需少量修改即可工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2021-12-04
    • 2019-05-20
    • 1970-01-01
    相关资源
    最近更新 更多