Scala 展开RDD并为新元素分配连续ID
我想消除Scala 展开RDD并为新元素分配连续ID,scala,apache-spark,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中,我会使用折叠式。有了Spark,我能想到的最好方法就是将平面映射到(Foo,Option[Long]),按过滤。2.isEm
Foo
的歧义。其中一些需要拆分为单独的实例。每个都需要一个唯一的、连续的Id
val maxId: Long = foos.map(_.id).max()
foos.flatMap { foo =>
if (foo.bar) List(foo, foo.copy(id = ???, ...))
else List(foo)
}
在普通Scala中,我会使用折叠式。有了Spark,我能想到的最好方法就是将平面映射到(Foo,Option[Long])
,按过滤。2.isEmpty
,zipWithIndex并加入。有更聪明的方法吗
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)。在任何分布式系统中,可以独立生成的标识优于顺序生成器
所以最好的方法是.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的
mapAccumL
和Lens
实现UpdadeIDs
可能会更简单,但我选择避免使用外部库。也许其他人可以理解您的问题,但我无法解决。通常,您在常规Scala集合上使用的所有典型操作(特别是monad操作)都可以在RDD上使用。我在问题中举了一个简洁的例子。@Synesso集合元素的顺序重要吗?@Odomontois不重要我假设zipWithIndex是分区的局部索引,对吗?所以这里的要点是计算每个分区要更改的数量并分配开始索引,将其广播到集群,然后每个分区使用自己的开始索引分配唯一的、连续的索引。我要试一试。@Synesso完全正确,如果你说的是zipWithIndex
你的意思是mapPartitionsWithIndex