transformations函数分为两类:
* Typed transformations
这类函数的返回值一般都是dataset,也就是说不会改变原来的数据集的类型。
* Untyped transformations
这类函数的返回值,会根据不同的函数返回不同的类型。
本文讲解的是第一类的函数,也就是说返回的类型基本上是相同的都是dataset。
本文的所有例子,都是基于spark-2.1进行操作。
本文的所有例子,都是基于以下简单的csv数据集进行讲解:
scala> import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.SparkSession
scala> val spark = SparkSession.builder().appName("Spark SQL basic example").config("spark.some.config.option", "some-value").getOrCreate()
17/11/28 07:08:49 WARN SparkSession$Builder: Using an existing SparkSession; some configuration may not take effect.
spark: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession@c45908c
scala> import spark.implicits._
import spark.implicits._
scala> val df = spark.read.format("csv").option("header", true).load("/user/hadoop/csvdata/csvdata")
df: org.apache.spark.sql.DataFrame = [id: string, name: string ... 1 more field]
scala> df.show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 1| n1| 10|
| 2| n2| 20|
| 3| n3| 30|
| 4| n4| 40|
| 5| n5| 50|
| 6| n6| 60|
| 7| n6| 60|
| 8| n8| 60|
| 8| n9| 60|
| 9| n9| 60|
+---+----+-----+
说明:这些函数最后返回的都是一个类型,也就是Dataset[T]。这类dataframe是dataset的行集合。
功能
若你需要更少的分区,可以通过该函数来得到具有确定分区数的新的数据集。如果请求更多的分区,它将保持在当前的分区数量。类似于在RDD上定义的合并,该操作导致狭窄的依赖性,例如,如果你从1000个分区转到100个分区,那么就不会有shuffle,而是100个新分区中的每一个都会声称当前分区有10个。
但是,如果您正在进行激烈的合并,例如到numPartitions = 1,这可能会导致你的计算发生在比你喜欢的节点更少的节点上(例如numPartitions = 1的情况下是一个节点)。为了避免这种情况,您可以调用repartition。这将增加一个shuffle步骤,但意味着当前的上游分区将并行执行(无论当前分区是什么)。
原型
def coalesce(numPartitions: Int): Dataset[T]
scala> val df3 = spark.read.format("csv").option("header", false).load("/warehouse/spark/testdata/")
df3: org.apache.spark.sql.DataFrame = [_c0: string, _c1: string ... 34 more fields]
scala> df3.rdd.partitions.length
res10: Int = 15
scala> df3.coalesce(10)
res11: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [_c0: string, _c1: string ... 34 more fields]
scala> df3.rdd.partitions.length
res12: Int = 15
scala> val df4 = df3.coalesce(10)
df4: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [_c0: string, _c1: string ... 34 more fields]
scala> df4.rdd.partitions.length
res13: Int = 10
功能
按照某个字段去重。
原型
def dropDuplicates(colNames: Array[String]): Dataset[T]
def dropDuplicates(col1: String, cols: String*): Dataset[T]
def dropDuplicates(colNames: Seq[String]): Dataset[T]
// 按所有字段去重
def dropDuplicates(): Dataset[T]
scala> df.sort().dropDuplicates("id").show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 7| n6| 60|
| 1| n1| 10|
| 6| n6| 60|
| 4| n4| 40|
| 8| n8| 60|
| 3| n3| 30|
| 2| n2| 20|
| 5| n5| 50|
| 9| n9| 60|
+---+----+-----+
功能
对dataframe满足条件的列进行过滤。
原型
where的原型有两种:
def where(conditionExpr: String): Dataset[T]
def where(condition: Column): Dataset[T]
通过以上原型,可以看出,一种是直接添加字符串,一种是通过列来对数据进行过滤。
scala> val df3 = df.select("id", "name").where($"score">50).distinct()
df3: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string]
scala> df3.show()
+---+----+
| id|name|
+---+----+
| 7| n6|
| 9| n9|
| 6| n6|
| 8| n8|
| 8| n9|
+---+----+
以上例子也可以写成下面的形式,结果是一样的:
val df3 = df.select("name", "score").where("score>50").distinct()
scala> val df3 = df.select("name", "score").where("score>10 and score<50").distinct()
df3: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [name: string, score: string]
scala> df3.show()
+----+-----+
|name|score|
+----+-----+
| n4| 40|
| n2| 20|
| n3| 30|
+----+-----+
也可以写成以下形式:
val df3 = df.select("name", "score").where($"score">10 && $"score" > 50).distinct()
功能
可以根据特定的列或排序表达式进行排序。同时还可以指定按升序还是降序排序。
注意:默认情况下,sort是按升序排列的。
函数原型
def sort(sortExprs: Column*): Dataset[T]
def sort(sortCol: String, sortCols: String*): Dataset[T]
按score降序排列以下记录。
scala> val df3 = df.select("id","name", "score").where("score<50").sort($"score".desc)
df3: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string ... 1 more field]
// 可以看到记录已按score成降序排列
scala> df3.show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 4| n4| 40|
| 3| n3| 30|
| 2| n2| 20|
| 1| n1| 10|
+---+----+-----+
scala> val df3 = df.select("id","name", "score").where("score>50").sort($"id".desc, $"score".desc)
df3: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string ... 1 more field]
scala> df3.show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 9| n9| 60|
| 8| n9| 60|
| 8| n8| 60|
| 7| n6| 60|
| 6| n6| 60|
+---+----+-----+
功能
选择需要返回的列,并可以对列进行一些操作。
原型
def select[U1, U2, U3](c1: TypedColumn[T, U1], c2: TypedColumn[T, U2], c3: TypedColumn[T, U3]): Dataset[(U1, U2, U3)]
def select[U1, U2](c1: TypedColumn[T, U1], c2: TypedColumn[T, U2]): Dataset[(U1, U2)]
def select[U1](c1: TypedColumn[T, U1]): Dataset[U1]
scala> val df3 = df.select("id", "name").limit(2)
df3: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string]
scala> df3.show()
+---+----+
| id|name|
+---+----+
| 1| n1|
| 2| n2|
+---+----+
scala> val df2 = df.select($"id" as "id2")
df2: org.apache.spark.sql.DataFrame = [id2: string]
scala> val df4 = df.select($"id", expr("score+10").as[Double] as "newscore").limit(2)
df4: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, newscore: double]
scala> df4.show()
+---+--------+
| id|newscore|
+---+--------+
| 1| 20.0|
| 2| 30.0|
+---+--------+
功能说明
对数据集去重。
函数原型
def distinct(): Dataset[T]
scala> df.select("id").distinct().sort("id").collect()
res5: Array[org.apache.spark.sql.Row] = Array([1], [2], [3], [4], [5], [6], [7], [8], [9])
功能说明
对两个数据集取交集,返回两个数据集共有的行。
函数原型
def intersect(other: Dataset[T]): Dataset[T]
scala> val df2 = df.where($"score">50)
df2: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string ... 1 more field]
scala> df2.show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 6| n6| 60|
| 7| n6| 60|
| 8| n8| 60|
| 8| n9| 60|
| 9| n9| 60|
+---+----+-----+
scala> val df3 = df.intersect(df2)
df3: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string ... 1 more field]
scala> df3.sort("id").show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 6| n6| 60|
| 7| n6| 60|
| 8| n8| 60|
| 8| n9| 60|
| 9| n9| 60|
+---+----+-----+
函数功能
合并两个数据集。注意:合并的两个数据集(dataset)的列数必须相同,否则会报错。
函数原型
def union(other: Dataset[T]): Dataset[T]
scala> val df4 = df.union(df3)
df4: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string ... 1 more field]
scala> df4.show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 1| n1| 10|
| 2| n2| 20|
| 3| n3| 30|
| 4| n4| 40|
| 5| n5| 50|
| 6| n6| 60|
| 7| n6| 60|
| 8| n8| 60|
| 8| n9| 60|
| 9| n9| 60|
| 8| n8| 60|
| 7| n6| 60|
| 9| n9| 60|
| 6| n6| 60|
| 8| n9| 60|
+---+----+-----+
功能
返回数据集的前n行,不同于head的是:limit返回的是一个dataset,而head返回的是array。
函数原型
def limit(n: Int): Dataset[T]
scala> val df2 = df.limit(2)
df2: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [id: string, name: string ... 1 more field]
scala> df2.show()
+---+----+-----+
| id|name|score|
+---+----+-----+
| 1| n1| 10|
| 2| n2| 20|
+---+----+-----+
功能
根据特定的函数对数据进行聚合。然后返回一个KeyValueGroupedDataset类型的dataset。
注意:该函数会通过HashPartitioner的方式把数据重按key的值新分区,把相同key的值分到同一分区中,这样在大部分的情况下会导致shuffle。完成这个过程后,会在本地的分区上执行aggregation。
函数原型
// for java
def groupByKey[K](func: MapFunction[T, K], encoder: Encoder[K]): KeyValueGroupedDataset[K, T]
// for scala
def groupByKey[K](func: (T) => K)(implicit arg0: Encoder[K]): KeyValueGroupedDataset[K, T]
scala> df.groupByKey(l=>l.getString(0)).count.show()
+-----+--------+
|value|count(1)|
+-----+--------+
| 7| 1|
| 1| 1|
| 6| 1|
| 4| 1|
| 8| 2|
| 3| 1|
| 2| 1|
| 5| 1|
| 9| 1|
+-----+--------+
scala> df.groupByKey(r=>r.getString(0)).mapValues(r=>r.getString(1)).reduceGroups( (id, name) => (id+","+name)).show()
+-----+----------------------------------+
|value|ReduceAggregator(java.lang.String)|
+-----+----------------------------------+
| 7| n6|
| 1| n1|
| 6| n6|
| 4| n4|
| 8| n8,n9|
| 3| n3|
| 2| n2|
| 5| n5|
| 9| n9|
+-----+----------------------------------+
功能
对数据集上的每一行使用函数func进行处理,并返回一个新的dataset。
注意:dataframe的map和rdd的不同,这里需要返回一个新的dataset。
函数原型
// for java
def map[U](func: MapFunction[T, U], encoder: Encoder[U]): Dataset[U]
// for scala
def map[U](func: (T) ⇒ U)(implicit arg0: Encoder[U]): Dataset[U]
scala> val df2 = df.map(r=>(r.getString(0),r.getString(1))).limit(2)
df2: org.apache.spark.sql.Dataset[(String, String)] = [_1: string, _2: string]
scala> df2.show()
+---+---+
| _1| _2|
+---+---+
| 1| n1|
| 2| n2|
+---+---+
功能
在数据集的每个分区上使用函数func
函数原型
def mapPartitions[U](f: MapPartitionsFunction[T, U], encoder: Encoder[U]): Dataset[U]
def mapPartitions[U](func: (Iterator[T]) ⇒ Iterator[U])(implicit arg0: Encoder[U]): Dataset[U]