优化代码逻辑之 K-近邻算法之鸢尾花实例 使用Spark实现KNN的Demo2

这篇代码是KNN的优化,代码就是

flatMap里面的结构转换
和aggregate的应用感觉有点难理解
但是思想简单

首先要知道这个优化是基于 有个大数据的思想:分而治之

求出一份数据最大的前一百个数 如果数据太多,内存太小,则将数据切割成多份
每份求出前一百 然后这多份的前一百再求出真正的前一百
TODO 优化的地方
使用 广播变量进行广播

2、没有跟上一个代码一样嵌套两层循环 比如y在外面 x在里面

 y  {
  x  {
    结果:(x, y)
  }
 }

不是像上面那样了 而是成了 Array[(String, (Double, String))] 这样的格式

这次是y的RDD在外面 x这个array在里面了 它的所有位置点在里面进行匹配
Array[(x1, (dis, y.label))] 即每个未知点与当前flatMap里面的已知点的距离和已知点的标签 组成的数组
注意使用了flatMap 所以将数组去掉了 在RDD里面是一个个的[(String, (Double, String))]
这样就得到了想要的 所有的未知点与已知点两两匹配得到的每一条数据

2、有着多个数组的RDD进行aggregateByKey 这个key就是未知点 ok这就是分组了 然后再进行计算

  • 有着多个数组的RDD进行aggregateByKey有两个括号3个参数, 这都是对value的操作 即(Double, String)
    就是指距离和标签 先是初始值 然后是分区内的计算 然后是分区间的计算
    初始值:不可变的TreeSet,TreeSet可以自动排序但缺点是不能存重复值,这里先忽略

(Double, String) 将距离放在前边的原因是要安装key距离排序 这样调换一下就不用手动写排序实现类了

不可变是为了线程安全 不可变是指对象不可变
分区内2个参数(buffer, value: (Double, String))先是buffer初始数组
然后是每一条数据的value值 简单每条都加进buffer即可

因为放进TreeSet的都是自动排好序的 所以始终拿前K个数据 这样不用携带大量数据 省存储还排序快

分区间也是2个参数 (buffer1, buffer2) 这样就是两个buffer相加合在一起了,还是一直保持只取前K个
正好TreeSet默认是升序 我们需要的也是距离未知点最近的 ok

这样得出的结果就是每个未知点与距离最近的前9个已知点的distance和label
就是这样似的RDD[(String, TreeSet[(Double, String)])]

然后value就不需要distance了,map一下,做9个label的WordCount即可 然后取出最多的label
即为KNN推测结果

package IrisKNN.teacher
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.immutable
import scala.collection.immutable.TreeSet

/**
 * Created by Shi shuai RollerQing on 2019/12/30 19:26
 */
object KNNDemo2 {


  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setMaster("local[*]")
      .setAppName(this.getClass.getCanonicalName)
    val sc = new SparkContext(conf)
    val K: Int = 9
    val data: RDD[String] = sc.textFile("C:\\Users\\HP\\IdeaProjects\\sparkCore\\data\\iris.dat")
    val dataRDD: RDD[LabeledPoint] = data.map(line => {
      val arr: Array[String] = line.split(",")
      if (arr.length == 5) LabeledPoint(arr.last, arr.init.map(_.toDouble))
      else LabeledPoint("", arr.map(_.toDouble))
    })
    val sampleRDD: RDD[LabeledPoint] = dataRDD.filter(_.label != "")
    val testData: Array[Array[Double]] = dataRDD.filter(_.label == "").map(_.point).collect()
    val testDataBC: Broadcast[Array[Array[Double]]] = sc.broadcast(testData)

    val unKnownAndDisAndLabelRDD: RDD[(String, (Double, String))] = sampleRDD.flatMap { rddPoint =>
      val points: Array[Array[Double]] = testDataBC.value
      // map后的point也是array 所以需要换成String
      //然后注意将dis放在前边 label放在后面 方便后续TreeSet根据dis排序
      val unKnownAndDisAndLabel: Array[(String, (Double, String))] = points.map(point => (point.mkString(","), (getDistance(point, rddPoint.point), rddPoint.label)))
      unKnownAndDisAndLabel
    }
    val LabeledRDD: RDD[(String, TreeSet[(Double, String)])] = unKnownAndDisAndLabelRDD.aggregateByKey(immutable.TreeSet[(Double, String)]())(
      (buffer, value: (Double, String)) => {
        val newBuffer = buffer + value
        newBuffer.take(K)
      },
      (buffer1, buffer2) => (buffer1 ++ buffer2).take(K)
    )
    LabeledRDD.map{case (key, buffer) =>
      val labelSorted: List[String] = buffer.toList.map(_._2).groupBy(x => x).mapValues(_.length).toList.sortBy(_._2).reverse.take(1).map(_._1)
      (key, labelSorted.toString())
    }.foreach(println)

    sc.stop()
  }

  def getDistance(x: Array[Double], y: Array[Double]) = {
    import scala.math._
    sqrt(x.zip(y).map(t => pow(t._1 - t._2, 2.0)).sum)
  }
}

这么写不知道为什么错了。。。我认为是两个
在这里插入图片描述

结果没有错误 但是原来的顺序都乱了 可以在getDistance那个地方 不倒换dis和label的位置 然后实现TreeSet的一个自定义排序 本代码就不改了 就是加个实现类的事 写在下一篇的Demo里面吧
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值