spark中dataframe,dataset,sparksql中的各种用法

package org.apache.spark.examples


import DsFilter.Student
import org.apache.spark.{HashPartitioner, Partitioner}
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.types.StructType

object DsFilter {

  def main(args: Array[String]): Unit = {
    System.setProperty("hadoop.home.dir","E:\\office\\hadoop-2.7.1") //如果读取不到环境变量,设置这个
    val spark: SparkSession = SparkSession
      .builder
      .appName("Spark Examples")
      .master("local[*]") //我的机器是4core
      .getOrCreate()

    var list = Seq[Student]()
    for (i <- 1 to 10){
      list= list :+ Student("cc",i,i.toString)
      list= list :+ Student("chi",i,i.toString)
      list= list:+Student("zbf",i,i.toString)
      list= list:+Student("dd",i,i.toString)
    }//创建40条记录
    val userList=Seq(User("cc",true),User("chi",true),User("dd",false),User("zbf",false))
    val rdd: RDD[Student] = spark.sparkContext.parallelize(list) //parallelize(list , numpartition) 这里可以指定分区数,默认是hash分区,既然是hash分区那么可能分配不均
    val partitions: Long = rdd.count()
    println(s"rdd中的数据量=${partitions}") // 40
    printRDDPartition(rdd)
    /**
     * 第0个分区-------(cc,1,1),(chi,1,1),(zbf,1,1),(dd,1,1),(cc,2,2),(chi,2,2),(zbf,2,2),(dd,2,2),(cc,3,3),(chi,3,3)
     * 第1个分区-------(zbf,3,3),(dd,3,3),(cc,4,4),(chi,4,4),(zbf,4,4),(dd,4,4),(cc,5,5),(chi,5,5),(zbf,5,5),(dd,5,5)
     * 第2个分区-------(cc,6,6),(chi,6,6),(zbf,6,6),(dd,6,6),(cc,7,7),(chi,7,7),(zbf,7,7),(dd,7,7),(cc,8,8),(chi,8,8)
     * 第3个分区-------(zbf,8,8),(dd,8,8),(cc,9,9),(chi,9,9),(zbf,9,9),(dd,9,9),(cc,10,10),(chi,10,10),(zbf,10,10),(dd,10,10)
     */
    val studentName: Array[String] = rdd.map(_.name).collect().distinct //获取有多少个name, 目前是四个
    val hashparitionedRddTurple: RDD[(String, Student)] = rdd.map(x=>(x.name,x)).partitionBy(new HashPartitioner(4))
    val hashpartitonedRDd: RDD[Student] = hashparitionedRddTurple.map(_._2) //里面的元素还是那40条 但是存放位置不一样了
    println("hashpartitonedRDd分区后的数据")
    printRDDPartition(hashpartitonedRDd)
    /**
     * 第0个分区-------(cc,1,1),(chi,1,1),(dd,1,1),(cc,2,2),(chi,2,2),(dd,2,2),(cc,3,3),(chi,3,3),(dd,3,3),(cc,4,4),(chi,4,4),(dd,4,4),(cc,5,5),(chi,5,5),(dd,5,5),(cc,6,6),(chi,6,6),(dd,6,6),(cc,7,7),(chi,7,7),(dd,7,7),(cc,8,8),(chi,8,8),(dd,8,8),(cc,9,9),(chi,9,9),(dd,9,9),(cc,10,10),(chi,10,10),(dd,10,10)
     * 第1个分区-------
     * 第2个分区-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
     * 第3个分区-------
     */
    val paritionedRddTurple: RDD[(String, Student)] = rdd.map(x=>(x.name,x)).partitionBy(new MyPartition(studentName))
    val partitonedRDd: RDD[Student] = paritionedRddTurple.map(_._2) //里面的元素还是那40条 但是存放位置不一样了
    println("rdd分区后的数据")
    printRDDPartition(partitonedRDd)
    /**
     * 第0个分区-------(cc,1,1),(cc,2,2),(cc,3,3),(cc,4,4),(cc,5,5),(cc,6,6),(cc,7,7),(cc,8,8),(cc,9,9),(cc,10,10)
     * 第1个分区-------(chi,1,1),(chi,2,2),(chi,3,3),(chi,4,4),(chi,5,5),(chi,6,6),(chi,7,7),(chi,8,8),(chi,9,9),(chi,10,10)
     * 第2个分区-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
     * 第3个分区-------(dd,1,1),(dd,2,2),(dd,3,3),(dd,4,4),(dd,5,5),(dd,6,6),(dd,7,7),(dd,8,8),(dd,9,9),(dd,10,10)
     */
    /*   rdd中的数据其实看似有序,但是对于我们来说大部分无序,所以需要我们自己定义分区器,
    自定义分区的作用:一个分区做一件事,
                    ①比如我要输出文件有序第一个文件是cc第二个是chi第三个是zbf,现在4个分区最后输出文件是4个
                    ②每个分区数据有不同的逻辑 比如我想cc的age+1,chi的age+2
                    rdd.foreachPartition(iter=>{
                        val list: List[Student] = iter.toList
                       if(list.head.name.equals("cc")){}
                       if(list.head.name.equals("chi")){}
                    })
                    // 下面是lowb方法,先过滤出需要的rdd然后再处理
                    rdd.filter(_.name.equals("cc"))
                    rdd.filter(_.name.equals("chi"))
                    多嘴说一句
                    rdd.foreachPartition作用 比如我现在rdd是4个分区,数据是40个
                    如果涉及到数据库连接建议用foreaparition 此时只需要创建4个连接,每个连接对应一个分区
                    如果用foreach 则需要创建40个连接 每个连接对应一条数据
                    有人会说我在最外面建立一个就好对应整个数据集合,不可能,这个涉及到序列化问题,简单来说上面两个连接都是在worker端建立
                    //driver端
                    建立连接 对应整个数据 然后driver到work相当于两个世界,要把driver的数据到work相当于快递要打包(序列化),目前连接不能序列化
                    foreach/partition(
                    //work端
                    //建立连接 对应一条数据或者一个分区
                    )
    */

    import spark.implicits._   //导入隐世转换,因为rdd是org.apache.spark.rdd包下的本身没有toDF方法,所以导入spark.implicits._包,才有rdd.todf
    val ds: Dataset[User] = spark.createDataset(userList)
    val rddToDf: DataFrame = rdd.toDF() //注意 rdd中是student样例类所以可以直接转化为,如果是List(Seq("cc",1,"1"),Seq("cc",2,"2")) 要toDF("name","age","score")
    rddToDf.show(5) //action算子,自己测试时可以用,实际生产个人不建议频繁用
    /**
     * +----+---+-----+
     * |name|age|score|
     * +----+---+-----+
     * |  cc|  1|    1|
     * | chi|  1|    1|
     * | zbf|  1|    1|
     * |  dd|  1|    1|
     * |  cc|  2|    2|
     * +----+---+-----+
     * only showing top 5 rows
     */
    //df的repartition
    val dfPartitionedRdd: RDD[Student] = rddToDf.repartition(4,new Column("name")).rdd.map(x=>Student(x.getString(0),x.getInt(1),x.getString(2)))
    println("rdd转化成df后repartition后每个rdd分区")
    printRDDPartition(dfPartitionedRdd)
    /**  说明下,这里df.repartition 根据name分区,是根据name 的hashcode值,以上述为例
     * cc.hashcode=1 chi.hashcode=1 dd.hashcode=0 zbf.hashcode=3
     * 取余后 dd分配到0分区,cc和chi分配到1分区,zbf分配到3分区,
     * df的reparition 和 rdd.partition都是hash分区
     * 第0个分区-------(dd,1,1),(dd,2,2),(dd,3,3),(dd,4,4),(dd,5,5),(dd,6,6),(dd,7,7),(dd,8,8),(dd,9,9),(dd,10,10)
     * 第1个分区-------(cc,1,1),(chi,1,1),(cc,2,2),(chi,2,2),(cc,3,3),(chi,3,3),(cc,4,4),(chi,4,4),(cc,5,5),(chi,5,5),(cc,6,6),(chi,6,6),(cc,7,7),(chi,7,7),(cc,8,8),(chi,8,8),(cc,9,9),(chi,9,9),(cc,10,10),(chi,10,10)
     * 第2个分区-------
     * 第3个分区-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
     */
    rddToDf.describe("name","age","score").show()
    /** //科学计数法
     * +-------+----+------------------+
     * |summary|name|               age|
     * +-------+----+------------------+
     * |  count|  30|                30|
     * |   mean|null|               5.5|
     * | stddev|null|2.9213837061606074|
     * |    min|  cc|                 1|
     * |    max| zbf|                10|
     * +-------+----+------------------+
     */
    println(" rddToDf.printSchema()=")
    rddToDf.printSchema()
    /**
     * root
     * |-- name: string (nullable = true)
     * |-- age: integer (nullable = false)
     * |-- score: string (nullable = true)
     */
    val column1: Column = rddToDf.apply("name")
    println(s"rddToDf.apply=${column1}")
    /**
     * rddToDf.apply=name
     */
    val schema: StructType = rddToDf.schema
    println(s" rddToDf.schema=${schema.mkString("|")}")
    /**
     * rddToDf.schema=StructField(name,StringType,true)|StructField(age,IntegerType,false)|StructField(score,StringType,true)
     */
    val columns: Array[String] = rddToDf.columns
    println(s" rddToDf.columns=${columns.mkString("|")}")
    /**
     * rddToDf.columns=name|age|score
     */
    val dtypes: Array[(String, String)] = rddToDf.dtypes
    println(s" rddToDf.dtypes=${dtypes.mkString("|")}")
    /**
     * rddToDf.dtypes=(name,StringType)|(age,IntegerType)|(score,StringType)
     */
    val column: Column = rddToDf.col("name")
    println(s"column=${column}")
    /**
     * column=name
     */
    rddToDf.explain() //目前层次未到 有点难看懂
    rddToDf.createTempView("Student") //创建一张临时表
    val df: DataFrame = spark.sql("select * from Student")
    df.show(5)
    /**
     * +----+---+-----+
     * |name|age|score|
     * +----+---+-----+
     * |  cc|  1|    1|
     * | chi|  1|    1|
     * | zbf|  1|    1|
     * |  dd|  1|    1|
     * |  cc|  2|    2|
     * +----+---+-----+
     * only showing top 5 rows
     */

    import org.apache.spark.sql.functions._ //下面这种属于DSL风格sql查询,方法也是再functions._下的包里才有
    df.filter(col("name").equalTo("cc")).show(5)
    /**
     * +----+---+-----+
     * |name|age|score|
     * +----+---+-----+
     * |  cc|  1|    1|
     * |  cc|  2|    2|
     * |  cc|  3|    3|
     * |  cc|  4|    4|
     * |  cc|  5|    5|
     * +----+---+-----+
     */
    df.filter("age>5").show(5)
    /**
     * +----+---+-----+
     * |name|age|score|
     * +----+---+-----+
     * |  cc|  6|    6|
     * | chi|  6|    6|
     * | zbf|  6|    6|
     * |  dd|  6|    6|
     * |  cc|  7|    7|
     * +----+---+-----+
     * only showing top 5 rows
     */
      df.groupBy("name").agg(count(col("age")),avg(col("score"))).show(3)
    /**
     * +----+----------+----------+
     * |name|count(age)|avg(score)|
     * +----+----------+----------+
     * | zbf|        10|       5.5|
     * |  cc|        10|       5.5|
     * | chi|        10|       5.5|
     * +----+----------+----------+
     * only showing top 3 rows
     */
    df.groupBy("name").agg(max(col("age"))as("maxAge"),avg(col("score")).as("avgScore")).show(3)
    /** //取别名
     * +----+------+--------+
     * |name|maxAge|avgScore|
     * +----+------+--------+
     * | zbf|    10|     5.5|
     * |  cc|    10|     5.5|
     * | chi|    10|     5.5|
     * +----+------+--------+
     * only showing top 3 rows
     */
    df.cube("name","age").agg(sum("score").as("score")).orderBy(col("name"),col("score")).show(100)
    /**55条记录 =40条记录+4条name=null + 10条age=null +1条name=null&&age=null
     * select name ,age ,sum(score) as score from student groupby name ,age
     * union select null age ,sum(score) as score from student groupby age
     * union select name,null ,sum(score) as score from student groupby name
     * union select null,null,sum(score) as score from student groupby name
     * +----+----+-----+
     * |name| age|score|
     * +----+----+-----+
     * |null|   1|  4.0|
     * |null|   2|  8.0|
     * |null|   3| 12.0|
     * |null|   4| 16.0|
     * |null|   5| 20.0|
     * |null|   6| 24.0|
     * |null|   7| 28.0|
     * |null|   8| 32.0|
     * |null|   9| 36.0|
     * |null|  10| 40.0|
     * |null|null|220.0|
     * |  cc|   1|  1.0|
     * |  cc|   2|  2.0|
     * |  cc|   3|  3.0|
     * |  cc|   4|  4.0|
     * |  cc|   5|  5.0|
     * |  cc|   6|  6.0|
     * |  cc|   7|  7.0|
     * |  cc|   8|  8.0|
     * |  cc|   9|  9.0|
     * |  cc|  10| 10.0|
     * |  cc|null| 55.0|
     * | chi|   1|  1.0|
     * | chi|   2|  2.0|
     * | chi|   3|  3.0|
     * | chi|   4|  4.0|
     * | chi|   5|  5.0|
     * | chi|   6|  6.0|
     * | chi|   7|  7.0|
     * | chi|   8|  8.0|
     * | chi|   9|  9.0|
     * | chi|  10| 10.0|
     * | chi|null| 55.0|
     * |  dd|   1|  1.0|
     * |  dd|   2|  2.0|
     * |  dd|   3|  3.0|
     * |  dd|   4|  4.0|
     * |  dd|   5|  5.0|
     * |  dd|   6|  6.0|
     * |  dd|   7|  7.0|
     * |  dd|   8|  8.0|
     * |  dd|   9|  9.0|
     * |  dd|  10| 10.0|
     * |  dd|null| 55.0|
     * | zbf|   1|  1.0|
     * | zbf|   2|  2.0|
     * | zbf|   3|  3.0|
     * | zbf|   4|  4.0|
     * | zbf|   5|  5.0|
     * | zbf|   6|  6.0|
     * | zbf|   7|  7.0|
     * | zbf|   8|  8.0|
     * | zbf|   9|  9.0|
     * | zbf|  10| 10.0|
     * | zbf|null| 55.0|
     * +----+----+-----+
     */
    df.rollup("name","age").agg(sum("score").as("score")).orderBy(col("name"),col("score")).show(100)
    /**45条记录=40 + 4条name分组 和1条 不分组
     * select name ,age ,sum(score) as score from student groupby name ,age
     * union select name,null ,sum(score) as score from student groupby name
     * union select null,null,sum(score) as score from student groupby name
     * !!!!!区别  个人理解!!!!!!
     * cube立方三维,借用kylin的思想就是从多个角度去看分组,如果我分组维度有3个,name,age,score,那么先减去1个维度获得【(name,age)(name,score)(age.score)】,然后减去2个维度获得[(name),(age),(score)],再减去3个维度【】
     * rollup,向上 注意比如我name age 分组,(注意顺序!!)那么我还是逐渐减少分组维度,向上递减,先减去age这个维度获得4条记录,然后减去name这个维度 获得1条记录
     * +----+----+-----+
     * |name| age|score|
     * +----+----+-----+
     * |null|null|220.0|
     * |  cc|   1|  1.0|
     * |  cc|   2|  2.0|
     * |  cc|   3|  3.0|
     * |  cc|   4|  4.0|
     * |  cc|   5|  5.0|
     * |  cc|   6|  6.0|
     * |  cc|   7|  7.0|
     * |  cc|   8|  8.0|
     * |  cc|   9|  9.0|
     * |  cc|  10| 10.0|
     * |  cc|null| 55.0|
     * | chi|   1|  1.0|
     * | chi|   2|  2.0|
     * | chi|   3|  3.0|
     * | chi|   4|  4.0|
     * | chi|   5|  5.0|
     * | chi|   6|  6.0|
     * | chi|   7|  7.0|
     * | chi|   8|  8.0|
     * | chi|   9|  9.0|
     * | chi|  10| 10.0|
     * | chi|null| 55.0|
     * |  dd|   1|  1.0|
     * |  dd|   2|  2.0|
     * |  dd|   3|  3.0|
     * |  dd|   4|  4.0|
     * |  dd|   5|  5.0|
     * |  dd|   6|  6.0|
     * |  dd|   7|  7.0|
     * |  dd|   8|  8.0|
     * |  dd|   9|  9.0|
     * |  dd|  10| 10.0|
     * |  dd|null| 55.0|
     * | zbf|   1|  1.0|
     * | zbf|   2|  2.0|
     * | zbf|   3|  3.0|
     * | zbf|   4|  4.0|
     * | zbf|   5|  5.0|
     * | zbf|   6|  6.0|
     * | zbf|   7|  7.0|
     * | zbf|   8|  8.0|
     * | zbf|   9|  9.0|
     * | zbf|  10| 10.0|
     * | zbf|null| 55.0|
     * +----+----+-----+
     */
    df.withColumn("newname",col("name")).show(3)
    /** 新增一列
     * +----+---+-----+-------+
     * |name|age|score|newname|
     * +----+---+-----+-------+
     * |  cc|  1|    1|     cc|
     * | chi|  1|    1|    chi|
     * | zbf|  1|    1|    zbf|
     * +----+---+-----+-------+
     * only showing top 3 rows
     */
    df.withColumn("newname",col("name")).select("name","age","score").show(3)
    /**
     * +----+---+-----+-------+
     * |name|age|score|newname|
     * +----+---+-----+-------+
     * |  cc|  1|    1|     cc|
     * | chi|  1|    1|    chi|
     * | zbf|  1|    1|    zbf|
     * +----+---+-----+-------+
     * only showing top 3 rows
     */
    df.select(col("name").equalTo("cc")).show(5)
    /**这个刚好和上面对应 cc chi zbf dd cc 刚好是true false false false true
     * +-----------+
     * |(name = cc)|
     * +-----------+
     * |       true|
     * |      false|
     * |      false|
     * |      false|
     * |       true|
     * +-----------+
     * only showing top 5 rows
     */
    df.select(col("age").lt(5)).show(5) //小于
    /**
     * +---------+
     * |(age < 5)|
     * +---------+
     * |     true|
     * |     true|
     * |     true|
     * |     true|
     * |     true|
     * +---------+
     * only showing top 5 rows
     */
    val dsBC: Broadcast[Dataset[User]] = spark.sparkContext.broadcast(ds)
    df.join(ds,df("name")===ds("name"),"outer").orderBy(col("score")).show(4)
    /**两张表关联
     * 'inner', 'outer', 'full', 'fullouter', 'full_outer', 'leftouter', 'left', 'left_outer',
     * 'rightouter', 'right', 'right_outer', 'leftsemi', 'left_semi', 'leftanti', 'left_anti', 'cross'
     * +----+---+-----+----+-----+
     * |name|age|score|name|shuai|
     * +----+---+-----+----+-----+
     * |  cc|  1|    1|  cc| true|
     * | chi|  1|    1| chi| true|
     * | zbf|  1|    1| zbf|false|
     * |  dd|  1|    1|  dd|false|
     * +----+---+-----+----+-----+
     * only showing top 10 rows
     */



  }

  //打印输出
  def printRDDPartition(rdd:RDD[Student]): Unit ={
    val array: Array[Array[Student]] = rdd.glom().collect()
    for(i <- 0 until  array.size){
      println(s"第${i}个分区-------${array(i).map(x=>{
        (x.name,x.age,x.score)
      }).mkString(",")}")
    }
  }
  case class Student(name: String, age: Int,score:String){}
  case class User(name: String, shuai:Boolean){}
  class MyPartition(studentName:Array[String]) extends  Partitioner{
    override def numPartitions: Int = studentName.size //分区数
    override def getPartition(key: Any): Int = { //这个方法是那40条数据,每条数据都会把name 传进来返回一个分区号
      val index: Array[(String, Int)] = studentName.zipWithIndex //给每个名字固定一个下标 =>其实也就是第几个分区
      val tuples: Array[(String, Int)] = index.filter(_._1.equals(key.toString)) //在index数组中找到传来名字的属于哪个分区
      tuples.head._2 //head是因为 数据去重了只有一条
    }
  }

}

你可能感兴趣的:(spark)