目录
2.select()/selectExpr()/col()/apply()方法
一:Scala基础
1.定义与使用常量
(1)常量:用 val关键字定义一个常量
(2)变量:用var关键字定义一个变量
2.定义与使用数组
(1)数组是Scala中常用的一种数据结构,数组是一种存储了相同类型元素的固定大小的顺序集合
(2)定义数组的两个方式
#1
var arr:Array[String] = new Array[String](num)
#2
var arr:Array[String] = Array(元素1,元素2,......)
(3)数组操作的常用方法
#查看数组z的长度
z.length
#查看数组z的第一个元素
z.head
#查看数组z中除了第一个元素外的其他元素
z.tail
#判断数组z是否为空
z.isEmpty
#判断数组z是否包含元素"baidu"
z.contains("baidu")
(4) 连接两个数组可以使用操作符“++”,还可以使用concat()方法
#通过concat()方法连接数组
importArray._
val arr4=concat(arr1,arr2)
(5)使用range()创建区间数组
#创建区间数组,生成数组(1,3,5,7,9)
val arr=range(1,10,2)
(6)定义与使用函数
函数是Scala的重要组成部分,Scala作为支持函数式编程的语言Scala函数的以将函数作为对象,定义函数的语法格式如下。
def functionName(参数列表):[returntype]={}
函数的定义由一个def关键字开始,紧接着是函数名称和可选的参数列表,其次悬个冒号“:”和函数的返回类型,之后是赋值运算符“一”,最后是方法体。其中,参数外中需要指定参数名称和参数类型。函数的返回类型[returntype]可以是任意合法的ScalaySca据类型。若函数无返回值,则函数的返回类型为“Unit”。
例如,定义一个函数add,实现两个数相加的功能,函数返回类型为Int,两个数概的结果作为返回值。在Java中,函数需要使用return关键字指明返回值,而Scala函数义个可以不加returm关键字指明返回值。
#定义两个整数相加的代码1
def add(a:Int,b:Int):Int=(a+b}
Scala也可以使用“return”关键字指明返回的结果,如代码2-8所示。
#定义两个整数相加的代码2
def add(a:Int,b:Int):Int=(varsum=0;sum=a+b;return sum)
Scala提供了多种不同的函数调用方式,以下是调用函数的标准格式。
functionName(参数列表)
3.定义与使用元组
元组(Tuple)是一种类似于列表的结构,但是元组可以包含不同类型的元素,元组的值是通过将单个的值包含在圆括号种构成的
val t=(1,3.14,"a")
val t-new Tuple3(1,3.14,"a")
4.使用函数组合器
Scala为各种数据结构提供了很多函数组合器,函数组合器的参数都是一个函数,运用函数组合器的操作会对集合中的每个元素分别应用一个函数。以列表为例,介绍常用的的数组合器的用法。
1.map()方法
map()方法可通过一个函数重新计算列表scala>valnum:List[Int]=List(1,2,3,4,5)中的所有元素,并且返回一个包含相同数目元num:List[Int]=List(1,2,3,4,5)素的新列表。例如,定义一个Int类型列表,列scala>num.map(x=>x*x)表中的元素为1~5,使用map()方法对列表中的元素进行平方计算。
val num:List[Int]=List(1,2,3,4,5)
num.map(x=>x*x)
2.foreach()
方法foreach()方法和map()方法类似,但是foreach()方法没有返回值,只用于对参数的结果进行输出。例如,使用foreach方法对num中的元素进行平方计算并输出.
val num:List[Int]=List(1,2,3,4,5)
num.foreach(x=>print(x*x+"/t")
3.filter()方法
使用filter()方法可以移除传入函数的返回值为false的元素。例如,过滤列表num中的奇数,得到只包含偶数的列表.
val num:List[Int]=List(1,2,3,4,5)
num.filter(x=>x%2==0)
4.flatten()方法
flatten()方法可以将嵌套的结构晨开,即flatten()方法可以将一个二维的列表展开成一个一维的列表。定义一个二维列表list,通过flattenO方法可以将list展开为一维列表。
val list=List(List(1,2,3),List(4,5,6))
list.flatten
5.flatMap()方法
flatMap()方法结合了map0方法和flatten()方法的功能,接收一个可以处理嵌套列表的函数,再对返回结果进行连接。
val str=List("a:b:c","d:e:f")
str.flatMap(x=>x.split(";"))的使用
6.groupBy()方法
groupBy0方法可对集合中的元素进行分组操作,返回的结果是一个映射。对1~1据奇偶性进行分组,因此groupBv0方法传入的参数是一个计算偶数的函数,得到的结一个映射,包含两个键值对,键为false对应的值为奇数列表,键为true对应的值为偶表,如图2-41所示。val num:list[Intl=list(1.2.3,4.5,6,7,8,9,10)
num.groupBy(x=>x%2==0)
二:Spark编程基础
1.创建RDD与应用RDD
1.1 从内存中读取数据创建RDD
1.parallelize()
有两个输入参)
(1)要转化的集合:必须是Sep集合,指的是一类具有一定长度的可迭代访问的对象,每个数据元素均带有一个从0开始的,固定的序列
(2)分区数:若不设分区数,则RDD的分区数默认为该程序分配到的资源的CPU核心数通过parallelize0方法用一个数组的数据创建RDD,并设置分区数为4,如代码3-1所示,创建后查看该RDD的分区数。
parallelize()方法创建RDD及查看分区个数
#定义一个数组
val data=Array(1,2,3,4,5)
#使用parallelize()方法创建RDD
val distData=sc.parallelize(data)
#查看RDD默认分区个数
distData.partitions.size
#设置分区个数为4后创建RDD
val distData=sc.parallelize(data,4)
#再次查看RDD分区个数
distData.partitions.size
2.makeRDD()
makeRDDO方法有两种使用方式,第一种使用方式与parallelize()方法一致; 第二种方式是通过接收一个Seq[(T,Seq[String])]参数类型创建RDD。第二种方式生成的RDD中保存的是T的值,Scq[String]部分的数据会按照Seq[(TSeq[String])]的顺序存放到各个分区中,一个Scq[String]对应存放至一个分区,并为数据提供位置信息,通过preferredLocationsO方法可以根据位置信息查看每一个分区的值。调用makeRDD)时不可以直接指定RDD的分区个数,分区的个数与Seq[String]参数的个数是保持一致的。使用makeRDDO方法创建RDD,并根据位置信息查看每一个分区的值。
使用makeRDD()方法创建RDD并查看各分区的值
#定义一个序列seq
val seq=Seq((1,Seq("iteblog.com","sparkhost1.com")),(3,Seq("itebolg.com","sparkhost2.com")),
(2,Seq("iteblog.com","sparkhost3.com")))
#使用makeRDD()方法创建RDD
val iteblog=sc.makeRDD(seq)
#查看RDD的值
iteblog.collect
#查看分区个数
iteblog.partitioner
iteblog.partitions.size
#根据位置信息查看每一个分区的值
iteblog.preferredLocations(iteblog.partitions(0))
iteblog.preferredLocations(iteblog.partitions(1))
iteblog.preferredLocations(iteblog.partitions(2))
1.2从外部存储系统中读取数据创建RDD
从外部存储系统中读取数据创建RDD是指直接读取存放在文件系统中的数据文件创建RDD。从内存中读取数据创建RDD的方法常用于测试,从外部存储系统中读取数据创建RDD才是用于实践操作的常用方法。
从外部存储系统中读取数据创建RDD的方法可以有很多种数据来源,可通过SparkConte对象的textFile()方法读取数据集。textFile()方法支持多种类型的数据集,如目录、文本文件、压缩文件和通配符匹配的文件等,并且允许设定分区个数,分别读取HDFS文件和Linu本地文件的数据并创建RDD,具体操作如下。
(1)通过HDFS文件创建RDD
这种方式较为简单和常用,直接通过textFile()方法读取HDFS文件的位置即可。在HDFS的/user/root目录下有一个文件test.txt,读取该文件创建一个RDD。
通过HDFS文件创建RDD
val test=sc.textFile("/user/root/test.txt")
(2)通过Linux本地文件创建RDD
本地文件的读取也是通过sc.textFile("路径)的方法实现的,在路径前面加上“file://”表示从Linux本地文件系统读取。在IntelliJIDEA开发环境中可以直接读取本地文件;但在spark-shell中,要求在所有节点的相同位置保存该文件才可以读取它,例如,在Linux的/opt目录下创建一个文件test.txt,任意输入4行数据并保存,将test.txt文件远程传输至所有节点的/opt目录下,才可以读取文件test.txt。读取test.txt文件,并且统计文件的数据行数。
通过Linux本地文件创建RDD
#读取本地文件test.txt
val test=sc.textFile("file:///opt/test.txt")
#统计test.txt文件的数据行数
test.count
1.3使用RDD的基本操作
1.使用map()方法转换数据
map()方法是一种基础的RDD转换操作,可以对RDD中的每一个数据元素通过某种函数进行转换并返回新的RDD。map()方法是懒操作,不会立即进行计算。
转换操作是创建RDD的第二种方法,通过转换已有RDD生成新的RDD。因为RDD是一个不可变的集合,所以如果对RDD数据进行了某种转换,那么会生成一个新的RDD。例如,通过一个存放了5个Int类型的数据元素的列表创建一个RDD,可通过map0方法对每一个元素进行平方运算,结果会生成一个新的RDD。
map()方法示例
#创建RDD
val distData=sc.parallelize(List(1,3,45,3,76))
#map()方法求平方值
val sq_dist=distData.map(x->x*x)
2.使用sortBy()方法进行排序
sortBy()方法用于对标准RDD进行排序,有3个可输入参数,说明如下。
(1)第1个参数是一个函数f(T)=>K,左边是要被排序对象中的每一个元素,右边返回的值是元素中要进行排序的值。
(2)第2个参数是ascending,决定排序后RDD中的元素是升序的还是降序的,默认是true,即升序排序,如果需要降序排序则需要将参数的值设置为false。
(3)第3个参数是numPartitions,决定排序后的RDD的分区个数,默认排序后的分区个数和排序之前的分区个数相等,即this.partitions.size。
第一个参数是必须输入的,而后面的两个参数可以不输入。例如,通过一个存放了3个二元组的列表创建一个RDD,对元组的第二个值进行降序排序,分区个数设置为1。
#创建RDD
val data=sc.parallelize(List(1,3),(45,3),(7,6)))
#使用sortBy()方法对元组的第二个值进行降序排序,分区个数设置为1
val sort_data=data.sortBy(x => x._2,false,1)
3.使用collect()方法查询数据
collect()方法是一种行动操作,可以将RDD中所有元素转换成数组并返回到Driver端适用于返回处理后的少量数据。因为需要从集群各个节点收集数据到本地,经过网络传输并且加载到Driver内存中,所以如果数据量比较大,会给网络传输造成很大的压力。因此,数据量较大时,尽量不使用collect()方法,否则可能导致Driver端出现内存溢出问题。collect()方法有以下两种操作方式。
(1)collect:直接调用collect返回该RDD中的所有元素,返回类型是一个Array[T]数组,这是较为常用的一种方式。