将一行拆分为多行数据框

时间:2019-06-10 08:37:16

标签: scala apache-spark dataframe

我想将数据帧的一行转换为多行。如果小时数相同,则行将不会拆分,但如果小时数不同,则行将分裂为多行,而小时数之间会有所不同。我很喜欢使用dataframe函数或hive查询的解决方案。

输入表或数据框
enter image description here


预期输出表或数据框 enter image description here


请帮助我获得预期输出的解决方法。

2 个答案:

答案 0 :(得分:2)

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

一个简单的UDF解决方案将返回一个序列,然后您可以使用functions.explode。与使用flatMap相比,它的清洁度和效率要低得多。

最后但并非最不重要的一点是,您可以创建自己的生成表的UDF,但这对于解决此问题而言可能是非常过分的。

答案 1 :(得分: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]