flink实战—通过例子学习DataSet API:DataSet转换算子(DataSet Transformations)-1

概述

本文讲述各个DataSet 转换算子的使用。并通过实际的例子对其功能进行了说明。
说明:本来想通过一篇文章来讲述所有的transformtion算子,但由于篇幅和时间,只能能分成几篇进行介绍。

基本概念

数据转换(Data transformations)将一个或多个DataSet转换为新的DataSet。程序可以将多个转换组合成复杂的程序集。

测试数据集说明

假设我们有一个数据集,是鸡尾花的文件,来源如下面的连接:

https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data

样例数据如下:

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
4.6,3.4,1.4,0.3,Iris-setosa

该数据文件保存在文件夹:

~/work/iris.data

Map函数

Map转换在DataSet的每个元素上应用用户定义的map函数。它实现了一对一的映射,也就是说,该函数对每个元素都会返回一个结果。

注意:Map会对每个成员应用定义的函数,而且针对每个成员都会返回一个函数运行的结果。实际的运行代码如下:

scala> val data = benv.fromElements("123,456,6,7")
data: org.apache.flink.api.scala.DataSet[String] = org.apache.flink.api.scala.DataSet@5b626cc9

scala> val d2 = data.map{x => x.split(",")}
d2: org.apache.flink.api.scala.DataSet[Array[String]] = org.apache.flink.api.scala.DataSet@178b5e0f

# 查看一下d2的内容
scala> d2
res73: org.apache.flink.api.scala.DataSet[Array[String]] = org.apache.flink.api.scala.DataSet@178b5e0f

注意:通过查看d2的内容,我们可以看到,这是一个Array[String]的DataSet。为什么会这样呢?是因为我们使用了x.split(",")这个函数,针对DataSet中的每个成员,split函数返回的就是一个Array[String]。

flatMap函数

FlatMap转换函数会在DataSet的每个成员上应用用户定义的flat-map函数。它是Map函数的变种,和Map函数不同的是,flatMap函数会合并在DataSet每个成员上的自定义函数的运行结果,最终返回的可能是一个或多个结果。

要进一步理解flatMap和Map的区别,需要通过实际的操作例子:

scala> val data = benv.fromElements("123,456,6,7")
data: org.apache.flink.api.scala.DataSet[String] = org.apache.flink.api.scala.DataSet@1a0c273a

scala> data.flatMap{_.split(",")}
res76: org.apache.flink.api.scala.DataSet[String] = org.apache.flink.api.scala.DataSet@75bab362

scala> data.flatMap{_.split(",")}.print()
123
456
6
7

从结果中可以看到,flatMap函数把结果进行了扁平化处理。

MapPartition函数

MapPartition在单个函数调用中转换并行分区。map-partition函数将分区作为可以迭代的对象,并且可以生成任意数量的结果值。每个分区中的元素数量取决于并行度(degree-of-parallelism)和先前的操作。

scala> val data = benv.fromElements("123,456,6,7")
data: org.apache.flink.api.scala.DataSet[String] = org.apache.flink.api.scala.DataSet@79aba813

scala> data.mapPartition(
     |   lines => {
     |           lines.flatMap(_.split(",")).map((_, 1))
     |   }
     | ).print()

(123,1)
(456,1)
(6,1)
(7,1)

Filter函数

Filter转换在DataSet的每个元素上应用用户定义的过滤器函数,并仅保留函数返回true的元素。

scala> data
res17: org.apache.flink.api.scala.DataSet[String] = org.apache.flink.api.scala.DataSet@79aba813

scala> data.flatMap{_.split(",") }.map(_.toInt).print()
123
456
6
7

scala> data.flatMap{_.split(",") }.map(_.toInt).filter(_ > 9).print()
123
456

First-n

返回数据集的前n个(任意)元素。First-n可以应用于常规数据集,分组数据集(grouped data set)或分组排序数据集(grouped-sorted)。分组key可以指定为:key选择器功能,元组(tuple)位置或案例类字段。

scala> val data = benv.readTextFile("/home/ubuntu/work/iris.data")
data: org.apache.flink.api.scala.DataSet[String] = org.apache.flink.api.scala.DataSet@6f22c1f1

scala> data.first(2).print()
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa

first函数也可以对GroupSet进行过滤,不过此时获取的每个group的前n条记录:

scala> val data2 = benv.readTextFile("/home/ubuntu/work/iris.data").
            filter(_.length>0).
            map(x=>x.split(",")).
            map(x=>(x(0),x(1),x(2),x(3),x(4)))

scala> data2.groupBy(0).first(1).print()
(4.3,3.0,1.1,0.1,Iris-setosa)
(4.4,2.9,1.4,0.2,Iris-setosa)
(4.5,2.3,1.3,0.3,Iris-setosa)
(4.6,3.6,1.0,0.2,Iris-setosa)
(4.7,3.2,1.3,0.2,Iris-setosa)
(4.8,3.4,1.6,0.2,Iris-setosa)
(4.9,3.1,1.5,0.1,Iris-setosa)
...

以上代码打印按元组的第一个元素进行分组的,每个分组的第一条记录的值。注意:是每个分组的第一条记录的值。

Union

生成两个DataSet的并集,这两个DataSet必须属于同一类型。可以使用多个联合调用实现两个以上DataSet的并集,如下所示:

val vals1: DataSet[(String, Int)] = // [...]
val vals2: DataSet[(String, Int)] = // [...]
val vals3: DataSet[(String, Int)] = // [...]

val unioned = vals1.union(vals2).union(vals3)

Rebalance

均匀地重新平衡DataSet的并行分区以消除数据偏斜。

val in: DataSet[String] = // [...]
// rebalance DataSet and apply a Map transformation.
val out = in.rebalance().map { ... }

Reduce

通过将两个元素重复组合成一个元素,将一组元素组合成一个元素。Reduce可以应用于完整数据集或分组数据集。

reduce操作可以对分组数据集进行操作。指定用于分组的key可以通过多种方式完成:

  • key表达式
  • key选择器函数
  • 一个或多个字段位置的key(仅限元组数据集)
  • Case Class 字段(仅限于Case类)

基于元组的位置来进行reduce

scala>  val data2 = benv.readTextFile("/home/ubuntu/work/iris.data").
     |             filter(_.length>0).
     |             map(x=>x.split(",")).
     |             map(x=>(x(4),x(0),x(1),x(2),x(3)))

scala> data2.groupBy(0).reduce((a,b) => (a._1,(a._2.toFloat + b._2.toFloat).toString,a._3,a._4,a._5)).print()
(Iris-setosa,250.29999,3.0,1.4,0.2)
(Iris-versicolor,296.80005,3.0,4.4,1.4)
(Iris-virginica,329.39996,3.3,6.0,2.5)

注意:以上代码显得比较繁琐,应该把reduce中的计算过程写成函数。

基于全部数据集进行reduce

val intNumbers = env.fromElements(1,2,3)
val sum = intNumbers.reduce (_ + _)

说明:以上的reduce会挨个把数据集中的元素进行相加,最后会得到一个加和的整数。

参考文献

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页