spark常用函数:transformation和action

1、RDD提供了两种类型的操作:transformation和action

所有的transformation都是采用的懒策略,如果只是将transformation提交是不会执行计算的,计算只有在action被提交的时候才被触发。

1)transformation操作:得到一个新的RDD,比如从数据源生成一个新的RDD,从RDD生成一个新的RDD

map(func):对调用map的RDD数据集中的每个element都使用func,然后返回一个新的RDD,这个返回的数据集是分布式的数据集

mapValues顾名思义就是输入函数应用于RDDKev-ValueValue,原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素。因此,该函数只适用于元素为KV对的RDD。

mapWith是map的另外一个变种,map只需要一个输入函数,而mapWith有两个输入函数。第一个函数是把RDD的partition index(index0开始)作为输入,输出为新类型A;第二个函数是把二元组(T, A)作为输入(其中T为原RDD中的元素,A为第一个函数的输出),输出类型为U。

mapPartitions(func):和map很像,但是map是每个element,而mapPartitions是每个partition

mapPartitionsWithSplit(func):和mapPartitions很像,但是func作用的是其中一个split上,所以func中应该有index

mapPartitionsWithIndex(func)函数:mapPartitionsWithIndex的func接受两个参数,第一个参数是分区的索引,第二个是一个数据集分区的迭代器。而输出的是一个包含经过该函数转换的迭代器。下面测试中,将分区索引和分区数据一起输出。

sample(withReplacement,faction,seed):抽样,withReplacement为true表示有放回;faction表示采样的比例;seed为随机种子

takeSample() 函数和上面的sample 函数是一个原理,但是不使用相对比例采样,而是按设定的采样个数进行采样,同时返回结果不再是RDD,而是相当于对采样后的数据进行Collect(),返回结果的集合为单机的数组。

filter(func) : 对调用filter的RDD数据集中的每个元素都使用func,然后返回一个包含使func为true的元素构成的RDD

flatMap(func):和map差不多,但是flatMap生成的是多个结果

flatMapValues类似于mapValues,不同的在于flatMapValues应用于元素为KV对的RDD中Value。每个一元素的Value被输入函数映射为一系列的值,然后这些值再与原RDD中的Key组成一系列新的KV对。

flatMapWith与mapWith很类似,都是接收两个函数,一个函数把partitionIndex作为输入,输出是一个新类型A;另外一个函数是以二元组(T,A)作为输入,输出为一个序列,这些序列里面的元素组成了新的RDD。

union(otherDataset):返回一个新的dataset,包含源dataset和给定dataset的元素的集合

distinct([numTasks]):返回一个包含源数据集中所有不重复元素的新数据集

groupByKey(numTasks):返回(K,Seq[V]),也就是hadoop中reduce函数接受的key-valuelist

reduceByKey(func,[numTasks]):就是用一个给定的reducefunc再作用在groupByKey产生的(K,Seq[V]),比如求和,求平均数

sortBy (dataSet, boolean)函数:排序。第二个参数默认为true,即升序排序。

sortByKey([ascending],[numTasks]):按照key来进行排序,是升序还是降序,ascending是boolean类型

join(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,(V,W))的dataset,numTasks为并发的任务数。本质是通过cogroup算子先进行协同划分,再通过flatMapValues 将合并的数据打散。

cogroup(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,Seq[V],Seq[W])的dataset,numTasks为并发的任务数

cartesian(otherDataset):笛卡尔积就是m*n,大家懂的

coalesce(numPartitions, true)函数:对RDD中的分区重新进行合并。返回一个新的RDD,且该RDD的分区个数等于numPartitions个数。如果shuffle设置为true,则会进行shuffle。

repartition(numPartitions)随机重新shuffle RDD中的数据,并创建numPartitions个分区。此操作总会通过网络来shuffle全部数据

pipe(command, [envVars])通过POSIX 管道来将每个RDD分区的数据传入一个shell命令(例如Perl或bash脚本)。RDD元素会写入到进程的标准输入,其标准输出会作为RDD字符串返回。

 

2)action操作:action是得到一个值,或者一个结果(直接将RDD cache到内存中)

reduce(func):说白了就是聚集,但是传入的函数是两个参数输入返回一个值,这个函数必须是满足交换律和结合律的

collect():一般filter或者足够小的结果的时候,再用collect封装返回一个数组

count():返回的是dataset中的element的个数

first():返回的是dataset中的第一个元素      类似于take(1)

take(n):返回前n个elements,这个士driver program返回的

takeSample(withReplacement,num,seed):抽样返回一个dataset中的num个元素,随机种子seed

takeOrdered(n, [ordering])返回一个由数据集的前n个元素组成的有序数组,使用自然序或自定义的比较器。

saveAsTextFile(path):把dataset写到一个text file中,或者hdfs,或者hdfs支持的文件系统中。对于每个元素,Spark将会调用toString方法,spark把每条记录都转换为一行记录,然后写到file中。

saveAsSequenceFile(path)将数据集的元素,以Hadoopsequencefile的格式,保存到指定的目录下,本地系统,HDFS或者任何其它hadoop支持的文件系统。这个只限于由key-value对组成,并实现了Hadoop的Writable接口,或者隐式的可以转换为Writable的RDD。(Spark包括了基本类型的转换,例如Int,Double,String,等等)

saveAsObjectFile(path)将数据集元素写入Java序列化的可以被SparkContext.objectFile()加载的简单格式中

countByKey():返回的是key对应的个数的一个map,作用于一个RDD

foreach(func):对dataset中的每个元素都使用func

 

3)其它 函数操作:

lookup操作:通过key找value值。Lookup 函数对(Key, Value) 型的RDD 操作,返回指定Key 对应的元素形成的Seq。

contains(str)函数:包含str字符串

take 和 takeAsTextFile操作

fold,foldLeft, and foldRight之间的区别

trim函数:把字符两端的空格截掉

top 返回最大的 k 个元素。

take 返回最小的 k 个元素。

takeOrdered 返回最小的 k 个元素,并且在返回的数组中保持元素的顺序。

first 相当于top(1) 返回整个RDD中的前k 个元素,可以定义排序的方式 Ordering[T]。返回的是一个含前k 个元素的数组。

fold 和reduce 的原理相同,但是与reduce 不同,相当于每个reduce 时,迭代器取的第一个元素是zeroValue。

aggregate 先对每个分区的所有元素进行aggregate 操作,再对分区的结果进行fold 操作。aggreagate 与fold 和reduce 的不同之处在于,aggregate相当于采用归并的方式进行数据聚集,这种聚集是并行化的。而在fold 和reduce 函数的运算过程中,每个分区中需要进行串行处理,每个分区串行计算完结果,结果再按之前的方式进行聚集,并返回最终聚集结果。

 

4)Spark 1.4为DataFrame新增的统计与数学函数

随机数据生成(RandomData Generation)

describe函数:概要与描述性统计(Summary and descriptive statistics)

协方差与相关性(Samplecovariance and correlation)

交叉列表(Crosstabulation)

频率项(Frequentitems)

数学函数(Mathematicalfunctions)

 

 

2transformation操作函数总结:

1)、sortBy函数与sortByKey函数:

①sortBy(func, assending, numPartitions)函数:有三个参数,第一个必须要有,后两个可以省略

第一个参数是一个函数,返回类型和RDD中元素的类型是一致的;

第二个参数是ascending,这参数决定排序后RDD中的元素是升序还是降序,默认是true,也就是升序;

第三个参数是numPartitions,该参数决定排序后的RDD的分区个数,默认排序后的分区个数和排序之前的个数相等,即为this.partitions.size。

scala>val data = List(3,1,90,3,5,12)   data: List[Int] =List(3, 1, 90, 3, 5, 12)

scala>val rdd = sc.parallelize(data)   

scala> val result = rdd.sortBy(x => x, false, 1)       false 为倒序排列;第三个参数为设置rdd的分区个数,默认为原rdd的个数

scala>result.partitions.size         res4: Int = 1

 

②sortByKey(boolean)函数:升序或降序由ascending布尔参数决定,默认true为升序。sortByKey函数作用于Key-Value形式的RDD,并对Key进行排序:主要接受两个函数,含义和sortBy一样。该函数返回的RDD一定是ShuffledRDD类型的,因为对源RDD进行排序,必须进行Shuffle操作,而Shuffle操作的结果RDD就是ShuffledRDD。

对Key进行了排序:key为数字,也可以为字符

scala> val a =sc.parallelize(List("wyp", "iteblog", "com","397090770", "test"), 2)

scala> val b = sc.parallelize(List(3,1,9,12,4))          也可以为字符型 val b2 = sc.parallelize(List("3","1",  "9","12", "4"))

scala> val c = b.zip(a)           zipba组成key-value形式,其中bkeyavalue

scala> c.sortByKey().collect                       注意sortByKey的小括号不能省

res33: Array[(Int,String)] = Array((1,iteblog), (3,wyp), (4,test), (9,com), (12,397090770))

 

2)、map函数、flatMap函数

①map(func)与flatMap(func)函数

map是对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD。任何原RDD中的元素在新RDD中都有且只有一个元素与之对应。

scala> val a =sc.parallelize(1 to 9, 3)

scala> val b =a.map(x=>x*2)

scala> b.collect

res48: Array[Int] =Array(2, 4, 6, 8, 10, 12, 14, 16, 18)

把原RDD中每个元素都乘以2来产生一个新的RDD

 

flatMap(func)函数:与map类似,区别是原RDD中的元素经map处理后只能生成一个元素,而原RDD中的元素经flatmap处理后可生成多个元素来构建新RDD。 如果是多个集合,最后合并为一个集合

举例:对原RDD中的每个元素x产生y个元素(从1到y,y为元素x的值)

scala> val a=sc.parallelize(1 to 4, 2)

scala> val b=a.flatMap(x => 1 to x)

res57: Array[Int] =Array(1, 1, 2, 1, 2, 3, 1, 2, 3, 4)

将Rdd的每个集合元素合并为一个集合:

scala> valkv  =sc.parallelize(List(List(1,2),List(3,4),List(3,6,8)))

scala> kv.collect

res8: Array[List[Int]] = Array(List(1, 2), List(3, 4), List(3, 6, 8))     多个集合

scala>kv.flatMap(x=>x.map(_+1)).collect

res9: Array[Int] = Array(2, 3, 4, 5, 4, 7, 9)           一个集合

 

②mapPartitions(func)函数:map的一个变种。map的输入函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区,也就是把每个分区中的内容作为整体来处理的。最终的RDD由所有分区经过输入函数处理后的结果合并起来的。

mapPartitions还有些变种,比如mapPartitionsWithContext,它能把处理过程中的一些状态信息传递给用户指定的输入函数。还有mapPartitionsWithIndex,它能把分区的index传递给用户指定的输入函数。

scala> val nums =sc.parallelize(1 to 9, 3)

scala> defmyfunc[T](iter: Iterator[T]): Iterator[(T, T)]={

     | var res = List[(T, T)]()

     | var pre = iter.next

     | while(iter.hasNext) {

     |  val cur = iter.next;

     |  res.::= (pre, cur)

     |  }

     | res.iterator

     | }

scala>nums.mapPartitions(myfunc).collect

res10: Array[(Int,Int)] = Array((1,3), (1,2), (4,6), (4,5), (7,9), (7,8))

 

mapPartitionsWithIndex(func)函数:mapPartitionsWithIndex的func接受两个参数,第一个参数是分区的索引,第二个是一个数据集分区的迭代器。而输出的是一个包含经过该函数转换的迭代器。下面测试中,将分区索引和分区数据一起输出。

scala> val x =sc.parallelize(List(1,2,3,4,5,6,7,8,9,10), 3)

scala>def myfunc(index: Int, iter:Iterator[Int]): Iterator[String] = {

     | iter.toList.map(x => index+"-"+x).iterator

     | }

scala>x.mapPartitionsWithIndex(myfunc).collect

res12: Array[String]= Array(0-1, 0-2, 0-3, 1-4, 1-5, 1-6, 2-7, 2-8, 2-9, 2-10)

 

③mapValues与flatMapValues函数

mapValues顾名思义就是输入函数应用于RDDKev-ValueValue,原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素。因此,该函数只适用于元素为KV对的RDD。

scala> val a =sc.parallelize(List("dog", "tiger", "lion","cat", "panther", "eagle"), 2)

scala> val b =a.map(x => (x.length, x))

scala>b.mapValues("x"+_+"x").collect

res54: Array[(Int,String)] = Array((3,xdogx), (5,xtigerx), (4,xlionx), (3,xcatx), (7,xpantherx),(5,xeaglex))

 

flatMapValues

flatMapValues类似于mapValues,不同的在于flatMapValues应用于元素为KV对的RDD中Value。每个一元素的Value被输入函数映射为一系列的值,然后这些值再与原RDD中的Key组成一系列新的KV对。

scala> val a =sc.parallelize(List((1,2),(3,4),(3,6)))

scala> val b =a.flatMapValues(x=>x.to(5))

scala> b.collect

res59: Array[(Int,Int)] = Array((1,2), (1,3), (1,4), (1,5), (3,4), (3,5))

原RDD中每个元素的值被转换为一个序列(从其当前值到5),比如第一个KV对(1,2), 其值2被转换为2,3,4,5。然后其再与原KV对中Key组成一系列新的KV对(1,2),(1,3),(1,4),(1,5)。

 

④mapWith与flatMapWith函数

mapWith是map的另外一个变种,map只需要一个输入函数,而mapWith有两个输入函数。第一个函数是把RDD的partition index(index0开始)作为输入,输出为新类型A;第二个函数是把二元组(T, A)作为输入(其中T为原RDD中的元素,A为第一个函数的输出),输出类型为U。

举例:把partitionindex 乘以10,然后加上2作为新的RDD的元素。

scala> val x = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10), 3)      有三个分区,每个分区的下标为0,1,2

scala>x.mapWith(a => a*10)((a, b)=>(b+2)).collect

res56: Array[Int] =Array(2, 2, 2, 12, 12, 12, 22, 22, 22, 22)

 

flatMapWith与mapWith很类似,都是接收两个函数,一个函数把partitionIndex作为输入,输出是一个新类型A;另外一个函数是以二元组(T,A)作为输入,输出为一个序列,这些序列里面的元素组成了新的RDD。

scala> val a =sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)

scala>a.flatMapWith(x=>x, true)((x, y)=>List(y, x)).collect

res58: Array[Int] =Array(0, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 2, 7, 2, 8, 2, 9)

 

3)、sample函数:sample(withReplacement,faction,seed):抽样,withReplacement为true表示又放回;faction表示采样的比例;seed为随机种子

scala> val a =sc.parallelize(1 to 10000, 3)

scala>a.sample(false, 0.1, 0).count

res15: Long = 956

 

4)、distinct去重函数、union求并函数、intersection求交集函数

scala> val kv1 =sc.parallelize(List(("A", 1), ("B", 2), ("C", 3),("A", 4), ("B", 5)))

scala> val kv2 =sc.parallelize(List(("A", 4), ("A", 2), ("C", 3),("A", 4), ("B", 5)))

scala> kv2.distinct.collect

res17:Array[(String, Int)] = Array((A,4), (B,5), (C,3), (A,2))

 

scala> kv1.union(kv2).collect

res18:Array[(String, Int)] = Array((A,1), (B,2), (C,3), (A,4), (B,5), (A,4), (A,2),(C,3), (A,4), (B,5))

 

scala> kv1.intersection(kv2).collect

res19:Array[(String, Int)] = Array((A,4), (B,5), (C,3))

 

5)、sortByKey,groupByKey, reduceByKey等transformation操作涉及到了shuffle操作,所以这里引出两个概念宽依赖和窄依赖。

窄依赖(narrowdependencies)

子RDD的每个分区依赖于常数个父分区(与数据规模无关)

输入输出一对一的算子,且结果RDD的分区结构不变。主要是map/flatmap

输入输出一对一的算子,但结果RDD的分区结构发生了变化,如union/coalesce

从输入中选择部分元素的算子,如filter、distinct、substract、sample

宽依赖(widedependencies)

子RDD的每个分区依赖于所有的父RDD分区

对单个RDD基于key进行重组和reduce,如groupByKey,reduceByKey

对两个RDD基于key进行join和重组,如join

经过大量shuffle生成的RDD,建议进行缓存。这样避免失败后重新计算带来的开销。

注意:reduce是一个action,和reduceByKey完全不同,reduceByKey是transformation操作。

 

sortByKey(boolean)函数:升序或降序由ascending布尔参数决定,默认true为升序

scala> val kv1 =sc.parallelize(List(("A", 1), ("B", 2), ("C", 3),("A", 4), ("B", 5)))

kv1:org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[28] atparallelize at <console>:21

scala> kv1.sortByKey().collect                            注意sortByKey的小括号不能省

res23:Array[(String, Int)] = Array((A,1), (A,4), (B,2), (B,5), (C,3))

 

groupByKey函数:在一个(K,V)对的数据集上调用,返回一个(K,Seq[V])对的数据集

注意:默认情况下,只有8个并行任务来做操作,但是你可以传入一个可选的numTasks参数来改变它。如果分组是用来计算聚合操作(如sum或average),那么应该使用reduceByKey或combineByKey 来提供更好的性能。

scala>kv1.groupByKey().collect

res21:Array[(String, Iterable[Int])] = Array((B,CompactBuffer(2, 5)),(A,CompactBuffer(4, 1)), (C,CompactBuffer(3)))

 

reduceByKey函数:在一个(K,V)对的数据集上调用时,返回一个(K,V)对的数据集,使用指定的reduce函数,将相同key的值聚合到一起。类似groupByKey,reduce任务个数是可以通过第二个可选参数来配置的

scala>kv1.reduceByKey(_+_).collect

res22:Array[(String, Int)] = Array((B,7), (A,5), (C,3))

 

6)、coalesce(numPartitions, true)函数:RDD中的分区重新进行合并(减少rdd的分)。返回一个新的RDD,且该RDD的分区个数等于numPartitions个数。如果shuffle设置为true,则会进行shuffle。

scala> var data =sc.parallelize(List(1,2,3,4), 4)

scala>data.partitions.length

res27: Int = 4

scala> val result= data.coalesce(2, false)

scala>result.partitions.length

res28: Int = 2

 

repartition(numPartitions)随机重新shuffle RDD中的数据,并创建numPartitions个分区。这个操作总会通过网络来shuffle全部数据

 

7)、join函数和cogroup函数:

join(otherDataset,[numTasks])在类型为(K,V)和(K,W)类型的数据集上调用时,返回一个相同key对应的所有元素对在一起的(K, (V, W))数据集

scala> valkv1=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5)))

scala> valkv3=sc.parallelize(List(("A",10),("B",20),("D",30)))

scala>kv1.join(kv3).collect

res31:Array[(String, (Int, Int))] = Array((B,(2,20)), (B,(5,20)), (A,(4,10)),(A,(1,10)))

 

cogroup(otherDataset, [numTasks])在类型为(K,V)和(K,W)的数据集上调用,返回一个 (K, Seq[V], Seq[W])元组的数据集。这个操作也可以称之为groupwith

scala>kv1.cogroup(kv3).collect

res32:Array[(String, (Iterable[Int], Iterable[Int]))] = Array((B,(CompactBuffer(5,2),CompactBuffer(20))), (D,(CompactBuffer(),CompactBuffer(30))),(A,(CompactBuffer(1, 4),CompactBuffer(10))),(C,(CompactBuffer(3),CompactBuffer())))

 

8)、sortBy (dataSet, boolean)函数:排序。第二个参数默认为true,即升序排序。

scala> val rdd =sc.parallelize(List(3,1,90,3,5,12))

scala> rdd.sortBy(x => x, false)       第二个参数默认为true,即升序排列

res40: Array[Int] =Array(90, 12, 5, 3, 3, 1)

查看执行后rdd 分区的个数:   scala> result.partitions.size

res18: Int = 2      

注意:默认为两个,但可以手动修改rdd的分区个数手动修改rdd分区的个数

scala> val result= rdd.sortBy(x => x, false, 1)

scala>result.partitions.size

res21: Int = 1

 

3action操作函数总结:

1)、reduce(func)函数、reduceByKey函数transformation操作)

reduce(func)将RDD中元素两两传递给输入函数,同时产生一个新的值,新产生的值与RDD中下一个元素再被传递给输入函数直到最后只有一个值为止。

scala> val c =sc.parallelize(1 to 10)

scala> c.reduce((x, y) => x+y)     res60: Int = 55         RDD中的元素求和

 

reduceByKey就是对元素为KV对的RDDKey相同的元素的Value进行reduce,因此,Key相同的多个元素的值被reduce为一个值,然后与原RDD中的Key组成一个新的KV

scala> val a =sc.parallelize(List((1,2), (3,4), (3,6)))

scala>a.reduceByKey((x, y) => x +y).collect

res61: Array[(Int,Int)] = Array((1,2), (3,10))

对Key相同的元素的值求和,因此Key为3的两个元素被转为了(3,10)。

 

2)、foreach(func)在数据集的每一个元素上,运行函数func进行更新。这通常用于边缘效果,例如更新一个累加器,或者和外部存储系统进行交互,例如HBase.

scala> valnum=sc.parallelize(1 to 10)

scala>num.take(5).foreach(println)

 

3)、

 

4、其它函数总结:

1)、zip函数:把两个单独的rdd组合为key-value形式

scala> val a =sc.parallelize(List("wyp", "iteblog", "com","397090770", "test"), 2)

scala> val b =sc.parallelize(List(3,1,9,12,4))

scala> val c = b.zip(a)           zipba组成key-value形式,其中bkeyavalue

scala>c.sortByKey().collect

res33: Array[(Int,String)] = Array((1,iteblog), (3,wyp), (4,test), (9,com), (12,397090770))

 

2)、lookup操作:通过key找value值。

scala> val rdd2 =sc.parallelize(List(('a', 1), ('a', 2), ('b', 1), ('b', 3)))

scala>rdd2.lookup('a')

res15: Seq[Int] =WrappedArray(1, 2)

scala>rdd2.lookup('b')

res16: Seq[Int] =WrappedArray(1, 3)

 

3)take 和takeAsTextFile操作

[hadoop@hadoop1 ~]$ cat a.txt

2014-03-04        121212121212        12

2014-03-06        161616161616        16

[hadoop@hadoop1 ~]$ cat b.txt

2014-03-02        121212121212        1        33.555333        -117.888878786

2014-03-06        161616161616        2        33.677666        -117.886868687

 

scala> val format= new java.text.SimpleDateFormat("yyyy-mm-dd")

scala> case classRegister(d: java.util.Date, uuid: String, cust_id: String , lat: Float, lng:Float)

scala> case classClick(d: java.util.Date, uuid: String, landing_page: Int)

首先,读取文件的内容;然后,以tab键进行分词,接着以第二列为key,每一行的所有内容为Value构建起的Register作为Value的值

scala> val reg =sc.textFile("/ai/b.txt").map(_.split('\t')).map(r => (r(1),Register(format.parse(r(0)), r(1), r(2), r(3).toFloat, r(4).toFloat)))

scala> val clk =sc.textFile("/ai/a.txt").map(_.split('\t')).map(c =>(c(1),Click(format.parse(c(0)), c(1), c(2).trim.toInt)))

clk:org.apache.spark.rdd.RDD[(String, Click)] =MapPartitionsRDD[86] at map at <console>:25

 

scala> val result = reg join clk  ——》result: org.apache.spark.rdd.RDD[(String, (Register, Click))] = MapPartitionsRDD[89] at join at <console>:31

scala>result.take(2)

res17:Array[(String, (Register, Click))] = Array((121212121212,(Register(Thu Jan 02 00:03:00 CST 2014,121212121212,1,33.555332,-117.88888),Click(Sat Jan 04 00:03:00 CST 2014,121212121212,12))),(161616161616,(Register(Mon Jan 06 00:03:00 CST2014,161616161616,2,33.677666,-117.88687),Click(Mon Jan 06 00:03:00 CST2014,161616161616,16))))

 

scala>result.saveAsTextFile("/ai/join/result")

[hadoop@hadoop1 ~]$hadoop fs -cat /ai/join/result/part-00000

(121212121212,(Register(ThuJan 02 00:03:00 CST 2014,121212121212,1,33.555332,-117.88888),Click(Sat Jan 0400:03:00 CST 2014,121212121212,12)))

(161616161616,(Register(MonJan 06 00:03:00 CST 2014,161616161616,2,33.677666,-117.88687),Click(Mon Jan 0600:03:00 CST 2014,161616161616,16)))

 

4)fold, foldLeft, and foldRight之间的区别

主要的区别是fold函数操作遍历问题集合的顺序。foldLeft是从左开始计算,然后往右遍历。foldRight是从右开始算,然后往左遍历。而fold遍历的顺序没有特殊的次序。

scala> valnumbers = List(5,4,8,6,2)

scala>numbers.fold(0){(z,i)=>z+i}

res50: Int = 25

List中的fold方法需要输入两个参数:初始值以及一个函数。输入的函数也需要输入两个参数:累加值和当前item的索引。那么上面的代码片段发生了什么事?

代码开始运行的时候,初始值0作为第一个参数传进到fold函数中,list中的第一个item作为第二个参数传进fold函数中。

1、fold函数开始对传进的两个参数进行计算,在本例中,仅仅是做加法计算,然后返回计算的值;

2、Fold函数然后将上一步返回的值作为输入函数的第一个参数,并且把list中的下一个item作为第二个参数传进继续计算,同样返回计算的值;

3、第2步将重复计算,直到list中的所有元素都被遍历之后,返回最后的计算值,整个过程结束;

4、这虽然是一个简单的例子,让我们来看看一些比较有用的东西。早在后面将会介绍foldLeft函数,并解释它和fold之间的区别,目前,你只需要想象foldLeft函数和fold函数运行过程一样。

下面是一个简单的类和伴生类

scala> classFoo(val name:String, val age:Int, val sex:Symbol)

scala> objectFoo{

     | def apply(name:String, age:Int, sex:Symbol)=new Foo(name, age, sex)

     | }

假如我们有很多的Foo实例,并存在list中:

scala> valfooList=Foo("Hugh Jass", 25, 'male)::Foo("Biggus Dickus",43, 'male)::Foo("Incontinetia Buttocks", 37, 'female)::Nil

我们想将上面的list转换成一个存储[title] [name], [age]格式的String链表:

scala> valstringList = fooList.foldLeft(List[String]()) {(z,f)=>

     | val title=f.sex match{

     |  case 'male=>"Mr."

     |  case 'female=>"Ms."

     |  }

     |  z:+ s"$title ${f.name},${f.age}"

     | }

 

5)trim函数:把字符两端的空格截掉

scala>val people =sc.textFile("/spark/people.txt").map(_.split(",")).map(p=>Person(p(0),p(1).trim.toInt)).toDF()  

people: org.apache.spark.sql.DataFrame = [name: string, age: int]        注意:如果去掉trim,会报错

scala>people.registerTempTable("people")

 

5、spark的统计与数学函数

1)随机数据生成(Random Data Generation)主要是为测试数据提供方便快捷的接口,如range、rand和randn。rand函数提供均匀正态分布,而randn则提供标准正态分布。在调用这些函数时,还可以指定列的别名,以方便我们对这些数据进行测试。

scala> val df =sqlContext.range(1, 10).withColumn("uniform",rand(seed=10)).withColumn("normal", randn(seed=27))

scala> sqlContext.range(1, 10).show          生成一个属性为id的数据框,左闭右开,即不包含10

+---+

| id|

+---+

|  1|

。。。

|  9|

+---+

 

2)概要与描述性统计(Summary and Descriptive Statistics)包含了计数、平均值、标准差、最大值、最小值运算。只需要针对DataFrame调用describe函数即可:

scala>df.describe().show

scala> people.describe().show            注意:此括号不能省略

+-------+------------------+--------------------+-------------------+

|summary|                id|             uniform|             normal|

+-------+------------------+--------------------+-------------------+

|  count|                 9|                   9|                  9|

|   mean|               5.0|  0.3501113296528809|-0.4749238142128228|

|stddev|2.5819888974716116| 0.18389046183528623| 0.9742194777287445|

|    min|                 1|0.018326130186194667|-2.372340011831022|

|    max|                 9|  0.7224977951905031| 0.7873642272821919|

+-------+------------------+--------------------+-------------------+

 

如果返回的DataFrame含有大量的列,你可以返回其中的一部分列:

scala>df.describe("uniform", "normal").show()

+-------+--------------------+-------------------+

|summary|             uniform|             normal|

+-------+--------------------+-------------------+

|  count|                   9|                  9|

|   mean| 0.3501113296528809|-0.4749238142128228|

| stddev|0.18389046183528623| 0.9742194777287445|

|    min|0.018326130186194667|-2.372340011831022|

|    max| 0.7224977951905031| 0.7873642272821919|

+-------+--------------------+-------------------+

 

自定选择要统计的列及函数:

scala>df.select(mean("uniform"), min("uniform"),max("uniform")).show()

+------------------+--------------------+------------------+

|      avg(uniform)|        min(uniform)|      max(uniform)|

+------------------+--------------------+------------------+

|0.3501113296528809|0.018326130186194667|0.7224977951905031|

+------------------+--------------------+------------------+

 

3)样本协方差和相关性(Sample covariance and correlation)

协方差表示的是两个变量的总体的误差。正数意味着其中一个增加,另外一个也有增加的趋势;而负数意味着其中一个数增加,另外一个有降低的趋势。DataFrame两列中的样本协方差计算可以如下:

scala> val df =sqlContext.range(0, 10).withColumn("rand1",rand(seed=10)).withColumn("rand2", rand(seed=27))

scala> df.stat.cov("rand1", "rand2")

res18: Double =0.003702640706789616

scala>df.stat.cov("id", "id")

res19: Double =9.166666666666666

两个随机生成的列之间的协方差接近零;而id列和它自己的协方差非常大。

协方差的值为9.17可能很难解释,而相关是协方差的归一化度量,这个相对更好理解,因为它提供了两个随机变量之间的统计相关性的定量测量。

scala> df.stat.corr("rand1", "rand2")

res21: Double =0.07372733310735549

scala> df.stat.corr("id", "id")

res22: Double = 1.0

ID那列完全与相关本身;而两个随机生成的列之间的相关性非常低。

 

4)交叉分类汇总表(又称列联表)(Cross tabulation)

如果同时按几个变量或特征,把数据分类列表时,这样的统计表叫作交叉分类汇总表,其主要用来检验两个变量之间是否存在关系,或者说是否独立。在Spark 1.4中,我们可以计算DataFrame中两列之间的交叉分类汇总表,以便获取计算的两列中不同对的数量,下面是关于如何使用交叉表来获取列联表的例子

scala> val df =sqlContext.jdbc("jdbc:mysql://node3:3306/test?user=hive&password=mysql","cross_table")

scala> df.show

+-----+------+

| name|  item|

+-----+------+

|Alice|  milk|

|Alice|  milk|

|Alice| bread|

|Alice|butter|

|  Bob|butter|

|  Bob|butter|

|  Bob|butter|

|  Bob|apples|

+-----+------+

scala>df.stat.crosstab("name", "item").show()

+---------+------+------+----+-----+

|name_item|apples|butter|milk|bread|

+---------+------+------+----+-----+

|      Bob|    1|     3|   0|   0|

|    Alice|    0|     1|   2|   1|

+---------+------+------+----+-----+

我们需要记住,列的基数不能太大。也就是说,name和item distinct之后的数量不能过多。试想,如果item distinct之后的数量为10亿,那么你如何在屏幕上显示这个表??

 

5)频繁项(Frequent items)

了解列中那些频繁出现的item对于我们了解数据集非常重要。在Spark 1.4中,我们可以通过使用DataFrames来发现列中的频繁项,

 

6)数学函数(Mathematical functions)

Spark 1.4中增加了一系列的数学函数,用户可以自如地将这些操作应用到他们列。我可以在这里看到所有的数学函数。输入必须是一个列函数,并且这个列函数只能输入一个参数,比如cos, sin, floor, ceil。对于那些需要输入两个参数的列函数,比如pow, hypot,我们可以输入两列或者列的组合。

 

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值