Scala 将数据帧的一行拆分为多行

Scala 将数据帧的一行拆分为多行,scala,apache-spark,dataframe,Scala,Apache Spark,Dataframe,我想将数据帧中的一行转换为多行。如果小时数相同,则行不会被拆分,但如果小时数不同,则行将根据小时数的差异拆分为多行。我擅长使用dataframe函数或配置单元查询解决方案 输入表或数据帧 预期的输出表或数据帧 请帮助我找到预期输出的解决方案。对于这种简单模式,最简单的解决方案是在为输入和输出模式定义case类之后使用 一个简单的UDF解决方案将返回一个序列,然后您可以使用functions.explode。远不如使用flatMap干净高效 最后但并非最不重要的一点是,您可以创建自己的表来生

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

输入表或数据帧

预期的输出表或数据帧


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

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

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


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

您可以在映射操作中实现自己的逻辑,并使用flatMap来实现这一点

以下是我实施解决方案的粗略方式,您可以根据需要即兴创作


你能分享你已经尝试过的东西吗?用scala准备一个数据样本,而不是别人会用的屏幕截图,并尝试给出答案。如果你使用excel或屏幕截图,回答者会加倍努力。希望你能理解。非常感谢。它几乎没有改动。
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]