比较RDD的子集

时间:2016-01-04 19:32:07

标签: scala apache-spark

我正在寻找一种智能地比较RDD子集的方法。

假设我有一个RDD,其键/值对类型(Int-> T)。我最终需要说“将密钥1的所有值与密钥2的所有值进行比较,并将密钥3的值与密钥5和密钥7的值进行比较”,我将如何有效地执行此操作?

我目前正在考虑这样做的方法是创建一个过滤后的RDD列表,然后使用RDD.cartesian()

def filterSubset[T] = (b:Int, r:RDD[(Int, T)]) => r.filter{case(name, _) => name == b} 

Val keyPairs:(Int, Int) // all key pairs

Val rddPairs = keyPairs.map{

            case (a, b) =>

                filterSubset(a,r).cartesian(filterSubset(b,r))

        }

rddPairs.map{whatever I want to compare…}

然后我会迭代列表并在每对RDD上执行映射以收集我需要的关系数据。

我无法分辨这个想法的是,设置可能的数百个地图作业然后迭代它们是否效率极低。在这种情况下,spark中的懒惰估值会优化所有地图之间的数据混乱吗?如果没有,有人可以推荐一种可能更有效的方法来解决这个问题吗?

感谢您的帮助

2 个答案:

答案 0 :(得分:3)

解决此问题的一种方法是复制和分区数据以反映要比较的密钥对。让我们从创建两个映射开始,从实际键到我们将用于复制和连接的临时键:

def genMap(keys: Seq[Int]) = keys
  .zipWithIndex.groupBy(_._1)
  .map{case (k, vs) => (k -> vs.map(_._2))}

val left = genMap(keyPairs.map(_._1))
val right = genMap(keyPairs.map(_._2))

接下来,我们可以通过使用新密钥进行复制来转换数据:

def mapAndReplicate[T: ClassTag](rdd: RDD[(Int, T)], map: Map[Int, Seq[Int]]) = {
  rdd.flatMap{case (k, v) => map.getOrElse(k, Seq()).map(x => (x, (k, v)))}  
}

val leftRDD = mapAndReplicate(rddPairs, left)
val rightRDD = mapAndReplicate(rddPairs, right)

最后我们可以合作:

val cogrouped = leftRDD.cogroup(rightRDD)

比较/过滤对:

cogrouped.values.flatMap{case (xs, ys) => for {
  (kx, vx) <- xs
  (ky, vy) <- ys
  if cosineSimilarity(vx, vy) <= threshold
} yield ((kx, vx), (ky, vy)) }

显然,目前的形式这种方法是有限的。它假定任意一对密钥的值可以适合内存并需要大量的网络流量。它仍然应该让你知道如何继续。

另一种可能的方法是将数据存储在外部系统(例如数据库)中,并根据需要获取所需的键值对。

既然你试图找到元素之间的相似性,我也会考虑完全不同的方法。我会尝试使用自定义分区器来分割数据,而不是天真地比较逐个键,这反映了文档之间预期的相似性。它总的来说远非微不足道,但应该会给出更好的结果。

答案 1 :(得分:0)

使用Dataframe,您可以使用join轻松进行笛卡尔运算:

dataframe1.join(dataframe2, dataframe1("key")===dataframe2("key"))

它可能会完全符合您的要求,但效率却很高。

如果您不知道如何创建数据帧,请参阅http://spark.apache.org/docs/latest/sql-programming-guide.html#creating-dataframes