Spark(三)-- SparkSQL扩展(数据操作) -- 缺失值处理(三)

9.缺失值的处理

导读

  1. DataFrame 中什么时候会有无效值

  2. DataFrame 如何处理无效的值

  3. DataFrame 如何处理 null

9.1 缺失值的处理思路

如果想探究如何处理无效值, 首先要知道无效值从哪来, 从而分析可能产生的无效值有哪些类型, 在分别去看如何处理无效值

什么是缺失值

一个值本身的含义是这个值不存在则称之为缺失值, 也就是说这个值本身代表着缺失, 或者这个值本身无意义, 比如说 null, 比如说空字符串

关于数据的分析其实就是统计分析的概念, 如果这样的话, 当数据集中存在缺失值, 则无法进行统计和分析, 对很多操作都有影响

缺失值如何产生的

Spark 大多时候处理的数据来自于业务系统中, 业务系统中可能会因为各种原因, 产生一些异常的数据

例如说因为前后端的判断失误, 提交了一些非法参数. 再例如说因为业务系统修改 MySQL 表结构产生的一些空值数据等. 总之在业务系统中出现缺失值其实是非常常见的一件事, 所以大数据系统就一定要考虑这件事.

缺失值的类型

常见的缺失值有两种

  • nullNaN 等特殊类型的值, 某些语言中 null 可以理解是一个对象, 但是代表没有对象, NaN 是一个数字, 可以代表不是数字

    针对这一类的缺失值, Spark 提供了一个名为 DataFrameNaFunctions 特殊类型来操作和处理

  • "Null""NA"" " 等解析为字符串的类型, 但是其实并不是常规字符串数据

    针对这类字符串, 需要对数据集进行采样, 观察异常数据, 总结经验, 各个击破

9.2 DataFrameNaFunctions

DataFrameNaFunctions 使用 Dataset 的 na 函数来获取

val df = ...
val naFunc: DataFrameNaFunctions = df.na

当数据集中出现缺失值的时候, 大致有两种处理方式, 一个是丢弃, 一个是替换为某值, DataFrameNaFunctions 中包含一系列针对空值数据的方案

  • DataFrameNaFunctions.drop 可以在当某行中包含 null 或 NaN 的时候丢弃此行

  • DataFrameNaFunctions.fill 可以在将 null 和 NaN 充为其它值

  • DataFrameNaFunctions.replace 可以把 null 或 NaN 替换为其它值, 但是和 fill 略有一些不同, 这个方法针对值来进行替换

9.3 如何使用 SparkSQL 处理 null 和 NaN ?

首先要将数据读取出来, 此次使用的数据集直接存在 NaN, 在指定 Schema 后, 可直接被转为 Double.NaN

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

  @Test
  def nullAndNaN(): Unit = {
    //1.创建SparkSession
    //2.导入数据集
    //3.读取数据集
    //3.1通过Spark-csv自动的推断类型来读取,推断数字的时候会将NaN推断为字符串
    /*spark.read
      .option("header",true)
      .option("inferSchema",true)
      .csv("dataset/beijingpm_with_nan.csv")*/
    //3.2直接读取字符串,在后续的操作中使用map算子转类型
    /*spark.read.csv().map(row => row...)*/
    //3.3指定schema,不要自动推断
    val schema = StructType(
      List(
        StructField("id",LongType),
        StructField("year",IntegerType),
        StructField("month",IntegerType),
        StructField("day",IntegerType),
        StructField("hour",IntegerType),
        StructField("season",IntegerType),
        StructField("pm",DoubleType)
      )
    )

    //自动会将"NaN" 转为Double.NaN
    val sourceDF = spark.read
      .option("header",value = true)
      .schema(schema)
      .csv("dataset/beijingpm_with_nan.csv")

    sourceDF.show()

    //4.丢弃
    // 2019,12,12,NaN
    // 规则
    // (1).any,只要有一个列NaN就丢弃
    sourceDF.na.drop("any").show()
    sourceDF.na.drop().show()
    // (2).all,所有的列数据都是NaN的行才丢弃
    sourceDF.na.drop("all").show()
    // (3).某些列的规则
    sourceDF.na.drop("any",List("year","month","day","hour")).show()

    //5.填充
    //规则:
    //1.针对所有列数据进行默认值填充
    sourceDF.na.fill(0).show()
    //2.针对特定列填充
    sourceDF.na.fill(0,List("year","month")).show()
  }

对于缺失值的处理一般就是丢弃和填充

9.3.1 丢弃包含 null 和 NaN 的行

当某行数据所有值都是 null 或者 NaN 的时候丢弃此行

df.na.drop("all").show()

当某行中特定列所有值都是 null 或者 NaN 的时候丢弃此行

df.na.drop("all", List("pm", "id")).show()

当某行数据任意一个字段为 null 或者 NaN 的时候丢弃此行

df.na.drop().show()
df.na.drop("any").show()

当某行中特定列任意一个字段为 null 或者 NaN 的时候丢弃此行

df.na.drop(List("pm", "id")).show()
df.na.drop("any", List("pm", "id")).show()

9.3.2 填充包含 null 和 NaN 的列

填充所有包含 null 和 NaN 的列

df.na.fill(0).show()

填充特定包含 null 和 NaN 的列

df.na.fill(0, List("pm")).show()

根据包含 null 和 NaN 的列的不同来填充

import scala.collection.JavaConverters._

df.na.fill(Map[String, Any]("pm" -> 0).asJava).show

9.4 如何使用 SparkSQL 处理异常字符串 ?

读取数据集, 这次读取的是最原始的那个 PM 数据集

  @Test
  def strProcessor():Unit = {
    //读取数据集
    val sourceDF = spark.read
      .option("header",true)
      .option("inferSchema",true)
      .csv("dataset/BeijingPM20100101_20151231.csv")
    sourceDF.show()
    //1.丢弃
    import spark.implicits._
    sourceDF.where('PM_Dongsi =!= "NA").show()
    //2.替换
    import org.apache.spark.sql.functions._
    //select name,age,case
    //when ... then ...
    //when ... then ...
    //else
    sourceDF.select(
      'No as "id",'year,'month,'day,'hour,'season,
      when('PM_Dongsi === "NA",Double.NaN)
        .otherwise('PM_Dongsi cast DoubleType) //不是NA将正常值转为Double
        .as("pmd")
    ).show()

    //需要注意:原类型和转换过后的类型,必须一致
    sourceDF.na.replace("",Map("NA" -> "NAN","NULL" -> "null")).show()
  }

9.4.1 使用函数直接转换非法的字符串

df.select('No as "id", 'year, 'month, 'day, 'hour, 'season,
    when('PM_Dongsi === "NA", 0)
    .otherwise('PM_Dongsi cast DoubleType)
    .as("pm"))
  .show()

9.4.2 使用 where 直接过滤

df.select('No as "id", 'year, 'month, 'day, 'hour, 'season, 'PM_Dongsi)
  .where('PM_Dongsi =!= "NA")
  .show()

9.4.3 使用 DataFrameNaFunctions 替换, 但是这种方式被替换的值和新值必须是同类型

df.select('No as "id", 'year, 'month, 'day, 'hour, 'season, 'PM_Dongsi)
  .na.replace("PM_Dongsi", Map("NA" -> "NaN"))
  .show()

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值