通过例子学习spark dataframe -- transformations函数(2)

来源:互联网 发布:分布式数据库的安全性 编辑:程序博客网 时间:2024/06/07 22:19

  • 通过例子学习spark dataframe transformations函数2
    • 说明
      • 数据说明
      • agg
      • groupBy
      • apply 和 col
      • cube
      • drop
      • join
      • na
      • select
      • withColumn 和 withColumnRenamed
      • stat

通过例子学习spark dataframe – transformations函数(2)

说明

transformations函数分为两类:
* Typed transformations
这类函数的返回值一般都是dataset,也就是说不会改变原来的数据集的类型。
* Untyped transformations
这类函数的返回值,会根据不同的函数返回不同的类型。

本文的所有例子,都是基于spark-2.1进行操作。
本文的所有例子,都是基于以下简单的csv数据集进行讲解:

数据说明

import org.apache.spark.sql.SparkSessionval spark = SparkSession.builder().appName("Spark SQL basic example").config("spark.some.config.option", "some-value").getOrCreate()import spark.implicits._val df = spark.read.format("csv").option("header", true).load("/user/hadoop/csvdata/csvdata")df.show()scala >scala> df.show()+---+----+-------+-----+| id|name|subject|score|+---+----+-------+-----+|  1|  n1|     s1|   10||  2|  n2|     s2|   20||  3|  n3|     s3|   30||  3|  n3|     s1|   20||  4|  n4|     s2|   40||  5|  n5|     s3|   50||  6|  n6|     s1|   60||  7|  n6|     s2|   40||  8|  n8|     s3|   90||  8|  n9|     s1|   30||  9|  n9|     s1|   20||  9|  n9|     s2|   70|+---+----+-------+-----+

agg

  • 功能说明
    在整个数据集范围类进行聚合操作。该函数相当于:
ds.groupBy().agg(...)
  • 函数原型
def agg(expr: Column, exprs: Column*): DataFrame def agg(exprs: Map[String, String]): DataFrame def agg(exprs: Map[String, String]): DataFrame def agg(aggExpr: (String, String), aggExprs: (String, String)*): DataFrame 
  • 例子1
scala> df.agg("score"->"avg", "score"->"max", "score"->"min", "score"->"count").show()+----------+----------+----------+------------+|avg(score)|max(score)|min(score)|count(score)|+----------+----------+----------+------------+|      40.0|        90|        10|          12|+----------+----------+----------+------------+

groupBy

  • 功能
    使用指定的列对数据集进行分组,以便我们可以对其进行聚合。请参阅RelationalGroupedDataset获取所有可用的聚合函数。
    这是groupBy的一个变体,它只能使用列名称对现有列进行分组(即不能构造表达式)。

  • 函数原型

def groupBy(col1: String, cols: String*): RelationalGroupedDataset def groupBy(cols: Column*): RelationalGroupedDataset 
  • 例子1
    在使用groupBy函数时,一般都是先分组,在使用agg等聚合函数对数据进行聚合。
    按name字段进行聚合,然后再使用agg聚合函数进行聚合。
scala> df.groupBy("name").agg("score"->"avg").sort("name").show()+----+----------+|name|avg(score)|+----+----------+|  n1|      10.0||  n2|      20.0||  n3|      25.0||  n4|      40.0||  n5|      50.0||  n6|      50.0||  n8|      90.0||  n9|      40.0|+----+----------+
  • 例子2
    按id和name两个字段对数据集进行分组,然后求score列的平均值。
scala> df.groupBy("id","name").agg("score"->"avg").sort("name").show()+---+----+----------+| id|name|avg(score)|+---+----+----------+|  1|  n1|      10.0||  2|  n2|      20.0||  3|  n3|      25.0||  4|  n4|      40.0||  5|  n5|      50.0||  7|  n6|      40.0||  6|  n6|      60.0||  8|  n8|      90.0||  9|  n9|      45.0||  8|  n9|      30.0|+---+----+----------+
  • 例子3
    计算每个subject的平均分数:
scala> df.groupBy("subject").agg("score"->"avg").sort("subject").show()+-------+------------------+|subject|        avg(score)|+-------+------------------+|     s1|              28.0||     s2|              42.5||     s3|56.666666666666664|+-------+------------------+
  • 例子4
    同时计算多个列值的平均值,最小值,最大值。
    (注:我这里用的是同一列,完全可以是不同列)
scala> df.groupBy("subject").agg("score"->"avg", "score"->"max", "score"->"min", "score"->"count").sort("subject").show()+-------+------------------+----------+----------+------------+|subject|        avg(score)|max(score)|min(score)|count(score)|+-------+------------------+----------+----------+------------+|     s1|              28.0|        60|        10|           5||     s2|              42.5|        70|        20|           4||     s3|56.666666666666664|        90|        30|           3|+-------+------------------+----------+----------+------------+

apply 和 col

  • 功能说明
    根据列名选择列并将其作为列返回。

  • 函数原型

def apply(colName: String): Column def col(colName: String): Column 
  • 例子1
scala> df.apply("name")res11: org.apache.spark.sql.Column = namescala> df.col("name")res16: org.apache.spark.sql.Column = name

cube

  • 功能说明
    使用指定的列为当前数据集创建一个多维数据集,因此我们可以对它们运行聚合。请参阅RelationalGroupedDataset获取所有可用的聚合函数。
    这是立方体的变体,只能使用列名称对现有列进行分组(即不能构造表达式)。

  • 原型

def cube(col1: String, cols: String*): RelationalGroupedDataset def cube(cols: Column*): RelationalGroupedDataset 
  • 例子1
scala> df.cube("name", "score")res18: org.apache.spark.sql.RelationalGroupedDataset = org.apache.spark.sql.RelationalGroupedDataset@3f88db17

drop

  • 函数功能
    删除数据集中的某个列。

  • 函数原型

def drop(col: Column): DataFrame def drop(colNames: String*): DataFrame def drop(colName: String): DataFrame 
  • 例子1
scala> df.drop("id").show()+----+-------+-----+|name|subject|score|+----+-------+-----+|  n1|     s1|   10||  n2|     s2|   20||  n3|     s3|   30||  n3|     s1|   20||  n4|     s2|   40||  n5|     s3|   50||  n6|     s1|   60||  n6|     s2|   40||  n8|     s3|   90||  n9|     s1|   30||  n9|     s1|   20||  n9|     s2|   70|+----+-------+-----+

join

  • join类型的说明
    内连接 : 只连接匹配的行
    左外连接 : 包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行
    右外连接 : 包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边表中全部匹配的行
    全外连接 : 包含左、右两个表的全部行,不管另外一边的表中是否存在与它们匹配的行。

  • 功能说明
    使用给定的连接表达式连接另一个DataFrame。以下执行df1和df2之间的完整外部联接。
    使用给定的连接表达式与另一个DataFrame进行内部连接。
    使用给定的列与另一个DataFrame进行设置连接。
    加入另一个DataFrame。

  • 函数原型

def join(right: Dataset[_], joinExprs: Column, joinType: String): DataFrame def join(right: Dataset[_], joinExprs: Column): DataFrame def join(right: Dataset[_], usingColumns: Seq[String], joinType: String): DataFrame def join(right: Dataset[_], usingColumns: Seq[String]): DataFrame // 内部使用给定的列与另一个DataFrame进行同等连接。def join(right: Dataset[_], usingColumn: String): DataFrame def join(right: Dataset[_]): DataFrame 

注意:这里的joinType必须是这几个中的一个:inner, outer, left_outer, right_outer, leftsemi.

  • 例子1
    该例子演示inner join。
scala> df.show()+---+----+-------+-----+| id|name|subject|score|+---+----+-------+-----+|  1|  n1|     s1|   10||  2|  n2|     s2|   20||  3|  n3|     s3|   30||  3|  n3|     s1|   20||  4|  n4|     s2|   40||  5|  n5|     s3|   50||  6|  n6|     s1|   60||  7|  n6|     s2|   40||  8|  n8|     s3|   90||  8|  n9|     s1|   30||  9|  n9|     s1|   20||  9|  n9|     s2|   70|+---+----+-------+-----+scala> val df2 = df.select("id", "subject","score")df2: org.apache.spark.sql.DataFrame = [id: string, subject: string ... 1 more field]scala> df2.show()+---+-------+-----+| id|subject|score|+---+-------+-----+|  1|     s1|   10||  2|     s2|   20||  3|     s3|   30||  3|     s1|   20||  4|     s2|   40||  5|     s3|   50||  6|     s1|   60||  7|     s2|   40||  8|     s3|   90||  8|     s1|   30||  9|     s1|   20||  9|     s2|   70|+---+-------+-----+scala> val df3 = df.join(df2, df("id")===df2("id"))17/12/03 21:40:59 WARN Column: Constructing trivially true equals predicate, 'id#0 = id#0'. Perhaps you need to use aliases.df3: org.apache.spark.sql.DataFrame = [id: string, name: string ... 5 more fields]scala> df3.show()+---+----+-------+-----+---+-------+-----+| id|name|subject|score| id|subject|score|+---+----+-------+-----+---+-------+-----+|  1|  n1|     s1|   10|  1|     s1|   10||  2|  n2|     s2|   20|  2|     s2|   20||  3|  n3|     s3|   30|  3|     s1|   20||  3|  n3|     s3|   30|  3|     s3|   30||  3|  n3|     s1|   20|  3|     s1|   20||  3|  n3|     s1|   20|  3|     s3|   30||  4|  n4|     s2|   40|  4|     s2|   40||  5|  n5|     s3|   50|  5|     s3|   50||  6|  n6|     s1|   60|  6|     s1|   60||  7|  n6|     s2|   40|  7|     s2|   40||  8|  n8|     s3|   90|  8|     s1|   30||  8|  n8|     s3|   90|  8|     s3|   90||  8|  n9|     s1|   30|  8|     s1|   30||  8|  n9|     s1|   30|  8|     s3|   90||  9|  n9|     s1|   20|  9|     s2|   70||  9|  n9|     s1|   20|  9|     s1|   20||  9|  n9|     s2|   70|  9|     s2|   70||  9|  n9|     s2|   70|  9|     s1|   20|+---+----+-------+-----+---+-------+-----+scala> val df4 = df2.limit(6)df4: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, subject: string ... 1 more field]scala> df4.show()+---+-------+-----+| id|subject|score|+---+-------+-----+|  1|     s1|   10||  2|     s2|   20||  3|     s3|   30||  3|     s1|   20||  4|     s2|   40||  5|     s3|   50|+---+-------+-----+scala> df.show()+---+----+-------+-----+| id|name|subject|score|+---+----+-------+-----+|  1|  n1|     s1|   10||  2|  n2|     s2|   20||  3|  n3|     s3|   30||  3|  n3|     s1|   20||  4|  n4|     s2|   40||  5|  n5|     s3|   50||  6|  n6|     s1|   60||  7|  n6|     s2|   40||  8|  n8|     s3|   90||  8|  n9|     s1|   30||  9|  n9|     s1|   20||  9|  n9|     s2|   70|+---+----+-------+-----+scala> val df6 = df.join(df4, "id")df6: org.apache.spark.sql.DataFrame = [id: string, name: string ... 4 more fields]scala> df6.show()+---+----+-------+-----+-------+-----+| id|name|subject|score|subject|score|+---+----+-------+-----+-------+-----+|  1|  n1|     s1|   10|     s1|   10||  2|  n2|     s2|   20|     s2|   20||  3|  n3|     s3|   30|     s1|   20||  3|  n3|     s3|   30|     s3|   30||  3|  n3|     s1|   20|     s1|   20||  3|  n3|     s1|   20|     s3|   30||  4|  n4|     s2|   40|     s2|   40||  5|  n5|     s3|   50|     s3|   50|+---+----+-------+-----+-------+-----+
  • 例子2
    本例说明left_outer的使用和结果。
    注意:数据集df4和df与上例的相同。
    小结:通过例子可以看到,left_outer的效果是,保留左边表格的所有id,即使右边的表没有这些id(关联字段的值)
scala> df4.show()+---+-------+-----+| id|subject|score|+---+-------+-----+|  1|     s1|   10||  2|     s2|   20||  3|     s3|   30||  3|     s1|   20||  4|     s2|   40||  5|     s3|   50|+---+-------+-----+scala> df.show()+---+----+-------+-----+| id|name|subject|score|+---+----+-------+-----+|  1|  n1|     s1|   10||  2|  n2|     s2|   20||  3|  n3|     s3|   30||  3|  n3|     s1|   20||  4|  n4|     s2|   40||  5|  n5|     s3|   50||  6|  n6|     s1|   60||  7|  n6|     s2|   40||  8|  n8|     s3|   90||  8|  n9|     s1|   30||  9|  n9|     s1|   20||  9|  n9|     s2|   70|+---+----+-------+-----+scala> val df7 = df.join(df4, df("id")===df4("id"), "left_outer")17/12/03 21:53:40 WARN Column: Constructing trivially true equals predicate, 'id#0 = id#0'. Perhaps you need to use aliases.df7: org.apache.spark.sql.DataFrame = [id: string, name: string ... 5 more fields]scala> df7.show()+---+----+-------+-----+----+-------+-----+| id|name|subject|score|  id|subject|score|+---+----+-------+-----+----+-------+-----+|  1|  n1|     s1|   10|   1|     s1|   10||  2|  n2|     s2|   20|   2|     s2|   20||  3|  n3|     s3|   30|   3|     s1|   20||  3|  n3|     s3|   30|   3|     s3|   30||  3|  n3|     s1|   20|   3|     s1|   20||  3|  n3|     s1|   20|   3|     s3|   30||  4|  n4|     s2|   40|   4|     s2|   40||  5|  n5|     s3|   50|   5|     s3|   50||  6|  n6|     s1|   60|null|   null| null||  7|  n6|     s2|   40|null|   null| null||  8|  n8|     s3|   90|null|   null| null||  8|  n9|     s1|   30|null|   null| null||  9|  n9|     s1|   20|null|   null| null||  9|  n9|     s2|   70|null|   null| null|+---+----+-------+-----+----+-------+-----+

na

  • 功能说明
    返回一个DataFrameNaFunctions以处理丢失的数据。

  • 函数原型

def na: DataFrameNaFunctions 

注意:该函数会返回一个类型的类,该类包含了各种操作空列的函数。
这些函数包括:drop(),fill(),replace(),fillCol(),replaceCol()

  • 例子1
// 删除包含任何空值的行scala> df.na.drop()
  • 例子2
// 使用常量值填充空值scala> df.na.fill("null")

select

  • 功能说明
    选择一组列。注意:该函数返回的是一个DataFrame类。

  • 函数原型

// 这是select的一个变体,只能使用列名选择现有的列(即不能构造表达式)。def select(col: String, cols: String*): DataFrame // 选择一组基于列的表达式。def select(cols: Column*): DataFrame // 选择一组SQL表达式。这是接受SQL表达式的select的一个变体。def selectExpr(exprs: String*): DataFrame 
  • 例子1
scala> df.select("id", "score").show()+---+-----+| id|score|+---+-----+|  1|   10||  2|   20||  3|   30||  3|   20||  4|   40||  5|   50||  6|   60||  7|   40||  8|   90||  8|   30||  9|   20||  9|   70|+---+-----+
  • 例子2:对select的列值进行操作
scala> df.select($"id", $"score"*10).show()+---+------------+| id|(score * 10)|+---+------------+|  1|       100.0||  2|       200.0||  3|       300.0||  3|       200.0||  4|       400.0||  5|       500.0||  6|       600.0||  7|       400.0||  8|       900.0||  8|       300.0||  9|       200.0||  9|       700.0|+---+------------+
  • 例子3:selectExpr的使用(select表达式)
ds.selectExpr("colA", "colB as newName", "abs(colC)")或ds.select(expr("colA"), expr("colB as newName"), expr("abs(colC)"))scala> df.selectExpr("id", "score * 10").show()+---+------------+| id|(score * 10)|+---+------------+|  1|       100.0||  2|       200.0||  3|       300.0||  3|       200.0||  4|       400.0||  5|       500.0||  6|       600.0||  7|       400.0||  8|       900.0||  8|       300.0||  9|       200.0||  9|       700.0|+---+------------+scala> df.selectExpr("id", "score as points").show()+---+------+| id|points|+---+------+|  1|    10||  2|    20||  3|    30|... ...

withColumn 和 withColumnRenamed

  • 功能说明
    通过添加一列或替换具有相同名称的现有列来返回新的数据集。
    withColumnRenamed只是重命名列。
    注意:该函数非常常用,可以和Column类的函数结合实现非常强大的数据集操作的功能。
    我会在《spark dataframe实践》一文中进行讲解这类应用。

  • 函数原型

def withColumn(colName: String, col: Column): DataFrame def withColumnRenamed(existingName: String, newName: String): DataFrame 
  • 例子1:通过重命名现有列来添加新列
scala> val df8 = df.withColumn("subs", df("subject"))df8: org.apache.spark.sql.DataFrame = [id: string, name: string ... 3 more fields]scala> df8.show()+---+----+-------+-----+----+| id|name|subject|score|subs|+---+----+-------+-----+----+|  1|  n1|     s1|   10|  s1||  2|  n2|     s2|   20|  s2||  3|  n3|     s3|   30|  s3||  3|  n3|     s1|   20|  s1||  4|  n4|     s2|   40|  s2||  5|  n5|     s3|   50|  s3||  6|  n6|     s1|   60|  s1||  7|  n6|     s2|   40|  s2||  8|  n8|     s3|   90|  s3||  8|  n9|     s1|   30|  s1||  9|  n9|     s1|   20|  s1||  9|  n9|     s2|   70|  s2|+---+----+-------+-----+----+
  • 例子2:重命名现有列,但不添加新列

从下面的例子中可以看出,把score列的值替换了,但并没有添加新的列。

scala> val df9 = df.withColumn("score", df("score")/100)df9: org.apache.spark.sql.DataFrame = [id: string, name: string ... 2 more fields]scala> df9.show()+---+----+-------+-----+| id|name|subject|score|+---+----+-------+-----+|  1|  n1|     s1|  0.1||  2|  n2|     s2|  0.2||  3|  n3|     s3|  0.3||  3|  n3|     s1|  0.2||  4|  n4|     s2|  0.4||  5|  n5|     s3|  0.5||  6|  n6|     s1|  0.6||  7|  n6|     s2|  0.4||  8|  n8|     s3|  0.9||  8|  n9|     s1|  0.3||  9|  n9|     s1|  0.2||  9|  n9|     s2|  0.7|+---+----+-------+-----+// 也可以直接通过withColumnRenamed进行重命名scala> val df9 = df.withColumnRenamed("score","score2") df9: org.apache.spark.sql.DataFrame = [id: string, name: string ... 2 more fields]scala> df9.show()+---+----+-------+------+| id|name|subject|score2|+---+----+-------+------+|  1|  n1|     s1|    10||  2|  n2|     s2|    20||  3|  n3|     s3|    30||  3|  n3|     s1|    20||  4|  n4|     s2|    40||  5|  n5|     s3|    50||  6|  n6|     s1|    60||  7|  n6|     s2|    40||  8|  n8|     s3|    90||  8|  n9|     s1|    30||  9|  n9|     s1|    20||  9|  n9|     s2|    70|+---+----+-------+------+

stat

  • 功能说明
    为工作统计功能支持返回一个DataFrameStatFunctions。
    该类的函数包括:approxQuantile,corr,cov,freqItems,sampleBy,countMinSketch,bloomFilter,buildBloomFilter等

  • 函数原型

def stat: DataFrameStatFunctions 
  • 例子1
scala> val cols = Array("score")cols: Array[String] = Array(score)scala> df.stat.freqItems(cols)res56: org.apache.spark.sql.DataFrame = [score_freqItems: array<string>]scala> df.stat.freqItems(cols).show()+--------------------+|     score_freqItems|+--------------------+|[90, 30, 60, 50, ...|+--------------------+