Spark系列---core详解

 1)map操作  
  /**
      * map(func):返回一个新的分布式数据集,由每个原元素经过func函数转换后组成
      * 将原始集合中的每一个元素*7
      * map的操作是一个one to one的操作
      */
        private def mapMethod(sc: SparkContext) = {
        val list = 1 to 10
        val listRDD = sc.parallelize(list)
        val bs = 7
        println("listRDD's partition: " + listRDD.getNumPartitions)
        val retRDD: RDD[Int] = listRDD.map(num => num * bs)
        retRDD.foreach(num => println(num))
}

      2)flatmap操作
         /**
      * flatMap(func):类似于map,但是每一个输入元素,
      * 会被映射为0到多个输出元素(因此,func函数的返回值是一个Seq,而不是单一元素)
      *
      * flatMap是one to many
      * @param sc
      */
    private def flatMapMethod(sc: SparkContext) = {
        val list = List(
            "hello you",
            "hello  me he",
            "you you"
        )
        val listRDD:RDD[String] = sc.parallelize(list)
        val wordsRDD:RDD[String] = listRDD.flatMap{case line => {
            line.split(" ")
        }}
        wordsRDD.foreach(println)
} 
  
3)filter操作
    /**
      * filter(func): 返回一个新的数据集,由经过func函数后返回值为true的原元素组
成
      * 过滤出集合中的偶数even,留下奇数odd
      */
    private def filterMethod(sc: SparkContext) = {
        val range = 1 until 10
        val listRDD = sc.parallelize(range)
        val filteredRDD = listRDD.filter{case num => {num % 2 != 0}}

        filteredRDD.foreach(println)
    }
			
4)sample操作
    /**
      * 采集抽样:
      *     就是用样本空间来代替总体,查看总体数据的分布。
      *     这个Spark的sample抽样是否非准确抽样,如果要抽取固定的数据量,只能自
己手动处理。
      *    会在后面学习Spark如何解决数据倾斜的时候使用
      * sample(withReplacement, frac, seed):
      *     根据给定的随机种子seed,随机抽样出数量为frac的数据
      */
    private def sampleMethod(sc: SparkContext) = {
        val range = 1 to 100000
        val listRDD = sc.parallelize(range)
        val sampleRDD = listRDD.sample(true, 0.002)
        sampleRDD.foreach(println)
        println(s"sampleRDD的元素个数:${sampleRDD.count()}")
    }

5)union操作
   /**
     * union(otherDataset): 返回一个新的数据集,由原数据集和参数联合而成
     * 该操作类似于sql中的union all的操作(而sql中的union会做去重操作)
     */
    private def unionMethod(sc: SparkContext) = {
        val list1 = List(1, 2, 3, 4, 5)
        val list2 = List(3, 5, 6, 8, 10)

        val listRDD1 = sc.parallelize(list1)
        val listRDD2 = sc.parallelize(list2)

        val unionRDD = listRDD1.union(listRDD2)
        unionRDD.foreach(println)
        println("-----------------------------------")
        val interRDD = listRDD1.intersection(listRDD2)//求rdd的交集
        interRDD.foreach(println)
    }

6)groupbykey操作
   /**
     * groupByKey([numTasks]):
     *   在一个由(K,V)对组成的数据集上调用,返回一个(K,Iterable[V])对的数据集。
     *   注意:默认情况下,使用8个并行任务进行分组,你可以传入numTask可选参数,
     *   根据数据量设置不同数目的Task
     * 这就是sql中的groupBy的操作
     * 统计:文件E:/data/spark/topn.txt中每门课程的学员信息进行分组
     */
    private def gbkMethod(sc: SparkContext) = {
        val linesRDD:RDD[String] = sc.textFile("file:///E:/data/spark/topn.txt")
        val course2InfoRDD:RDD[(String, String)] = linesRDD.map(line => {
            val fields = line.split(" ")
            val course = fields(0)
            val name = fields(1)
            val score = fields(2)
            (course, name + "_" + score)
        })
//            course2InfoRDD.foreach(println)
        //分组
        val gbkRDD:RDD[(String, Iterable[String])] = course2InfoRDD.groupByKey()

//        gbkRDD.foreach(t => {
//            println(t._1 + "--->" + t._2)
//        })
        gbkRDD.foreach{case (course, infos) => {
            println(s"${course} ---> ${infos}")
        }}
        //求出每门课中的成绩最高者和成绩
        println("-----求出每门课中的成绩最高者和成绩------")
        val course2MaxInfo:RDD[(String, String)] = gbkRDD.map{case (course, infos) 
=> {
            var max = Int.MinValue
            var name = ""
            for (info <- infos) {
                val score = info.substring(info.indexOf("_") + 1).toInt
                val _name = info.substring(0, info.indexOf("_"))
                if(score >= max) {
                    max = score
                    name = _name
                }
            }
            (course, name + "-" + max)
        }}

        course2MaxInfo.foreach(println)
    }

7)reducebykey操作
   /**
     * reduceByKey(func, [numTasks]):
     *    在一个(K,V)对的数据集上使用,返回一个(K,V)对的数据集,key相同的值,
     *    都被使用指定的reduce函数聚合到一起。和groupByKey类似,任务的个数是可以
     *    通过第二个可选参数来配置的。
     *
     * 在执行类似操作的时候,建议使用reduceByKey而不是使用groupByKey,因为
groupByKey的执行效率要低于reduceByKey,
     * 究其原因主要在于groupByKey在计算的时候没有想reduceByKey一样的本地预聚合
combiner
     * 而countByKey是一个action操作
     */
    private def rbkMethod(sc: SparkContext) = {
        val list = List(
            "hello you",
            "hello  me he",
            "you you"
        )
        val listRDD:RDD[String] = sc.parallelize(list)
        val wordsRDD:RDD[String] = listRDD.flatMap{case line => {
            line.split("\\s+")
        }}

        val pairsRDD = wordsRDD.map((_, 1))

        val rbkRDD:RDD[(String, Int)] = pairsRDD.reduceByKey{case (v1, v2) => 
{v1 + v2}}
        rbkRDD.foreach(println)

        println("--------------groupByKey来完成reduceByKey的操作------------
-----")
        val gbkRDDs:RDD[(String, Int)] = pairsRDD.groupByKey().map{case (word, 
counts) => (word, counts.size)}
        gbkRDDs.foreach(println)
        println("--------------countByKey来完成reduceByKey的操作------------
-----")

        val cbkMap = pairsRDD.countByKey()
        cbkMap.foreach(println)
    }

8)join操作
    /**
      * join(otherDataset, [numTasks]):
      *     在类型为(K,V)和(K,W)类型的数据集上调用,返回一个(K,(V,W))对,
      *     每个key中的所有元素都在一起的数据集
      * 学生表student
      *     id      name       gender
      *     1       余喆         女
      *     2       高磊         男
      *     3   	 任绍俊       男
      *     4   	 梁光彩       女
      *     5   	 白卓民     ladyboy
      * 成绩表score
      *     id      score      sid
      *     001      90         1
      *     002      70         5
      *     003      40         2
      *     004      68.5       4
      *  将学生信息及其考试成绩信息查询出来
      *  select
      *     stu.id,
      *     stu.name,
      *     stu.gender,
      *     s.score
      *  from student stu
      *  join score s on stu.id = s.sid
      * sql中的join的类型:
      * 交叉连接across join,连接没有on  from student stu across join score s –
->产生笛卡尔积 ×
      * 内连接inner join 等值连接,查询左右两张表中都拥有的数据
      * 外连接     不等值连接
      *     左外连接 left outer join
      *         查询所有所有的数据,查询右表能在左表关联上的数据,查询不到的数据显
示为null
      *     右外连接 right outer join
      *         和做外链接相反
      *     全连接 full outer join
      *         左外连接 + 右外连接
      *
      */
    private def joinMethod(sc: SparkContext) = {
        val stuList = List(
            "1   余喆      女",
            "2   高磊      男",
            "3   任绍俊     男",
            "4   梁光彩     女",
            "5   白卓民     ladyboy"
        )

        val scoreList = List(
            "001      90         1",
            "002      70         5",
            "003      40         2",
            "004      68.5       4",
            "005      78.5       9"
        )

        val stuRDD:RDD[String] = sc.parallelize(stuList)
        val scoreRDD:RDD[String] = sc.parallelize(scoreList)

        val stuId2Info:RDD[(String, String)] = stuRDD.map(line => {
            val fields = line.split("\\s+")
            val stuId = fields(0)
            val name = fields(1)
            val gender = fields(2)
            (stuId, name + "," + gender)
        })
        val stuId2Score:RDD[(String, Double)] = scoreRDD.map(line => {
            val fields = line.split("\\s+")
            val stuId = fields(2)
            val score = fields(1).trim.toDouble
            (stuId, score)
        })
        println("----------------join之等值连接-------------------")
        val joinInfo:RDD[(String, (String, Double))] = 
stuId2Info.join(stuId2Score)

//        joinInfo.foreach(t => {
//            println(s"stuId:${t._1}==>info:${t._2._1}--${t._2._2}")
//        })
        joinInfo.foreach{case (stuId, (info, score)) => {
            println(s"stuId:${stuId}==>info:${info}--${score}")
        }}
        println("--------------不等值连接之leftouterjoin-----------------")
        val lojRDD:RDD[(String, (String, Option[Double]))] = stuId2Info.
leftOuterJoin(stuId2Score)
        lojRDD.foreach{case (stuId, (info, option)) => {
            println(s"stuId:${stuId}==>info:${info}—
${option.getOrElse(null)}")
        }}
        println("----------------不等值连接之rightouterjoin-----------------")
        val rojRDD:RDD[(String, (Option[String], Double))] = stuId2Info.
rightOuterJoin(stuId2Score)
        rojRDD.foreach{case (stuId, (option, score)) => {
            println(s"stuId:${stuId}==>info:${option.getOrElse(null)}—
${score}")
        }}
        println("----------------不等值连接之fullouterjoin------------------")
        val fojRDD:RDD[(String, (Option[String], Option[Double]))] = 
stuId2Info.fullOuterJoin(stuId2Score)
        fojRDD.foreach{case (stuId, (lOption, rOption)) => {
            println(s"stuId:${stuId}==>info:${lOption.getOrElse(null)}—
${rOption.getOrElse(null)}")
        }}
9)sortbykey操作
    /**
      * sparkcore中使用sortBy和sortByKey进行排序
      * 按照学员的身高进行排序
      * student
      * id  name    height
      * 1   黄术枝    172.5
      * 2   孙天宝    178.5
      * 3   李涛      180.5
      * 4   姜威      170
      * 5   闫建一    188
      * sortByKey:  ---->必须是k-v对
      *     指的是分区内部有序,如果有多个分区进行排序,整体不一定有序,要想整体有
序,请设置numPartitions=1
      * sortBy
      *     其底层也是sortByKey
      */
    private def sortMethod(sc: SparkContext) = {
        val list = List(
            "1   黄术枝    172.5",
            "2   孙天宝    178.5",
            "3   李涛      180.5",
            "4   姜威      170",
            "5   闫建一     188"
        )
        val stuRDD:RDD[String] = sc.parallelize(list)
        //使用sortByKey进行排序--->转化数据为k-v对key进行排序,k--->身高
        val height2Info:RDD[(Double, String)] = stuRDD.map(line => {
            val fields = line.split("\\s+")
            val id = fields(0)
            val name = fields(1)
            val height = fields(2).trim.toDouble

            (height, id + "," + name)
        })
        val sortedRDD:RDD[(Double, String)] = height2Info.sortByKey
(numPartitions=1, ascending = false)
        println("----------------sortByKey进行排序-------------------")
        sortedRDD.foreach{case (height, idName) => {
            println(s"${idName},${height}")
        }}
        println("----------------sortBy进行排序-------------------")

        /**
          * 使用给定的key对应的函数,对rdd进行排序,并返回有序的结果
          */
        val sortedByRDD = stuRDD.sortBy[Double](
            line => {
                val fields = line.split("\\s+")
                fields(2).trim.toDouble
            }, false, 1
        )(new Ordering[Double]() {
            //升序:前面比后面,降序,后面比前面
            override def compare(x: Double, y: Double) = {
                x.compareTo(y)
            }
        }, ClassTag.Double.asInstanceOf[ClassTag[Double]])
        sortedByRDD.foreach(println)
    }

10)两个底层算组:combineByKey,aggregateByKey
   package com.aura.bigdata.spark.core.p2

import org.apache.log4j.{Level, Logger}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable.ArrayBuffer

/**
  * 学习两个底层的算子:combineByKey,aggregateByKey
  *
  * 在研读reduceByKey源码的时候,看到将map的数据通过shuffle进行reduce的时
候,默认选择hash分区方式,将不同的key送到对应的分区中。
  * 分区的id就是0到numPartitions-1,如果有3个分区,分区id就是0,1,2
  * reduceByKey有map段的预聚合操作
  *
  * 同样,在看groupBykey的源码的时候,看到数据也是按照hashPartitioner方式进行
组织,同时官方建议如果我们需要计算分组内部的值得聚合做操
  * 比如求每一个组的平均值,求和,最大值,最小值等待,这个建议使用combineByKey,
或者reduceByKey进行代替,以获得更高的性能。
  * 究其原因在于groupByKey同样使用combineByKey实现,但是不同于reduceByKey的
时候,gbk没有本地预聚合。
  *
  * 如何自定分区:
  *     1、如果手动在代码中已经制定了分区,那么就直接将该数据发送该分区,
  *     2、如果没有执行,使用默认的defaultPartitioner(HashPartitioner)进行数据
的投送
  *
  *  使用combineByKey,aggregateByKey来分别模拟reduceByKey和groupByKey
  *      
  *  combineByKey和aggregateByKey的区别?
  *     1、combineByKey和aggregateByKey的底层实现都是combineByKeyWithClassTag
方法,所以本质上没有区别
  *     2、combineByKey更适合那些聚合前后数据类型不一致的rdd操作,aggregateByKey
更合适那些聚合前后数据类型一致的rdd操作。
  *  为了完成reduceByKey或者groupByKey中完成的比较复杂的聚合操作,这个时候使
用combineByKey和aggregateByKey更能洞悉数据操作的底层过程。
  */
object _02SparkTransformationOps {
    def main(args: Array[String]): Unit = {
        Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
        Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
        val conf = new SparkConf().setAppName("_02SparkTransformationOps")
            .setMaster("local[2]")
        val sc = new SparkContext(conf)

//        cbk2rbkMethod(sc)
        cbk2gbkMethod(sc)
        sc.stop()
    }
    /*
        使用combineByKey来模拟groupByKey
     */
    private def cbk2gbkMethod(sc: SparkContext) = {
        val linesRDD:RDD[String] = sc.textFile
("file:///E:/data/spark/topn.txt")

        val course2InfoRDD:RDD[(String, String)] = linesRDD.map(line => {
            val fields = line.split(" ")
            val course = fields(0)
            val name = fields(1)
            val score = fields(2)
            (course, name + "_" + score)
        })

//        course2InfoRDD.foreach(println)
        //(chinese,CompactBuffer(ls_91, zs_90, zl_76, wb_95, sj_74, yj_98))
        //(english,CompactBuffer(ww_56, zq_88, ts_87, ys_67, mz_77, gk_96))
//        course2InfoRDD.groupByKey().foreach(println)
//        val cbk2gbkRDD:RDD[(String, ArrayBuffer[String])] = course2InfoRDD.
combineByKey(createCombiner1, mergeValue1, mergeCombiners1)

        val cbk2gbkRDD = course2InfoRDD.combineByKey(
            nameScore => {
                val ab = new ArrayBuffer[String]()
                ab.append(nameScore)
                ab
            },
            (buffer:ArrayBuffer[String], nameScore:String) => {
                buffer.append(nameScore)
                buffer
            },
            (buffer:ArrayBuffer[String], buffer1:ArrayBuffer[String]) => {
                buffer.++(buffer1)
            }
        )
        cbk2gbkRDD.foreach(println)
    }
    def createCombiner1(nameScore:String):ArrayBuffer[String] = {
        val ab = ArrayBuffer[String]()
        ab.append(nameScore)
        ab
    }

    def mergeValue1(buffer:ArrayBuffer[String], 
nameScore:String):ArrayBuffer[String] = {
        buffer.append(nameScore)
        buffer
    }

    def mergeCombiners1(buffer1:ArrayBuffer[String], 
buffer2:ArrayBuffer[String]):ArrayBuffer[String] = {
        buffer1.++(buffer2)
    }

    /**
      * 使用combineByKey来模拟reduceByKey
      * @param sc
      */
    private def cbk2rbkMethod(sc: SparkContext) = {
        val list = List(
            "hello you",
            "hello  me he",
            "hello  shit he",
            "you you"
        )
        val listRDD: RDD[String] = sc.parallelize(list)
        val wordsRDD: RDD[String] = listRDD.flatMap { case line => {
            line.split("\\s+")
        }
        }

        val pairsRDD = wordsRDD.map((_, 1))

        //使用combineByKey来模拟reduceByKey
        //        pairsRDD.reduceByKey(_+_)
        val cbk2rbkRDD: RDD[(String, Int)] = pairsRDD.combineByKey
(createCombiner, mergeValue, mergeCombiners)
        cbk2rbkRDD.foreach(println)
    }

    /**
      * 第一个起始聚合操作,在每一个分区中,相同key只会执行一次该操作
      * (k1, v1), (k1, v2)
      * v1+v2
      * 1+。。。+10
      *
      * var sum = 1
      * for(i <- 2 to 10) {
      *     sum += i
      * }
      *
      * 改createCombiner就相当于创建这个sum的过程,在每一个分区对应的相同
的key中只需要调用一次
      *
      * @return
      */
        def createCombiner(num:Int) = {
          println(s"---------createCombiner----------------${num}")
          num
       }

    /**
      * 当sum创建好之后,便开始循环遍历集合,给sum身上不断赋值数据
      * 所以如果在某一个分区中的某一个key有N个值,当前方法会被调用N-1次
      */
        def mergeValue(sum:Int, num:Int) = {
           println(s"---------mergeValue----------------${sum}---->${num}")
           sum + num
    }

    /**
      * 当分区内部的数据计算完毕之后,便开始了相同key的分区间的计算
      */
    def mergeCombiners(sum1:Int, sum2:Int) = {
        println(s"------mergeCombiners------${sum1}---->${sum2}")
        sum1 + sum2
    }
}

11)使用aggregateByKey模拟reduceByKey和groupByKey
    package com.aura.bigdata.spark.core.p2

import org.apache.log4j.{Level, Logger}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable.ArrayBuffer

/**
  * 使用aggregateByKey模拟reduceByKey和groupByKey
  *
  */
object _03SparkTransformationOps {
    def main(args: Array[String]): Unit = {
        Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
        Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
        val conf = new SparkConf().setAppName("_03SparkTransformationOps")
            .setMaster("local[2]")
        val sc = new SparkContext(conf)

//        abk2RbkMethod(sc)
        abk2GbkMethod(sc)
        sc.stop()
    }

    /**
      * 使用aggregateByKey模拟groupByKey
      * @param sc
      */
    private def abk2GbkMethod(sc: SparkContext) = {
         val linesRDD:RDD[String]
 = sc.textFile("file:///E:/data/spark/topn.txt")

        val course2InfoRDD:RDD[(String, String)] = linesRDD.map(line => {
            val fields = line.split(" ")
            val course = fields(0)
            val name = fields(1)
            val score = fields(2)
            (course, name + "_" + score)
        })

        val abk2gbk = course2InfoRDD.aggregateByKey(new 
ArrayBuffer[String]())(
            (buffer: ArrayBuffer[String], nameScore: String) => {
                buffer.append(nameScore)
                buffer
            },
            (buffer1: ArrayBuffer[String],buffer2: ArrayBuffer[String]) => {
                buffer1.++(buffer2)
            }
        )
        abk2gbk.foreach(println)

    }
    private def abk2RbkMethod(sc: SparkContext) = {
        val list = List(
            "hello you",
            "hello  me he",
            "hello  shit he",
            "you you"
        )
        val listRDD: RDD[String] = sc.parallelize(list)
        val wordsRDD: RDD[String] = listRDD.flatMap { case line => {
            line.split("\\s+")
        }
        }

        val pairsRDD = wordsRDD.map((_, 1))
        //var sum = 0
        val abk2rbk = pairsRDD.aggregateByKey(0)(seqOp, combOp)
        pairsRDD.aggregateByKey(0)((sum, num) => sum + num, (sum1, sum2) => 
sum1+sum2)
        abk2rbk.foreach(println)
    }

    /**
      * 分区内的数据合并
      * 某一个分区中的某一个Key有N个值,当前方法会被调用N次
      */
    def seqOp(sum:Int, num:Int) = {
        println("---------------seqOp---------------")
        sum + num
    }

    /**
      * 分区间的数据合并
      */
    def combOp(sum1:Int, sum2:Int) = {
        println("---------------combOp---------------")
        sum1 + sum2
    }
}
4,SparkRDD的action操作

       package com.aura.bigdata.spark.core.p2

import org.apache.hadoop.io.{IntWritable, Text}
import org.apache.hadoop.io.compress.{DefaultCodec, SnappyCodec}
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat
import org.apache.log4j.{Level, Logger}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 学习SparkRDD的action操作
  * action操作是spark作业job执行的动因
  */
object _04SparkActionOps {
    def main(args: Array[String]): Unit = {
        Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
        Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
        val conf = new SparkConf().setAppName("_03SparkTransformationOps")
            .setMaster("local[2]")
        val sc = new SparkContext(conf)
        val list = List(
            "hello you",
            "hello  me he",
            "hello  shit  he",
            "you you"
        )
        val listRDD: RDD[String] = sc.parallelize(list)
        val wordsRDD: RDD[String] = listRDD.flatMap { case line => {
            line.split("\\s+")
        }
        }

        val pairsRDD = wordsRDD.map((_, 1))

        /*
            reduce(func): 通过函数func聚集数据集中的所有元素。Func函数接受2个
参数,返回一个值。这个函数必须是关联性的,确保可以被正确的并发执行
            该reduce操作和scala中的reduce操作一模一样
            val list = 1 to 100
            val listRDD = sc.parallelize(list)
            val ret = listRDD.reduce((v1, v2) => v1 + v2)
         */

        /*
            collect():
            在Driver的程序中,以数组的形式,返回数据集的所有元素。
            这通常会在使用filter或者其它操作后,返回一个足够小的数据子集再使用,
            直接将整个RDD集Collect返回,很可能会让Driver程序OOM

            将RDD中各个分区中的数据,全都拉取到driver中。
         */
           pairsRDD.collect().foreach(println)

        /**
          * count(): 返回数据集rdd的元素个数
          */
            println("===========pairsRDD总共有" + pairsRDD.count() + "条记录。")
        /*
            take(n): 返回一个数组,由数据集的前n个元素组成。
            注意,这个操作目前并非在多个节点之上并行执行,而是Driver程序所在机器,
            单机计算所有的元素(Gateway的内存压力会增大,需要谨慎使用)
         */
        println("=====================take(3)=====")
        pairsRDD.take(3).foreach(println)
        println("=====================takeOrdered(2)=====")
        val retRDD = pairsRDD.reduceByKey(_+_)
        retRDD.takeOrdered(2)(new Ordering[(String, Int)](){
            override def compare(x: (String, Int), y: (String, Int)) = {
                var ret = y._2.compareTo(x._2)
                if(ret == 0) {
                   ret = x._1.compareTo(y._1)
                }
                ret
            }
        }).foreach(println)
//        first(): 返回数据集的第一个元素(类似于take(1))
    /*
        saveAsTextFile(path):将数据集的元素,以textfile形式,保存到本地文件系统,
        hdfs或者任何其它hadoop支持的文件系统。Spark将会调用每个元素的toString
方法,
        并将它转换为文件中的一行文本
     */
        retRDD.saveAsTextFile("file:///E:/data/spark/wc" + 
System.currentTimeMillis())

        /**
          * saveAsSequenceFile(path):
          *     将数据集的元素,以sequencefile的格式,保存到指定的目录下,本地系
统,hdfs或者任何其它hadoop支持的文件系统。RDD的元素必须由key-
value对组成,并都实现了Hadoop的Writable接口,或隐式可以转换为
          *    Writable(Spark包括了基本类型的转换,例如Int,Double,String等等)
          */
//        retRDD.saveAsSequenceFile("file:///E:/data/spark/seq" + 
System.currentTimeMillis())

          retRDD.saveAsSequenceFile(
            "file:///E:/data/spark/seq" + System.currentTimeMillis(),
            Option(classOf[DefaultCodec])
        )
//        foreach(func): 在数据集的每一个元素上,运行函数func。这通常用于更新一
个累加器变量,或者和外部存储系统做交互

        retRDD.saveAsNewAPIHadoopFile(
            "file:///E:/data/spark/nahf" + System.currentTimeMillis(),
            classOf[Text],
            classOf[IntWritable],
            classOf[TextOutputFormat[Text, IntWritable]]
        )
        sc.stop()
    }
}

6,高级排序之求topN
       package com.aura.bigdata.spark.core.p3

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
  * SparkRDD的TopN求值
  *
  */
object _05SparkRDDTopNOps {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf()
            .setMaster("local[*]")
            .setAppName(s"${_05SparkRDDTopNOps.getClass.getSimpleName}")
        val sc = new SparkContext(conf)
        val linesRDD = sc.textFile("file:///E:/data/spark/core/10w行数据.txt")

        val retRDD:RDD[(String, Int)] = linesRDD.flatMap(_.split("\\s+")).map((_, 
1)).reduceByKey(_+_)
        retRDD.sortByKey()
        /**
          * 求出该文件中单词出现次数最高的N(1, 3, 5, 7, 10)个值
          */
//        retRDD.map{case (word, count) => (count, word)}.sortByKey(false, 
1).take(5).foreach(println)

        retRDD.takeOrdered(5)(new Ordering[(String, Int)](){
            override def compare(x: (String, Int), y: (String, Int)) = {
                y._2.compareTo(x._2)
            }
        }).foreach(println)
        sc.stop()
    }
}

7,二次排序
       package com.aura.bigdata.spark.core.p3

import org.apache.spark.rdd.RDD
import org.apache.spark.serializer.KryoSerializer
import org.apache.spark.{SparkConf, SparkContext}

import scala.reflect.ClassTag

/**
  * 二次排序
  *     排序的依据不唯一,有多个排序指标
  * 需求:
  *     20 21
        50 51
        50 52
        50 53
  *  先按照第一列的升序,再按照第二列的降序?
  *  1、排序,使用sortByKey
  *     要按照key进行排序,问题是当前的排序字段有两个,怎么办? 必须要使用复合
key,才可以进行排序,
  *     只能自定义对象进行排序
  *  2、排序,使用sortBy
  */
object _06SparkRDDSecondSortOps {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf()
            .setMaster("local[*]")
            .setAppName(s"${_06SparkRDDSecondSortOps.getClass.getSimpleName}")
            //spark提供的比java原生的序列化Serializable更加高效的一种序列化方
式
            .set("spark.serializer", classOf[KryoSerializer].getName)
            .registerKryoClasses(Array(classOf[SecondSort]))

        val sc = new SparkContext(conf)
        val linesRDD = sc.textFile("file:///E:/data/spark/secondsort.csv")

//        sbkMethod1(linesRDD)

        val sortedRDD:RDD[String] = linesRDD.sortBy(
            line => line,
            false,
            1
        )(new Ordering[String](){
            /*
               x: 1	2
               y: 3	4
             */
            override def compare(x: String, y: String) = {
                val xFields = x.split("\\s+")
                val yFields = y.split("\\s+")

                val xFirst = xFields(0).trim.toInt
                val yFirst = yFields(0).trim.toInt

                val xSecond = xFields(1).trim.toInt
                val ySecond = yFields(1).trim.toInt

                var ret = xFirst.compareTo(yFirst)
                if(ret == 0) {
                    ret = ySecond.compareTo(xSecond)
                }
                ret
            }
        }, ClassTag.Object.asInstanceOf[ClassTag[String]])
        sortedRDD.foreach(println)
        sc.stop()
    }

    private def sbkMethod1(linesRDD: RDD[String]) = {
        val ssRDD = linesRDD.map(line => {
            val fields = line.split("\\s+")
            val first = fields(0).trim.toInt
            val second = fields(1).trim.toInt
            (new SecondSort(first, second), null)
        })

        //        ssRDD.foreach(println)

        val sbkRDD = ssRDD.sortByKey(true, 1)

        sbkRDD.foreach { case (ss, tag) => {
            println(ss)
        }
        }
    }
}

/**
  * scala和java中的排序的对象
  * Ordered     ----->Comparable
  * Ordering    ----->Comparator
  */
class SecondSort(val first:Int, val second:Int) extends Ordered[SecondSort] /*with Serializable*/ {

    override def toString: String = this.first + "\t" + this.second

    //先按照第一列的升序,再按照第二列的降序?
    override def compare(that: SecondSort) = {
        var ret = this.first.compareTo(that.first)
        if(ret == 0) {
            ret = that.second.compareTo(this.second)
        }
        ret
    }
}
未序列化的错误: 
    需要序列化的原因是因为需要通过JVM,也就是说需要将多个分区的数据拉区到一起进
行操作,若是只动用一个分区,则不需要序列化

8,分组求topN
       package com.aura.bigdata.spark.core.p3

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable

/**
  * 分组排序-->分组topN
  * course  name    score
  * chinese ls 91
    english ww 56
    chinese zs 90
    求出每门课程成绩Top3,怎么办?
    1、假设只有一个课程,直接将该课程的成绩放到集合中进行排序即可。
2、现在是多门课程,要求出的是每门课程成绩Top3,必须要把每一门课程的所有的信息
进行单独的排序
        现在的问题,如何将不同课程的信息进行归类,也就是说把语文课的信息放在一起,
英语课的成绩放在一起,因为只有
    该科目的信息放在一起才可以进行科目内的排序,并求出top3.
    分组就要用到groupByKey
    RDD[String, Iterable[String]]
  */
object _07SparkRDDGroupSortOps {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf()
            .setMaster("local[*]")
            .setAppName(s"${_05SparkRDDTopNOps.getClass.getSimpleName}")
        val sc = new SparkContext(conf)
        val linesRDD = sc.textFile("file:///E:/data/spark/topn.txt")

        val course2Info:RDD[(String, String)] = linesRDD.map(line => {
            val course = line.substring(0, line.indexOf(" "))
            val info = line.substring(line.indexOf(" ") + 1)
            (course, info)
        })
        val course2Infos:RDD[(String, Iterable[String])] = 
course2Info.groupByKey()

        course2Infos.foreach(println)
        //要求每门课程的top3
        val course2Top3Infos:RDD[(String,  mutable.TreeSet[String])] = 
course2Infos.map{case (course, infos) => {
            /*
                infos是该门课程的所有的成绩信息,最后只需要3条,所以我们的组
内排序就在该map算子中完成
                排序的集合?TreeSet,使用比较器完成自定义的比较

                x: ls 91
                y: zs 90
             */
            var ts = new mutable.TreeSet[String]()(new Ordering[String](){
                override def compare(x: String, y: String) = {
                    val xScore = x.substring(x.indexOf(" ") + 1).trim.toInt
                    val yScore = y.substring(y.indexOf(" ") + 1).trim.toInt
                    yScore.compareTo(xScore)
                }
            })

            for(info <- infos) {
                ts.add(info)
                if(ts.size > 3) {
                    ts = ts.dropRight(1)
                }
            }
//            //取前3
//            (course, ts.take(3))
            (course, ts)
        }}

        course2Top3Infos.foreach(println)
        sc.stop()
    }
}

9,分组求topN二
       package com.aura.bigdata.spark.core.p3

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer

/**
  * 分组排序-->分组topN
  * course  name    score
  * chinese ls 91
    english ww 56
    chinese zs 90
    求出每门课程成绩Top3,怎么办?
    1、假设只有一个课程,直接将该课程的成绩放到集合中进行排序即可。
2、现在是多门课程,要求出的是每门课程成绩Top3,必须要把每一门课程的所有的信息
进行单独的排序
        现在的问题,如何将不同课程的信息进行归类,也就是说把语文课的信息放在一起,
英语课的成绩放在一起,因为只有
    该科目的信息放在一起才可以进行科目内的排序,并求出top3.
    分组就要用到groupByKey
    RDD[String, Iterable[String]]
  */
object _08SparkRDDGroupSortOps {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf()
            .setMaster("local[*]")
            .setAppName(s"${_05SparkRDDTopNOps.getClass.getSimpleName}")
        val sc = new SparkContext(conf)
        val linesRDD = sc.textFile("file:///E:/data/spark/topn.txt")

        val course2Info:RDD[(String, String)] = linesRDD.map(line => {
            val course = line.substring(0, line.indexOf(" "))
            val info = line.substring(line.indexOf(" ") + 1)
            (course, info)
        })
        val course2Infos:RDD[(String, ArrayBuffer[String])] = 
course2Info.combineByKey(
            createCombiner,
            mergeValue,
            mergeCombiners
        )

        course2Infos.foreach(println)
        //要求每门课程的top3
        val course2Top3Infos:RDD[(String,  mutable.TreeSet[String])] = 
course2Infos.map{case (course, infos) => {
            /*
                infos是该门课程的所有的成绩信息,最后只需要3条,所以我们的组
内排序就在该map算子中完成
                排序的集合?TreeSet,使用比较器完成自定义的比较

                x: ls 91
                y: zs 90
             */
            var ts = new mutable.TreeSet[String]()(new Ordering[String](){
                override def compare(x: String, y: String) = {
                    val xScore = x.substring(x.indexOf(" ") + 1).trim.toInt
                    val yScore = y.substring(y.indexOf(" ") + 1).trim.toInt
                    yScore.compareTo(xScore)
                }
            })

            for(info <- infos) {
                ts.add(info)
                if(ts.size > 3) {
                    ts = ts.dropRight(1)
                }
            }
//            //取前3
//            (course, ts.take(3))
            (course, ts)
        }}

        course2Top3Infos.foreach(println)
        sc.stop()
    }

    def createCombiner(info:String):ArrayBuffer[String] = {
        val ab = new ArrayBuffer[String]()
        ab.append(info)
        ab
    }
    def mergeValue(buffer:ArrayBuffer[String], info:String) = {
        buffer.append(info)
        buffer
    }
def mergeCombiners(buffer1:ArrayBuffer[String], 
buffer2:ArrayBuffer[String]) = {
        val buffer = buffer1.++(buffer2)
        buffer
    }
}

10,分组求topN三:
package com.aura.bigdata.spark.core.p3

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable

/**
  * 分组排序-->分组topN
  * course  name    score
  * chinese ls 91
    english ww 56
    chinese zs 90
    求出每门课程成绩Top3,怎么办?
    1、假设只有一个课程,直接将该课程的成绩放到集合中进行排序即可。
2、现在是多门课程,要求出的是每门课程成绩Top3,必须要把每一门课程的所有的信
息进行单独的排序
        现在的问题,如何将不同课程的信息进行归类,也就是说把语文课的信息放在一
起,英语课的成绩放在一起,因为只有
    该科目的信息放在一起才可以进行科目内的排序,并求出top3.
    分组就要用到groupByKey
    RDD[String, Iterable[String]]

    终极优化
        边分组一遍排序
  */
object _08SparkRDDGroupSortOps {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf()
            .setMaster("local[*]")
            .setAppName(s"${_05SparkRDDTopNOps.getClass.getSimpleName}")
        val sc = new SparkContext(conf)
        val linesRDD = sc.textFile("file:///E:/data/spark/topn.txt")

        val course2Info:RDD[(String, String)] = linesRDD.map(line => {
            val course = line.substring(0, line.indexOf(" "))
            val info = line.substring(line.indexOf(" ") + 1)
            (course, info)
        })
        val course2Infos = course2Info.combineByKey(
            createCombiner,
            mergeValue,
            mergeCombiners
        )

        course2Infos.foreach(println)
        sc.stop()
    }

    def createCombiner(info:String):mutable.TreeSet[String] = {
        val ts = new mutable.TreeSet[String]()(new Ordering[String](){
            override def compare(x: String, y: String) = {
                val xScore = x.substring(x.indexOf(" ") + 1).trim.toInt
                val yScore = y.substring(y.indexOf(" ") + 1).trim.toInt
                yScore.compareTo(xScore)
            }
        })
        ts.add(info)
        ts
    }

    def mergeValue(ts:mutable.TreeSet[String], info:String):mutable.TreeSet[String] = {
        ts.add(info)
        if(ts.size > 3) {
            ts.dropRight(1)//删除→_→的元素
        } else {
            ts
        }
    }
def mergeCombiners(ts1:mutable.TreeSet[String], 
ts2:mutable.TreeSet[String]):mutable.TreeSet[String] = {
//        val ts = ts1.++:(ts2)
//        if(ts.size > 3) {
//            ts.dropRight(ts.size - 3)
//        } else {
//            ts
//        }
        ts2.foreach(info => {
            ts1.add(info)
        })
        if(ts1.size > 3) {
            ts1.dropRight(ts1.size - 3)
        } else {
            ts1
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lipviolet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值