transformations函数分为两类:
* Typed transformations
这类函数的返回值一般都是dataset,也就是说不会改变原来的数据集的类型。
* Untyped transformations
这类函数的返回值,会根据不同的函数返回不同的类型。
本文的所有例子,都是基于spark-2.1进行操作。
本文的所有例子,都是基于以下简单的csv数据集进行讲解:
import org.apache.spark.sql.SparkSession
val 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|
+---+----+-------+-----+
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
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|
+----------+----------+----------+------------+
功能
使用指定的列对数据集进行分组,以便我们可以对其进行聚合。请参阅RelationalGroupedDataset获取所有可用的聚合函数。
这是groupBy的一个变体,它只能使用列名称对现有列进行分组(即不能构造表达式)。
函数原型
def groupBy(col1: String, cols: String*): RelationalGroupedDataset
def groupBy(cols: Column*): RelationalGroupedDataset
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|
+----+----------+
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|
+---+----+----------+
scala> df.groupBy("subject").agg("score"->"avg").sort("subject").show()
+-------+------------------+
|subject| avg(score)|
+-------+------------------+
| s1| 28.0|
| s2| 42.5|
| s3|56.666666666666664|
+-------+------------------+
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|
+-------+------------------+----------+----------+------------+
功能说明
根据列名选择列并将其作为列返回。
函数原型
def apply(colName: String): Column
def col(colName: String): Column
scala> df.apply("name")
res11: org.apache.spark.sql.Column = name
scala> df.col("name")
res16: org.apache.spark.sql.Column = name
功能说明
使用指定的列为当前数据集创建一个多维数据集,因此我们可以对它们运行聚合。请参阅RelationalGroupedDataset获取所有可用的聚合函数。
这是立方体的变体,只能使用列名称对现有列进行分组(即不能构造表达式)。
原型
def cube(col1: String, cols: String*): RelationalGroupedDataset
def cube(cols: Column*): RelationalGroupedDataset
scala> df.cube("name", "score")
res18: org.apache.spark.sql.RelationalGroupedDataset = org.apache.spark.sql.RelationalGroupedDataset@3f88db17
函数功能
删除数据集中的某个列。
函数原型
def drop(col: Column): DataFrame
def drop(colNames: String*): DataFrame
def drop(colName: String): DataFrame
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类型的说明
内连接 : 只连接匹配的行
左外连接 : 包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行
右外连接 : 包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边表中全部匹配的行
全外连接 : 包含左、右两个表的全部行,不管另外一边的表中是否存在与它们匹配的行。
功能说明
使用给定的连接表达式连接另一个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
.
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|
+---+----+-------+-----+-------+-----+
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|
+---+----+-------+-----+----+-------+-----+
功能说明
返回一个DataFrameNaFunctions以处理丢失的数据。
函数原型
def na: DataFrameNaFunctions
注意:该函数会返回一个类型的类,该类包含了各种操作空列的函数。
这些函数包括:drop(),fill(),replace(),fillCol(),replaceCol()
// 删除包含任何空值的行
scala> df.na.drop()
// 使用常量值填充空值
scala> df.na.fill("null")
功能说明
选择一组列。注意:该函数返回的是一个DataFrame类。
函数原型
// 这是select的一个变体,只能使用列名选择现有的列(即不能构造表达式)。
def select(col: String, cols: String*): DataFrame
// 选择一组基于列的表达式。
def select(cols: Column*): DataFrame
// 选择一组SQL表达式。这是接受SQL表达式的select的一个变体。
def selectExpr(exprs: String*): DataFrame
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|
+---+-----+
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|
+---+------------+
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|
... ...
功能说明
通过添加一列或替换具有相同名称的现有列来返回新的数据集。
withColumnRenamed只是重命名列。
注意:该函数非常常用,可以和Column类的函数结合实现非常强大的数据集操作的功能。
我会在《spark dataframe实践》一文中进行讲解这类应用。
函数原型
def withColumn(colName: String, col: Column): DataFrame
def withColumnRenamed(existingName: String, newName: String): DataFrame
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|
+---+----+-------+-----+----+
从下面的例子中可以看出,把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|
+---+----+-------+------+
功能说明
为工作统计功能支持返回一个DataFrameStatFunctions。
该类的函数包括:approxQuantile,corr,cov,freqItems,sampleBy,countMinSketch,bloomFilter,buildBloomFilter等
函数原型
def stat: DataFrameStatFunctions
scala> val cols = Array("score")
cols: Array[String] = Array(score)
scala> df.stat.freqItems(cols)
res56: org.apache.spark.sql.DataFrame = [score_freqItems: array]
scala> df.stat.freqItems(cols).show()
+--------------------+
| score_freqItems|
+--------------------+
|[90, 30, 60, 50, ...|
+--------------------+