spark中的转换算子(一)

package com.zyc.spark
import com.zyc.utils.DateUtils
import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}

/**
* Created with IntelliJ IDEA.
* Author: zyc2913@163.com
* Date: 2020/9/25 16:30
* Version: 1.0
* Description: 转换算子的应用
*/
object StudyTransformation {
  def main(args: Array[String]): Unit = {
    //配置spark环境
    val conf = new SparkConf()
      .setMaster("local[2]")
      .setAppName("demo2")
    val sc = new SparkContext(conf)

    /**
     * 转换算子
     * RDD -> transformation -> RDD
     */
    /**
     * 1.map 对每个元素操作map
     * 1.1声明  def map(f: T => U): RDD[U]
     * 1.2参数  一个一元函数,参数是原RDD的元素类型,返回值可以改变类型
     * 1.3返回值  新的RDD,泛型是f的返回值类型
     * 1.4作用  将原RDD中的每个元素应用到f中,将返回值收集到一个新的RDD中
     * 1.5应用举例  收集日志文件中的ip,time(访问时间),rCode(登录标识码)
     */
      //通过读取文件创建RDD
    val rdd1:RDD[String] = sc.textFile("C:\\Users\\Administrator\\Desktop\\book\\logs\\access.log-20190926")
    val rdd2 = rdd1.map(line => {
      val str = line.split("\\s+")
      val ip = str(0) // 小标为0的字符串为ip
      val time = DateUtils.dateFormat(str(3).tail) //调用工具类的日期转换函数将下标为3的字符串的尾部进行转换
      var rCode = 0 //定义登录标识码初始值为0
      try {
        rCode = str(8).toInt
        // rCode的值是下标为8的字符串,将String类型转成Int型
      } catch {
        case e: Exception =>
      }
      (ip, time, rCode)  //最后一行是返回值
    })
    //rdd2.foreach(println)

    /**
     * 2.filter 过滤器
     * 2.1声明  def filter(f: T => Boolean): RDD[T]
     * 2.2参数  一元函数,参数是原RDD中的元素,返回值是Boolean
     * 2.3返回值  和原RDD泛型一致的RDD
     * 2.4作用  过滤元素 (f返回True的元素将被留下)
     * 2.5应用举例  收集rCode为200和404的访问信息(ip, time, rCode)
     */
    rdd2.filter(_._3 == 200)  //留下rCode为200的函数返回值
     // .foreach(println)     //打印到控制台
    rdd2.filter(_._3 == 404)  //留下rCode为404的函数返回值
     // .foreach(println)     //打印到控制台

    /**
     * 3.flatMap 二维转换成一维map
     * 3.1声明  def flatMap(f: T => TraversableOnce[U]): RDD[U]
     * 3.2参数  一个一元函数,参数是原RDD的元素类型,返回值是一个集合
     * 3.3返回值  是一个f的返回值泛型的RDD
     * 3.4作用  将二维集合变成一维集合
     * 3.5应用举例  统计所有ip中0到9的数字出现的次数
     */
    rdd2.flatMap(x => {
      val res1:String = x._1
      val res2:String = res1.replace(".", "")
      val res3:Array[String] = res2.split("")
      res3
    })
      .map((_,1)) //写成二元组的形式
      .reduceByKey(_+_) //根据key聚合统计value
      //.foreach(println)
    //上面的也可以写成下面的样子
    val rdd3:RDD[String] = rdd2.flatMap(_._1.replace(".", "").split(""))
      rdd3.map((_,1))
        .reduceByKey(_+_)
        //.foreach(println)

    /**
     * 4.mapPartitions  按分区数map
     * 4.1声明  def mapPartitions( f: Iterator[T] => Iterator[U],preservesPartitioning: Boolean = false): RDD[U]
     * 4.2参数 第一个参数:一个一元函数,参数是一个原RDD泛型的迭代器,这个迭代器每次传入一个分区的全部元素,返回值也是一个迭代器,泛型不限
     * 4.3返回值  RDD[U]  泛型为f返回值泛型的RDD
     * 4.4作用 之前map是每次拿到一个元素,mapPartitions一次性拿到一个分区的所有元素,在将处理完的结果放回到新的RDD中
     */
    val rdd4:RDD[Int] = sc.makeRDD(1 to 10)  //1到10的数组
    //rdd4.map(_+1).foreach(println) //RDD每个元素+1,然后打印到控制台,运算了10次
    //rdd4.mapPartitions(iter => iter.map(_+1)).foreach(println) //RDD按分区数map,每个元素+1然后打印到控制台,运算了2次(设置的核心数为2,则默认分区数为2)
      rdd4.mapPartitions((iter:Iterator[Int]) => {
        Iterator(iter.toList.mkString("|")) //把数组转换成List,根据分区数分成了2个list,然后把元素按|分隔开
      })
     //.foreach(println) //打印结果为6|7|8|9|10  1|2|3|4|5

    /**
     * 5.mapPartitionsWithIndex  按分区数的下标map  (下标默认从0开始,下标也叫索引)
     * 5.1声明  def mapPartitionsWithIndex(
     *            分区索引
     *          f: (Int, Iterator[T]) => Iterator[U],
     *          preservesPartitioning: Boolean = false): RDD[U]
     * 5.2参数   与mapPartitions类似,多了一个int类型的参数,接收传进来的分区索引
     * 5.3返回值  RDD[U]
     * 5.4作用  与mapPartitions类似
     */

    rdd4.mapPartitionsWithIndex((index,iter) => {
      val tuple:(Int,String) = (index, iter.mkString("|")) //二元组类型(下标,按|分隔的数组)
      Iterator(tuple)
    })
      //.foreach(println) //打印结果:(0,1|2|3|4|5)   (1,6|7|8|9|10)   ( 设置的核心数为2,默认的分区数就为2,所以分区的下标就是0,1)

    /**
     * 6.sample 计算机的随机算法,可以保证如果输入参数不同-->>输出结果不同
     *6.1声明  def sample(
     *        withReplacement: Boolean, //是否放回 true表示放回,取的值可能会重复;false表示不放回,取的值不会重复
     *        fraction: Double, //抽样比例 不是非常精准(可能多几个也可能少几个),即抽出的个数占总个数的比值
     *        seed: Long = Utils.random.nextLong //随机种子
     *        ): RDD[T]
     * 6.2参数
     * 6.3返回值 和原RDD类型相同的RDD
     * 6.4作用  在海量数据中进行抽样
     * 6.5应用举例:对一个数组(1到100)进行抽样
     */
    val rdd5:RDD[Int] = sc.makeRDD(1 to 100) //定义一个1到100的数组
    //rdd5.sample(false,0.1).foreach(println)   //从数组(1到100)中不放回(false)的取总个数的比例为0.1(百分之10)的元素(因为是比例所以不精准,可能是10个,可能是9个,也可能是11个)打印到控制台
    /*
    takeSample精确抽样;sample模糊抽样
     */
    //rdd5.takeSample(false,10).foreach(println)  //从数组中不放回的随机取10个元素打印到控制台

    /**
     * 7.union   类似于集合中的并集 A ∪ B
     * 7.1声明   def union(other: RDD[T]): RDD[T]
     * 7.2参数    另外一个和原RDD类型相同的RDD
     * 7.3返回值  和原RDD类型相同
     * 7.4作用   合并RDD
     * 7.5应用举例:把两个数组合并到一起(并集)
     */
    val res1 = sc.makeRDD(1 to 5)
    val res2 = sc.makeRDD(3 to 7)
    //res1.union(res2).foreach(println) //将res1与res2合并(取并集有重复的元素)打印到控制台

    /**
     * 8.intersection  类似于集合中的交集  A ∩ B
     * 8.1声明    def intersection(other: RDD[T]): RDD[T]
     * 8.2参数   另外一个和原RDD类型相同的RDD
     * 8.3返回值  和原RDD类型相同
     * 8.4作用  交集
     * 8.5应用举例:取两个数组中相同的元素(交集)
     */
    //res1.intersection(res2).foreach(println) //取res1与res2中的相同元素(交集)打印到控制台

    /**
     * 9  distinct 去重
     * 9.1声明
     * 9.2参数
     * 9.3返回值
     * 9.4作用
     * 9.5应用举例:去除数组中的重复元素
     */
    //sc.makeRDD(Array(1,2,3,4,2,1,6,5,3,9)).distinct().foreach(println) //对数组去重后打印到控制台

    /**
     * 10 partitionBy 适用于key-value型,该函数来自于PairRDDFunctions.scala
     * 10.1声明 def partitionBy(partitioner: Partitioner): RDD[(K, V)]
     * 10.2参数  是一个分区器对象的实例
     * 10.3返回值  和原RDD类型相同
     * 10.4作用   按照自定的分区器,对RDD中的元素进行分区
     */
    val rdd6:RDD[Int] = sc.makeRDD(1 to 10)
    val rdd7:RDD[(Int,Int)] = rdd6.map(x => (x, x))  //将数组转成key-value型
    rdd7.partitionBy(new Partitioner {
      override def numPartitions: Int = 2 //设置分区数为2

      override def getPartition(key: Any): Int = key.asInstanceOf[Int] % 2  //将key分成奇数和偶数
    }).map(_._1)  //再转换成数组
      .mapPartitionsWithIndex((index,iter)=>Iterator((index,iter.mkString("|"))))  //按分区索引分,将元素用|分隔开
     // .foreach(println)  //打印结果(0,2|4|6|8|10)  (1,1|3|5|7|9)
  }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值