Spark(三)-- SparkSQL扩展(数据操作) -- 转换操作(一)

目录

8. Dataset (DataFrame) 的基础操作

8.1 有类型操作

8.1.1 转换类型的操作

8.1.2 过滤类型的操作

8.1.3 集合类型的操作

8.1.4 切分类型的操作

8.1.5 排序类型的操作

8.1.6 分区类型的操作

8.1.7 去重类型的操作

8.1.8 集合类型的操作

8.2 无类型操作

8.2.1 选择类型的操作

8.2.2 剪切类型的操作

8.2.3 聚合类型的操作


8. Dataset (DataFrame) 的基础操作

导读

这一章节主要目的是介绍 Dataset 的基础操作, 当然, DataFrame 就是 Dataset, 所以这些操作大部分也适用于 DataFrame

  1. 有类型的转换操作

  2. 无类型的转换操作

  3. 基础 Action

  4. 空值如何处理

  5. 统计操作

8.1 有类型操作

8.1.1 转换类型的操作

转换类型的操作主要包含:flatMap、map、mapPartitions、transform、as

(1)flatMap

方法描述:通过 flatMap 可以将一条数据转为一个数组, 后再展开这个数组放入 Dataset

    val ds = Seq("hello spark","hello hadoop").toDS
    ds.flatMap( x => x.split(" ")).show()

(2)map

方法描述:map 可以将数据集中每条数据转为另一种形式(通过传入计算函数来实现)

    val ds2 = Seq(Person("张三",18),Person("李四",20)).toDS()
    ds2.map( person => Person(person.name,person.age*2)).show()

(3)mapPartitions

方法描述:mapPartitions 和 map 一样, 但是 map 的处理单位是每条数据, mapPartitions 的处理单位是每个分区

    ds2.mapPartitions(
      //iter 不能大到每个Executor的内存放不下,不然就会OOM
      //对每个元素进行转换,后生成一个新的集合
      iter => {
        val result = iter.map(person => Person(person.name,person.age*2))
        result
      }
    ).show()

(4)transform

方法描述:map 和 mapPartitions 以及 transform 都是转换, map 和 mapPartitions 是针对数据, 而 transform 是针对整个数据集, 这种方式最大的区别就是 transform 可以直接拿到 Dataset 进行操作

    val ds = spark.range(10)
    //对数据集的操作
    ds.transform(dataset => dataset.withColumn("doubled",'id*2))
      .show()

(5)as

方法描述:as[Type] 算子的主要作用是将弱类型的 Dataset 转为强类型的 Dataset, 它有很多适用场景, 但是最常见的还是在读取数据的时候, 因为 DataFrameReader 体系大部分情况下是将读出来的数据转换为 DataFrame 的形式, 如果后续需要使用 Dataset 的强类型 API, 则需要将 DataFrame 转为 Dataset. 可以使用 as[Type] 算子完成这种操作

  @Test
  def as(): Unit = {
    // 1. 读取
    val schema = StructType(
      Seq(
        StructField("name", StringType),
        StructField("age", IntegerType),
        StructField("gpa", FloatType)
      )
    )

    val df: DataFrame = spark.read
      .schema(schema)
      .option("delimiter", "\t")
      .csv("dataset/studenttab10k")

    // 2. 转换
    // 本质上: Dataset[Row].as[Student] => Dataset[Student]
    // Dataset[(String, int, float)].as[Student] => Dataset[Student]
    val ds: Dataset[Student] = df.as[Student]

    // 3. 输出
    ds.show()
  }

8.1.2 过滤类型的操作

过滤类型的操作主要包含:filter

(1)filter

方法描述:用来按照条件过滤数据集

    val ds = Seq(Person("张三",15),Person("李四",20)).toDS()
    ds.filter( person => person.age>15 ).show()

8.1.3 集合类型的操作

集合类型的操作主要包含:groupBykey

(1)groupByKey

方法描述:grouByKey 算子的返回结果是 KeyValueGroupedDataset, 而不是一个 Dataset, 所以必须要先经过 KeyValueGroupedDataset 中的方法进行聚合, 再转回 Dataset, 才能使用 Action 得出结果。
其实这也印证了分组后必须聚合的道理

  @Test
  def groupByKey():Unit={
    val ds = Seq(Person("张三",15),Person("张三",15),Person("李四",20)).toDS()
    //select count(*) from person group by name
    val grouped: KeyValueGroupedDataset[String, Person] = ds.groupByKey(person => person.name)
    val result: Dataset[(String, Long)] = grouped.count()//聚合操作
    result.show()
  }

8.1.4 切分类型的操作

切分类型的操作主要包含:randomSplit、sample

(1)randomSplit

方法描述:randomSplit 会按照传入的权重随机将一个 Dataset 分为多个 Dataset, 传入 randomSplit 的数组有多少个权重, 最终就会生成多少个 Dataset, 这些权重的加倍和应该为 1, 否则将被标准化

    val ds = spark.range(15)
    //randomSplit:切多少分,权重多少
    val datasets: Array[Dataset[lang.Long]] = ds.randomSplit(Array(5,2,3))
    datasets.foreach(_.show())

(2)sample

方法描述:sample 会随机在 Dataset 中抽样

    val ds = spark.range(15)

    //sample 有放回
    ds.sample(withReplacement = false,fraction = 0.4).show()

8.1.5 排序类型的操作

排序类型的操作主要包含:orderBy、sort

(1)orderBy

方法描述:orderBy 配合 Column 的 API, 可以实现正反序排列

    val ds =  Seq(Person("张三",15),Person("张三",15),Person("李四",20)).toDS()
    ds.orderBy('age.desc).show()

(2)sort

方法描述:其实 orderBy 是 sort 的别名, 所以它们所实现的功能是一样的

    val ds =  Seq(Person("张三",15),Person("张三",15),Person("李四",20)).toDS()
    ds.sort('age.asc).show()

8.1.6 分区类型的操作

分区类型的操作主要包含:coalesce、repartitions

(1)coalesce

方法描述:减少分区, 此算子和 RDD 中的 coalesce 不同, Dataset 中的 coalesce 只能减少分区数, coalesce 会直接创建一个逻辑操作, 并且设置 Shuffle 为 false

val ds = spark.range(15)
ds.coalesce(1).explain(true)

(2)repartitions

方法描述:repartitions 有两个作用, 一个是重分区到特定的分区数, 另一个是按照某一列来分区, 类似于 SQL 中的 DISTRIBUTE BY

val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.repartition(4)
ds.repartition('name)

8.1.7 去重类型的操作

去重类型的操作主要包含:dropDuplicates、distinct

(1)dropDuplicates

方法描述:使用 dropDuplicates 可以去掉某一些列中重复的行

    val ds =  Seq(Person("张三",15),Person("张三",15),Person("李四",20)).toDS()
    ds.dropDuplicates("age").show()

(2)distinct

方法描述:当 dropDuplicates 中没有传入列名的时候, 其含义是根据所有列去重, dropDuplicates() 方法还有一个别名, 叫做 distinct

所以, 使用 distinct 也可以去重, 并且只能根据所有的列来去重

    val ds =  Seq(Person("张三",15),Person("张三",15),Person("李四",20)).toDS()
    ds.distinct().show()

8.1.8 集合类型的操作

集合类型的操作主要包含:except、intersect、union、limit

(1)except

方法描述:except 和 SQL 语句中的 except 一个意思, 是求得 ds1 中不存在于 ds2 中的数据, 其实就是差集

    val ds1 = spark.range(1,10)
    val ds2 = spark.range(5,15)

    //差集 ds1中去掉ds2中有的数据
    ds1.except(ds2).show()

(2)intersect

方法描述:求得两个集合的交集

    val ds1 = spark.range(1,10)
    val ds2 = spark.range(5,15)

    //交集
    ds1.intersect(ds2).show()

(3)union

方法描述:求得两个集合的并集

    val ds1 = spark.range(1,10)
    val ds2 = spark.range(5,15)

    //并集
    ds1.union(ds2).show()

(4)limit

方法描述:限制结果集数量

    val ds1 = spark.range(1,10)
    val ds2 = spark.range(5,15)

    //limit
    ds1.limit(3).show()

 

8.2 无类型操作

8.2.1 选择类型的操作

选择类型的操作主要包含:select、selectExpr、withColumn、withColumnRenamed

(1)select

方法描述:select 用来选择某些列出现在结果集中

(2)selectExpr

方法描述:在 SQL 语句中, 经常可以在 select 子句中使用 count(age), rand() 等函数, 在 selectExpr 中就可以使用这样的 SQL 表达式, 同时使用 select 配合 expr 函数也可以做到类似的效果

  val spark = SparkSession.builder().master("local[6]").appName("typed").getOrCreate()

  import spark.implicits._

  @Test
  def select():Unit={
    val ds =  Seq(Person("zhangsan",12),Person("lisi",15),Person("zhangsan",18)).toDS()
    //在Dataset中,select可以在任何位置调用
    ds.select('name).show()

    ds.selectExpr("sum(age)").show()

    import org.apache.spark.sql.functions._

    ds.select(expr("sum(age)")).show()
  }

(3)withColumn

方法描述:通过 Column 对象在 Dataset 中创建一个新的列或者修改原来的列

(4)withColumnRenamed

方法描述:修改列名

  @Test
  def column():Unit= {
    val ds = Seq(Person("zhangsan", 12), Person("lisi", 15), Person("zhangsan", 18)).toDS()

    import org.apache.spark.sql.functions._

    //如果想使用函数功能,有两种方式
    //1.使用function.xx   2.使用表达式,可以使用expr("") 随时随地编写表达式
    ds.withColumn("random",expr("rand()")).show()

    //重命名
    ds.withColumn("name_new",'name).show()

    ds.withColumn("name_jok",'name === "").show()

    ds.withColumnRenamed("name","new_name").show()
  }

 

8.2.2 剪切类型的操作

剪切类型的操作主要包含:drop

(1)drop

方法描述:剪掉某个列

import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.drop('age).show()

8.2.3 聚合类型的操作

聚合类型的操作主要包含:groupBy

(1)groupBy

方法描述:按照给定的行进行分组

  @Test
  def groupBy():Unit = {
    val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

    //为什么GroupByKey 是有类型的
    //最主要的原因是因为goupByKey 所生成的对象中的算子是有类型的
//    ds.groupByKey(x => x.name).mapValues()

    //为什么 GroupBy是无类型的
    //因为groupBy所生成的对象中的算子是无类型的,针对列进行处理的
    import org.apache.spark.sql.functions._
    ds.groupBy("name").agg(mean("age")).show()

  }

 

以上便是 SparkSQL关于数据操作中数据转换相关的算子用法.

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值