从下面分析可以看出,是先做了hash计算,然后使用hash join table来讲hash值相等的数据合并在一起。然后再使用udf计算距离,最后再filter出满足阈值的数据:

  1. 参考:https://github.com/apache/spark/blob/master/mllib/src/main/scala/org/apache/spark/ml/feature/LSH.scala
  1. /**
  2. * Join two datasets to approximately find all pairs of rows whose distance are smaller than
  3. * the threshold. If the [[outputCol]] is missing, the method will transform the data; if the
  4. * [[outputCol]] exists, it will use the [[outputCol]]. This allows caching of the transformed
  5. * data when necessary.
  6. *
  7. * @param datasetA One of the datasets to join.
  8. * @param datasetB Another dataset to join.
  9. * @param threshold The threshold for the distance of row pairs.
  10. * @param distCol Output column for storing the distance between each pair of rows.
  11. * @return A joined dataset containing pairs of rows. The original rows are in columns
  12. * "datasetA" and "datasetB", and a column "distCol" is added to show the distance
  13. * between each pair.
  14. */
  15. def approxSimilarityJoin(
  16. datasetA: Dataset[_],
  17. datasetB: Dataset[_],
  18. threshold: Double,
  19. distCol: String): Dataset[_] = {
  20.  
  21. val leftColName = "datasetA"
  22. val rightColName = "datasetB"
  23. val explodeCols = Seq("entry", "hashValue")
  24. val explodedA = processDataset(datasetA, leftColName, explodeCols)
  25.  
  26. // If this is a self join, we need to recreate the inputCol of datasetB to avoid ambiguity.
  27. // TODO: Remove recreateCol logic once SPARK-17154 is resolved.
  28. val explodedB = if (datasetA != datasetB) {
  29. processDataset(datasetB, rightColName, explodeCols)
  30. } else {
  31. val recreatedB = recreateCol(datasetB, $(inputCol), s"${$(inputCol)}#${Random.nextString(5)}")
  32. processDataset(recreatedB, rightColName, explodeCols)
  33. }
  34.  
  35. // Do a hash join on where the exploded hash values are equal.
  36. val joinedDataset = explodedA.join(explodedB, explodeCols)
  37. .drop(explodeCols: _*).distinct()
  38.  
  39. // Add a new column to store the distance of the two rows.
  40. val distUDF = udf((x: Vector, y: Vector) => keyDistance(x, y), DataTypes.DoubleType)
  41. val joinedDatasetWithDist = joinedDataset.select(col("*"),
  42. distUDF(col(s"$leftColName.${$(inputCol)}"), col(s"$rightColName.${$(inputCol)}")).as(distCol)
  43. )
  44.  
  45. // Filter the joined datasets where the distance are smaller than the threshold.
  46. joinedDatasetWithDist.filter(col(distCol) < threshold)
  47. }

补充:

sql join 算法 时间复杂度

2016年08月26日 12:04:34 stevewongbuaa 阅读数 2477
 

参考

stackoverflow

笔记

sql语句如下:

  1. SELECT T1.name, T2.date
  2. FROM T1, T2
  3. WHERE T1.id=T2.id
  4. AND T1.color='red'
  5. AND T2.type='CAR'

假设T1有m行,T2有n行,那么,普通情况下,应该要遍历T1的每一行的id(m),然后在遍历T2(n)中找出T2.id = T1.id的行进行join。时间复杂度应该是O(m*n)

如果没有索引的话,engine会选择hash join或者merge join进行优化。

hash join是这样的:

  1. 选择被哈希的表,通常是小一点的表。让我们愉快地假定是T1更小吧。
  2. T1所有的记录都被遍历。如果记录符合color=’red’,这条记录就会进去哈希表,以id为key,以name为value。
  3. T2所有的记录被遍历。如果记录符合type=’CAR’,使用这条记录的id去搜索哈希表,所有命中的记录的name的值,都被返回,还带上了当前记录的date的值,这样就可以把两者join起来了。

时间复杂度O(n+m),实现hash表是O(n),hash表查找是O(m),直接将其相加。

merge join是这样的:

1.复制T1(id, name),根据id排序。
2.复制T2(id, date),根据id排序。
3.两个指针指向两个表的最小值。

  1. >1 2<
  2. 2 3
  3. 2 4
  4. 3 5

4.在循环中比较指针,如果match,就返回记录。如果不match,指向较小值的指针指向下一个记录。

  1. >1 2< - match, 左指针小,左指针++
  2. 2 3
  3. 2 4
  4. 3 5
  5. 1 2< - match, 返回记录,两个指针都++
  6. >2 3
  7. 2 4
  8. 3 5
  9. 1 2 - match, 返回记录,两个指针都++
  10. 2 3<
  11. 2 4
  12. >3 5
  13. 1 2 - 左指针越界,查询结束。
  14. 2 3
  15. 2 4<
  16. 3 5
  17. >

时间复杂度O(n*log(n)+m*log(m))。排序算法的复杂度分别是O(n*log(n))和O(m*log(m)),直接将两者相加。

在这种情况下,使查询更加复杂反而可以加快速度,因为更少的行需要经受join-level的测试?

当然了。

如果原来的query没有where语句,如

  1. SELECT T1.name, T2.date
  2. FROM T1, T2

是更简单的,但是会返回更多的结果并运行更长的时间。

  

hash函数的补充:

  1. 可以看到 hashFunction 涉及到indices 字段下表的计算。另外的distance计算使用了jaccard相似度。

from:https://github.com/apache/spark/blob/master/mllib/src/main/scala/org/apache/spark/ml/feature/MinHashLSH.scala

  1. /**
  2. * :: Experimental ::
  3. *
  4. * Model produced by [[MinHashLSH]], where multiple hash functions are stored. Each hash function
  5. * is picked from the following family of hash functions, where a_i and b_i are randomly chosen
  6. * integers less than prime:
  7. * `h_i(x) = ((x \cdot a_i + b_i) \mod prime)`
  8. *
  9. * This hash family is approximately min-wise independent according to the reference.
  10. *
  11. * Reference:
  12. * Tom Bohman, Colin Cooper, and Alan Frieze. "Min-wise independent linear permutations."
  13. * Electronic Journal of Combinatorics 7 (2000): R26.
  14. *
  15. * @param randCoefficients Pairs of random coefficients. Each pair is used by one hash function.
  16. */
  17. @Experimental
  18. @Since("2.1.0")
  19. class MinHashLSHModel private[ml](
  20. override val uid: String,
  21. private[ml] val randCoefficients: Array[(Int, Int)])
  22. extends LSHModel[MinHashLSHModel] {
  23.  
  24. /** @group setParam */
  25. @Since("2.4.0")
  26. override def setInputCol(value: String): this.type = super.set(inputCol, value)
  27.  
  28. /** @group setParam */
  29. @Since("2.4.0")
  30. override def setOutputCol(value: String): this.type = super.set(outputCol, value)
  31.  
  32. @Since("2.1.0")
  33. override protected[ml] def hashFunction(elems: Vector): Array[Vector] = {
  34. require(elems.numNonzeros > 0, "Must have at least 1 non zero entry.")
  35. val elemsList = elems.toSparse.indices.toList
  36. val hashValues = randCoefficients.map { case (a, b) =>
  37. elemsList.map { elem: Int =>
  38. ((1L + elem) * a + b) % MinHashLSH.HASH_PRIME
  39. }.min.toDouble
  40. }
  41. // TODO: Output vectors of dimension numHashFunctions in SPARK-18450
  42. hashValues.map(Vectors.dense(_))
  43. }
  44.  
  45. @Since("2.1.0")
  46. override protected[ml] def keyDistance(x: Vector, y: Vector): Double = {
  47. val xSet = x.toSparse.indices.toSet
  48. val ySet = y.toSparse.indices.toSet
  49. val intersectionSize = xSet.intersect(ySet).size.toDouble
  50. val unionSize = xSet.size + ySet.size - intersectionSize
  51. assert(unionSize > 0, "The union of two input sets must have at least 1 elements")
  52. 1 - intersectionSize / unionSize
  53. }
  54.  
  55. @Since("2.1.0")
  56. override protected[ml] def hashDistance(x: Seq[Vector], y: Seq[Vector]): Double = {
  57. // Since it's generated by hashing, it will be a pair of dense vectors.
  58. // TODO: This hashDistance function requires more discussion in SPARK-18454
  59. x.zip(y).map(vectorPair =>
  60. vectorPair._1.toArray.zip(vectorPair._2.toArray).count(pair => pair._1 != pair._2)
  61. ).min
  62. }
  63.  
  64. @Since("2.1.0")
  65. override def copy(extra: ParamMap): MinHashLSHModel = {
  66. val copied = new MinHashLSHModel(uid, randCoefficients).setParent(parent)
  67. copyValues(copied, extra)
  68. }
  69.  
  70. @Since("2.1.0")
  71. override def write: MLWriter = new MinHashLSHModel.MinHashLSHModelWriter(this)
  72. }

  

minhash pyspark 源码分析——hash join table是关键的更多相关文章

  1. 第九篇:Spark SQL 源码分析之 In-Memory Columnar Storage源码分析之 cache table

    /** Spark SQL源码分析系列文章*/ Spark SQL 可以将数据缓存到内存中,我们可以见到的通过调用cache table tableName即可将一张表缓存到内存中,来极大的提高查询效 ...

  2. Memcached源码分析——hash

    以下为memcached中关于使用的hash算法的一点记录 memcached中默认使用的是Bob Jenkins的jenkins_hash算法 以下4段代码均在memcached-1.4.22/ha ...

  3. hbase源码分析:ERROR: Table already exists问题诊断

    问题描述: 重新安装了测试环境的hadoop,所以之前hbase所建的表数据都丢失了,但是zookeeper没有动.在hbase shell中list的时候,看不到之前建的表,但是create tes ...

  4. 【Spark SQL 源码分析系列文章】

    从决定写Spark SQL源码分析的文章,到现在一个月的时间里,陆陆续续差不多快完成了,这里也做一个整合和索引,方便大家阅读,这里给出阅读顺序 :) 第一篇 Spark SQL源码分析之核心流程 第二 ...

  5. 死磕以太坊源码分析之state

    死磕以太坊源码分析之state 配合以下代码进行阅读:https://github.com/blockchainGuide/ 希望读者在阅读过程中发现问题可以及时评论哦,大家一起进步. 源码目录 |- ...

  6. [源码分析] 带你梳理 Flink SQL / Table API内部执行流程

    [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 目录 [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 0x00 摘要 0x01 Apac ...

  7. java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制

    通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...

  8. SOFA 源码分析 — 负载均衡和一致性 Hash

    前言 SOFA 内置负载均衡,支持 5 种负载均衡算法,随机(默认算法),本地优先,轮询算法,一致性 hash,按权重负载轮询(不推荐,已被标注废弃). 一起看看他们的实现(重点还是一致性 hash) ...

  9. [转]数据库中间件 MyCAT源码分析——跨库两表Join

    1. 概述 2. 主流程 3. ShareJoin 3.1 JoinParser 3.2 ShareJoin.processSQL(...) 3.3 BatchSQLJob 3.4 ShareDBJo ...

随机推荐

  1. Spring boot后台搭建二集成Shiro实现用户验证

    上一篇文章中介绍了Shiro 查看 将Shiro集成到spring boot的步骤: (1)定义一个ShiroConfig,配置SecurityManager Bean,SecurityManager ...

  2. HDU6608-Fansblog(Miller_Rabbin素数判定,威尔逊定理应用,乘法逆元)

    Problem Description Farmer John keeps a website called ‘FansBlog’ .Everyday , there are many people ...

  3. php常用的验证

    <?php namespace Vendor\Func; /** * 常用的验证 * Class Verify * @package Vendor\Func */ class Verify { ...

  4. PHP设计模式 - 桥接模式

    将抽象部分与它的实现部分分离,使他们都可以独立的变抽象与它的实现分离,即抽象类和它的派生类用来实现自己的对象 桥接与适配器模式的关系(适配器模式上面已讲解): 桥接属于聚合关系,两者关联 但不继承 适 ...

  5. ScheduledExecutorService调度线程池运行几次后停止某一个线程

    开发中偶尔会碰到一些轮询需求,比如我碰到的和银行对接,在做完某一个业务后银行没有同步给到结果,这时候就需要查询返回结果,我们的需求是5分钟一次,查询3次,3次过后如果没有结果则T+1等银行的文件,对于 ...

  6. hdu 1022 Train Problem I【模拟出入栈】

    Train Problem I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  7. Goroutines和线程对比

    目录 栈不同 调度不同 GOMAXPROCS Goroutine没有ID号 栈不同 线程:每一个OS线程都有一个固定大小的内存块(一般会是2MB)来做栈,这个栈会用来存储当前正在被调用或挂起(指在调用 ...

  8. magicbook 踩坑

    新买了 magicbook pro 16.1寸的荣耀笔记本,在使用过程中发现了一些问题. 电脑详情 规格: magicbook pro 16.1 cpu: 锐龙 R5 操作系统: deepin 15. ...

  9. jQuery无缝轮播图思路详解-唯品会

    效果图如上: 需求:图片自动轮播,鼠标移上停止播放,离开恢复播放,箭头切换图片. html代码 <!--轮播图大盒子开始--> <div class="wrap" ...

  10. Non-Maximum Suppression(非极大值抑制)

    定义与介绍(NMS 以及soft-NMS也有简单的介绍): https://www.cnblogs.com/makefile/p/nms.html IoU的介绍这篇写的不错: https://oldp ...