展开RDD并为新元素

时间:2015-11-23 02:23:29

标签: scala apache-spark

我希望消除Foo的歧义。其中一些需要拆分为单独的实例。每个都需要一个独特的,连续的Id。

val maxId: Long = foos.map(_.id).max()

foos.flatMap { foo =>
  if (foo.bar) List(foo, foo.copy(id = ???, ...))
  else List(foo)
}

在Scala中,我使用了foldLeft。使用Spark,我能想到的最好的是flatMap到(Foo, Option[Long]),过滤_._2.isEmpty,zipWithIndex和join。有更聪明的方法吗?

e.g。给定

case class Foo(id: Long) {
  val bar: Boolean = id % 2 == 1
}

此输入

RDD( Foo(1), Foo(2), Foo(3) )

应该成为

RDD( Foo(1), Foo(2), Foo(3), Foo(4), Foo(5) )

因为Foo(1)Foo(3)已展开并接受了下一个可用的ID(4& 5)。

1 个答案:

答案 0 :(得分:1)

在任何分布式系统中,可以彼此独立生成的标识优于顺序生成器。

好方法是.copy( id = randomLong ),最好的方式是.copy( id = UUID.randomUUID() )

但问题是关于连续 ID的具体问题。我对该案的提议是

import Numeric.Implicits._
import scala.reflect.ClassTag

abstract class UpdateIDS[T: ClassTag, Id: Numeric : ClassTag] extends Serializable {
  def getId(elem: T): Id
  def setId(elem: T, id: Id): T
  def shouldChange(elem: T): Boolean
  val Id = implicitly[Numeric[Id]]

  def apply(xs: RDD[T]): RDD[T] = {
    val next = xs.map(getId).max + Id.one
    val counts: Seq[(Int, Int)] = xs.mapPartitionsWithIndex { (idx, elems) =>
      Iterator.single(idx, elems.count(shouldChange))
    }.collect.view
    val starts = counts.map(_._2).map(Id.fromInt).scanLeft(next)(_ + _)
    val startMapLocal = counts.zip(starts).map { case ((idx, _), start) => (idx, start) }.toMap
    val startMap = xs.context.broadcast(startMapLocal)

    xs.mapPartitionsWithIndex { case (idx, elems) =>
      elems.scanLeft((List.empty[T], startMap.value(idx))) { (pair, elem) =>
        pair match {
          case (_, counter) if shouldChange(elem) => (List(elem, setId(elem, counter)), counter + Id.one)
          case (_, counter) => (List(elem), counter)
        }
      }.flatMap { _._1 }
    }
  }
}

你可以轻松定义

object fooUpdateId extends UpdateIDS[Foo, Int] {
  def getId(foo: Foo) = foo.id
  def setId(foo: Foo, id: Int) = foo.copy(id = id)
  def shouldChange(foo: Foo) = foo.id % 2 == 1
}

然后运行

val foosUpdated = fooUpdateId(foos)

重要提示,此处生成的集合的顺序会被更改,以获得更高效的解决方案。如果您需要在不那么大的RDD中订购,那么您可以使用sortBy

另请注意,使用scalaz中的UpdadeIDsmapAccumL可能会稍微简化Lens的实现,但我选择避免使用外部库。