spark mapPartitionsWithIndex处理空分区

时间:2017-01-04 22:19:25

标签: scala apache-spark null rdd partition

如何在mapPartitionsWithIndex处理空分区?

可以找到完整的示例:https://gist.github.com/geoHeil/6a23d18ccec085d486165089f9f430f2

我的目标是通过RDD将最后一个众所周知的值填充为nan值,作为Spark / Scala: fill nan with last good observation的改进。

但是有些分区不包含任何值:

###################### carry 
Map(2 -> None, 5 -> None, 4 -> None, 7 -> Some(FooBar(2016-01-04,lastAssumingSameDate)), 1 -> Some(FooBar(2016-01-01,first)), 3 -> Some(FooBar(2016-01-02,second)), 6 -> None, 0 -> None)
(2,None)
(5,None)
(4,None)
(7,Some(FooBar(2016-01-04,lastAssumingSameDate)))
(1,Some(FooBar(2016-01-01,first)))
(3,Some(FooBar(2016-01-02,second)))
(6,None)
(0,None)
()
###################### carry 

case class FooBar(foo: Option[Date], bar: String)
val myDf = Seq(("2016-01-01", "first"), ("2016-01-02", "second"),
    ("2016-wrongFormat", "noValidFormat"),
    ("2016-01-04", "lastAssumingSameDate"))
    .toDF("foo", "bar")
    .withColumn("foo", 'foo.cast("Date"))
    .as[FooBar]
def notMissing(row: Option[FooBar]): Boolean = row.isDefined && row.get.foo.isDefined
myDf.rdd.filter(x => notMissing(Some(x))).count
val toCarry: Map[Int, Option[FooBar]] = myDf.rdd.mapPartitionsWithIndex { case (i, iter) => Iterator((i, iter.filter(x => notMissing(Some(x))).toSeq.lastOption)) }.collectAsMap

使用时

val toCarryBd = spark.sparkContext.broadcast(toCarry)
def fill(i: Int, iter: Iterator[FooBar]): Iterator[FooBar] = {
    if (iter.isEmpty) {
      iter
    } else {
      var lastNotNullRow: Option[FooBar] = toCarryBd.value.get(i).get
      iter.map(foo => {
        println("original ", foo)
        if (!notMissing(Some(foo))) {
          println("replaced")
          // this will go into the default case
          // FooBar(lastNotNullRow.getOrElse(FooBar(Option(Date.valueOf("2016-01-01")), "DUMMY")).foo, foo.bar)
          FooBar(lastNotNullRow.get.foo, foo.bar) // TODO warning this throws an error
        } else {
          lastNotNullRow = Some(foo)
          foo
        }
      })
    }
  }

  val imputed: RDD[FooBar] = myDf.rdd.mapPartitionsWithIndex { case (i, iter) => fill(i, iter) }

填写它会崩溃的值。

修改

如果应用答案输入,则输出

。仍然不是100%那里

+----------+--------------------+
|       foo|                 bar|
+----------+--------------------+
|2016-01-01|               first|
|2016-01-02|              second|
|2016-01-04|       noValidFormat|
|2016-01-04|lastAssumingSameDate|
+----------+--------------------+

1 个答案:

答案 0 :(得分:1)

就处理mapPartitions(和类似的)时处理空分区而言,一般方法是在输入空迭代器时返回正确类型的空迭代器。

看起来您的代码正在执行此操作,但是您的应用程序逻辑中似乎可能存在错误(即,假设如果某个分区的记录缺少某个值,则它将具有相同的前一行分区很好或者前一个分区不是空的并且有一个好的行 - 不一定是这种情况。您通过遍历并为每个分区收集上一个上一个好的值来部分修复此问题,然后如果您在分区的开头没有一个好的值,则查找收集的数组中的值

但是,如果这也发生在上一个分区为空的同时,则需要查找上一个上一个分区值,直到找到您要查找的分区值。 (请注意,假设数据集中的第一条记录有效,如果它不是您的代码仍会失败)。

你的解决方案非常接近工作,但只是有一些小的假设,而这些假设并不总是存在。