本节简要概述可用的Transformation。Transformations文档提供了所有转换的完整描述和示例。
作用:返回一个新的DataSet,该DataSet由每一个输入元素经过func函数转换后组成。map函数实现了一对一的映射,也就是说,func函数必须返回一个元素。
data.map { x => x.toInt } // 将数据集中的每一个元素转成Int类型
例子:
/** 功能:将数据集中的每个元素 * 2 */
def mapFunction(env:ExecutionEnvironment) = {
val data: DataSet[Int] = env.fromCollection(List(1, 2, 3))
// 每个元素*2
data.map(_ * 2).print() // 方式一:最重要方式
data.map(x => x * 2).print() // 方式二
data.map((x:Int) => { // 方式三
x * 2
}).print()
data.map((x) => { // 方式四
x * 2
}).print()
}
作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素)
/** 功能:将数据集中的每个元素通过空格进行分割 */
def flatMapFunction(env:ExecutionEnvironment) = {
val info: ListBuffer[String] = ListBuffer[String]()
info.append("hadoop spark")
info.append("zookeeper kafka")
info.append("flink blink")
val data: DataSet[String] = env.fromCollection(info)
data.flatMap(_.split(" "))
.map((_, 1))
.groupBy(0)
.sum(1)
.print()
}
作用:类似于map,但独立地在DataSet的每一个分区上运行,因此在类型为T的DataSet上运行时,func的函数类型必须是Iterator[T]) => TraversableOnce[R]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartition被调用M次。
map()和mapPartition()的区别:
map()
:每次处理一条数据。mapPartition()
:每次处理一个分区的数据,这个分区的数据处理完后,原DataSet中分区的数据才能释放,可能导致OOM。开发指导
:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。/** 功能:计算每个分区中元素的个数 */
def mapPartitionFunction(env:ExecutionEnvironment) = {
val info: ListBuffer[String] = ListBuffer[String]()
info.append("hadoop spark")
info.append("zookeeper kafka")
info.append("hive")
info.append("zookeeper")
info.append("hbase")
info.append("spark")
val data: DataSet[String] = env.fromCollection(info).setParallelism(3)
data.mapPartition { in => Some(in.size) }.print()
}
作用:过滤。返回一个新的DataSet,该DataSet由经过func函数计算后返回值为true的输入元素组成。
data.filter { _ > 1000 } // 筛选值>1000的元素
例子:
def filterFunction(env:ExecutionEnvironment) = {
val data: DataSet[Int] = env.
fromCollection(List(1, 2, 3))
data.filter(_ % 2 == 0).print()
}
作用:通过用户自定义的func函数聚集DataSet中的所有元素,先聚合分区内数据,再聚合分区间数据。
def reduceFunction(env:ExecutionEnvironment) = {
val data: DataSet[Int] = env
.fromCollection(List(1, 2, 3)).setParallelism(2)
data.reduce(_ + _).print() // 计算所有元素的和
}
作用:将一组元素组合为一个或多个元素。ReduceGroup可以应用于一个完整的数据集或一个分组的数据集。
def reduceGroupFunction(env:ExecutionEnvironment) = {
val info: ListBuffer[(Int, String)] = ListBuffer[(Int, String)]()
info.append((1, "hadoop"))
info.append((1, "spark"))
info.append((1, "hadoop"))
info.append((2, "spring"))
info.append((2, "java"))
info.append((3, "linux"))
info.append((4, "vue"))
val data: DataSet[(Int, String)] = env.fromCollection(info)
data.groupBy(0)
.reduceGroup(in => {
in.reduce((x, y) => (x._1, x._2 + ", " + y._2))
})
.print()
// 输出格式: (1,hadoop, spark, hadoop)
}
作用:聚合操作。
Aggregate操作只能应用在元组DataSet中,并且仅仅支持position key进行分组。
例子:根据第二个字段进行分组,在分组中根据第一个字段求和,第三个字段取最小值。
def aggregateFunction(env:ExecutionEnvironment) = {
val list = List(Tuple3(1, "hadoop", 1.0), Tuple3(2, "spark", 1.5), Tuple3(3, "zookeeper", 2.0), Tuple3(5, "zookeeper", 3.0))
val input: DataSet[(Int, String, Double)] = env.fromCollection(list)
input.groupBy(1)
.aggregate(Aggregations.SUM, 0)
.and(Aggregations.MIN, 2).print()
}
// 输出结果
// (1,hadoop,1.0)
// (8,zookeeper,2.0)
// (2,spark,1.5)
作用:去重。对源DataSet进行去重后,返回一个新的DataSet。
def distinctFunction(env:ExecutionEnvironment) = {
val info: ListBuffer[String] = ListBuffer[String]()
info.append("hadoop")
info.append("kafka")
info.append("hadoop")
val data: DataSet[String] = env.fromCollection(info)
data.distinct()
.print()
}
作用:获取两个DataSet中字段匹配关系的记录。两个数据集的元素通过一个或多个键连接,可以使用如下四种key类型:
def joinFunction(env:ExecutionEnvironment) = {
val info1: ListBuffer[(Int, String)] = ListBuffer[(Int, String)]()
info1.append((1, "hadoop"))
info1.append((2, "spark"))
val info2: ListBuffer[(String, String)] = ListBuffer[(String, String)]()
info2.append(("apache", "hadoop"))
info2.append(("apache", "spark"))
info2.append(("apache", "zookeeper"))
val data1: DataSet[(Int, String)] = env.fromCollection(info1)
val data2: DataSet[(String, String)] = env.fromCollection(info2)
data1.join(data2).where(1).equalTo(1)
.apply((first, second) => {
(first._1, second._1, first._2)
}).print()
// 输出: (编号, 公司, 框架名称)
}
与MySQL中的外连接相对应。OuterJoin转换对两个数据集执行左、右或完全的外连接。
OuterJoins 只支持Java and Scala DataSet API.
作用:计算两个DataSet的笛卡尔积。
def crossFunction(env:ExecutionEnvironment) = {
val list1 = List("hadoop", "spark")
val list2 = List(1, 2)
val data1: DataSet[String] = env.fromCollection(list1)
val data2: DataSet[Int] = env.fromCollection(list2)
data1.cross(data2).print()
}
作用:求两个DataSet的交集。
rebalance
将以循环方式分发数据,均匀地重新平衡每个分区上的数据,避免数据倾斜。
Note: 该函数只能应用在map此类方法之前。
def rebalanceFunction(env:ExecutionEnvironment) = {
val data: DataSet[Int] = env
.fromCollection(List(1, 2, 3)).setParallelism(2)
data.rebalance().map((_, 1)).print()
}
根据指定的key进行hash分区。key可以是指定的索引位置、表达式、key选择器。
例子:
val in: DataSet[(Int, String)] = // [...]
val result = in.partitionByHash(0).mapPartition { ... }
根据指定的key进行range分区。key可以是指定的索引位置、表达式、key选择器。
例子:
val in: DataSet[(Int, String)] = // [...]
val result = in.partitionByRange(0).mapPartition { ... }
作用:使用自定义分区函数根据指定的key将记录分配给特定的分区。key可以指定为位置键、表达式键和键选择器函数。
注意:此方法只适用于单个字段的键。
val in: DataSet[(Int, String)] = // [...]
val result = in
.partitionCustom(partitioner, key).mapPartition { ... }
作用:在指定字段上按指定的顺序对数据集的所有分区进行本地排序。字段可以指定为元组位置或字段表达式。对多个字段进行排序是通过链接sortPartition()调用完成的。
val in: DataSet[(Int, String)] = // [...]
// 根据字段1进行升序排序
val result = in.sortPartition(1, Order.ASCENDING).mapPartition { ... }
作用:返回一个DataSet中的前n个元素。
def firstNFunction(env:ExecutionEnvironment) = {
val info: ListBuffer[(Int, String)] = ListBuffer[(Int, String)]()
info.append((1, "hadoop"))
info.append((1, "spark"))
info.append((1, "flink"))
info.append((2, "spring"))
info.append((2, "java"))
info.append((3, "linux"))
info.append((4, "vue"))
val data: DataSet[(Int, String)] = env
.fromCollection(info)
// 1、取前2个元素
data.first(2).print()
// 2、根据第一个字段分组,并取每个分组中的前2个元素
data.groupBy(0)
.first(2).print()
// 3、根据第一个字段分组,并根据第二个字段倒序排序,并获取每个分组中的前2个元素
data.groupBy(0).sortGroup(1, Order.DESCENDING)
.first(2).print()
}