使用带有spark的分布式计算的monoid的示例

时间:2015-06-18 07:44:27

标签: scala apache-spark

我有用户爱好数据( RDD [Map [String,Int]] ),如:

("food" -> 3, "music" -> 1),
("food" -> 2),
("game" -> 5, "twitch" -> 3, "food" -> 3)

我想计算它们的统计数据,并将数据表示为 Map [String,Array [Int]] ,而数组大小为5,如:

("food" -> Array(0, 1, 2, 0, 0),
 "music" -> Array(1, 0, 0, 0, 0),
 "game" -> Array(0, 0, 0, 0, 1),
 "twitch" -> Array(0, 0, 1, 0 ,0))

foldLeft 似乎是正确的解决方案,但RDD无法使用它,并且数据太大而无法转换为List / Array以使用 foldLeft ,我怎么能做这个工作?

1 个答案:

答案 0 :(得分:3)

诀窍是在示例中用一个类替换数组,该类包含您想要的某些数据部分的统计信息,并且可以与同一统计信息的另一个实例(覆盖数据的其他部分)组合提供有关整个数据的统计数据。

例如,如果你有一个覆盖数据3,3,2和5的统计数据,我会收集它看起来像(0, 1, 2, 0, 1),如果你有另一个实例覆盖数据3,4,4看起来像(0, 0, 1, 2,0)。现在,您所要做的就是定义一个+操作,让您组合(0, 1, 2, 0, 1) + (0, 0, 1, 2, 0) = (0,1,3,2,1),覆盖数据3,3,2,5和3,4,4。

让我们这样做,并打电话给班级StatMonoid

case class StatMonoid(flags: Seq[Int] = Seq(0,0,0,0,0)) {
    def + (other: StatMonoid) = 
        new StatMonoid( (0 to 4).map{idx => flags(idx) + other.flags(idx)})
}

此类包含5个计数器的序列,并定义一个+操作,使其与其他计数器组合。

我们还需要一个方便的方法来构建它,这可能是StatMonoid中的构造函数,在伴随对象中,或者只是一个简单的方法,如你所愿:

def stat(value: Int): StatMonoid = value match {
    case 1 => new StatMonoid(Seq(1,0,0,0,0))
    case 2 => new StatMonoid(Seq(0,1,0,0,0))
    case 3 => new StatMonoid(Seq(0,0,1,0,0))
    case 4 => new StatMonoid(Seq(0,0,0,1,0))
    case 5 => new StatMonoid(Seq(0,0,0,0,1))
    case _ => throw new RuntimeException("illegal init value: $value")
}

这使我们可以轻松计算覆盖单个数据的统计数据的实例,例如:

scala> stat(4)
res25: StatMonoid = StatMonoid(List(0, 0, 0, 1, 0))

它还允许我们通过添加它们将它们组合在一起:

scala> stat(1) + stat(2) + stat(2) + stat(5) + stat(5) + stat(5)
res18: StatMonoid = StatMonoid(Vector(1, 2, 0, 0, 3))

现在将此应用于您的示例,让我们假设我们将您提到的数据作为地图的RDD:

val rdd =  sc.parallelize(List(Map("food" -> 3, "music" -> 1), Map("food" -> 2), Map("game" -> 5, "twitch" -> 3, "food" -> 3)))

我们需要做的就是找到每种食物的统计数据,即将数据展平以获得(" foodId" - > id)元组,将每个id转换为{{{{ 1}}以上,最后将它们组合在一起用于各种食物:

StatMonoid

哪个收益率:

import org.apache.spark.rdd.PairRDDFunctions
rdd.flatMap(_.toList).mapValue(stat).reduceByKey(_ + _).collect

现在,对于旁边的故事,如果你想知道为什么我打电话给班级res24: Array[(String, StatMonoid)] = Array((game,StatMonoid(List(0, 0, 0, 0, 1))), (twitch,StatMonoid(List(0, 0, 1, 0, 0))), (music,StatMonoid(List(1, 0, 0, 0, 0))), (food,StatMonoid(Vector(0, 1, 2, 0, 0)))) ,那只是因为......它一个幺半群:D,和一个非常常见且方便的,称为产品。简而言之,幺半群只是可以以关联方式相互组合的东西,它们在Spark中开发时非常常见,因为它们自然地定义了可以在分布式从站上并行执行的操作,并聚集在一起形成最终结果。