1. RDD
ctrl+h
查看实现方法
RDD方法 => RDD算子: RDD根据数据处理方式的不同将算子整体上分为Value类型、双Value类型和Key-Value类型
- Value型: map :分区内的数据执行是有序列,分区间是无序的.
本质:一个RDD就是一个弹性分布式对象集合,本质上是一个只读的分区记录集合,每个RDD可分成多个分区,每个分区就是一个数据集片段。
// 创建一个spark config
import org.apache.spark.{SparkConf, SparkContext}
val conf = new SparkConf().setAppName("spark").setMaster("local[*]")
val sc = new SparkContext(conf)
1.1 RDD的创建
- 采用textFile()方法从文件系统中加载数据创建RDD
// 例如:HDFS分布式系统文件:
val lines = sc.textFile("word.txt")
- 通过并行集合(数组)创建RDD
// 通过调用SparkContext的parallelize方法:
val array = Array(1,2,3,4,5) // or List
val rdd = spark.parallelize(array)
1.2 持久化
背景:每次调用行动操作时,都会触发一次从头开始的计算
- 使用
persist()
方法对一个rdd
标记为持久化 cache()
等同于persist(MEMORY_ONLY)
unpersist()
删除持久化的rdd
val list = List("hardoop","spark","Hive")
val rdd = sc.parallelize(list)
rdd.cache()
print(rdd.count())
print(rdd.collect().mkString(",")) // 不需要触发从头开始计算
1.3 分区
// 1. 创建rdd时,在调用textFile和parallelize方法的时候,手动指定分区个数即可
val rdd = sc.textFile(path,partitionNum)
// 2. 通过转换操作得到新的RDD时,直接调用repartition方法即可
1.4 RDD相关的操作
1.4.1 转换操作
// 1. map(func):将每个元素传递到函数func中,并将结果返回一个新的数据集
// 2.filter(func): 筛选满足函数func的元素,并返回一个新的数据集
val lines = sc.textFile("file:///user/local/spark/mycode/word.txt")
lines.filter(line => line.contains("spark")).count()
// 3. flatMap(func): 与map相似,但是每个输入元素都可以映射到0或多个输出结果
1.4.2 行动操作
// 1. count():返回数据集中的元素个数
// 2. collect(): 以数组的形式返回数据集中的所有元素
// 3. frist(): 返回数据集中的第一个元素
// 4. take(): 以数组的形式返回数据集中的前n个元素
// 5. reduce(func):通过函数func(输入两个参数,并返回一个值)聚合数据中的元素
lines.map(line => line.split(" ").size).reduce((a,b) => if (a>b) a else b) // 找出文本中单行文本所包含单词数最多的行
// 6. foreach(func):将数据集中的每个元素传递到函数func中运行
1.4.3 PairRDD(键值对)
- 创建
// 通过map函数实现:
val pairRDD = lines.flatMap(line => line.split(" ")).map(word => (word,1))
- 转换操作
// 1. groupByKey():应用于(k,v)键值对的数据集时,返回一个新的(k,iterable)形式的数据集
val words = Array("one","two","two","three","three","three")
val wordPairRDD = sc.parallelize(words).map(word => (word,1))
val wordCountWithGroup = wordPairsRDD.groupByKey().map(t=>(t._1,t._2.sum))
// 2. reduceByKey(func): 应用于(k,v)键值对的数据集时,返回一个新的(k,v)形式的数据集,其中的每个值是将每个key传递到函数func中进行聚合
val wordCountsWithReduce = wordPairsRDD.reduceByKey(_+_)
// 3. keys():只会把pair rdd中的key返回形成一个新的RDD
pairRDD.keys.foreach(println)
// 4. values(): 只会把pair rdd中的values返回形成一个新的RDD
// 5. sortByKey():返回一个根据键排序的RDD
pairRDD.reduceByKey(_+_).sortByKey(false).collect
// 6. sortBy()
pairRDD.reduceByKey(_+_).sortBy(_._2,false).collect
// 7. mapValues():对键值对RDD中的每个value都对应一个函数
pairRDD.mapValues(x=>x+1)
// 8. join: 内链接,对于给定的两个输入数据集(k,v1)和(k,v2)只有在两个数据集中都存在的key才会被输出,最终的得到一个新的(k,(v1,v2))类型的数据集。
val pairRDD1=sc.parallelize(Array(("spark",1),("spark",2),("hadoop",3),("hadoop",5)))
val pairRDD2=sc.parallelize(Array(("spark",fast)))
pairRDD1.join(pairRDD2).foreach(println)
1.5 综合应用
- 题目:给定一组键值对
("spark",2),("hadoop",4),("hadoop",6).("spark",6)
, 键值对的key表示图书名称,value表示某天图书的销量,请计算每个键对应的平均值,也就是计算每种图书的每天的平均销量。
val rdd = sc.parallelize(Array(("spark",2),("hadoop",4),("hadoop",6).("spark",6)))
rdd.mapValues(x=>(x,1)).reduceBykey((x,y)=>(x._1+y._1,x._2+y._2)).mapValues(x=>(x._1/x._2)).collect()
- 求TOP值
两个文件中包含id、user_id、payment、product_id,求Top N个payment值
import org.apach.spark.{SparkConf,SparkContext}
object TopValues{
def main(args,Array[String]):Unit={
val = conf new SparkConf().setAppName("TopValues").setMaster('local')
val sc =new SparkContext(conf)
sc.setLogLevel("ERROR")
val lines = sc.textFile("hdfs://localhost:9000/user/hadoop/spark/chaper",2)
var num =0
// 转化为pair格式,才能使用sortByKey
val result lines.filter(line => (line.trim().length>0)&&(line.split(",").length==4)).map(_.split(",")(2)).map(x=>(x.toint,"")).sortByKey(false).map(x=>x._1).take(5).foreach(x=>{
num=num+1
println(num+"\t"+x)
})
}
}
- 求出多个文件中数值的最大最小值
import org.apach.spark.{SparkConf,SparkContext}
object MaxAndMin{
def main(args,Array[String]):Unit={
val = conf new SparkConf().setAppName("MaxAndMin").setMaster('local')
val sc =new SparkContext(conf)
sc.setLogLevel("ERROR")
val lines = sc.textFile("hdfs://localhost:9000/user/hadoop/spark/chaper5",2)
val result =lines.filter(_.trim().length>0).map(x=>{
val max = Integer.MAX_VALUE
val min = Integer.MIN_VALUE
for (num <- x._2){
if(num>max){
max= num
}if(num<min){
min=num}
}
(max,min)}).colect.foreach(x=>{
println("max\t" + x._1)
println("min\t" + x._2)
})
}
}
- 有多个输入文件,每个文件中每一行内容均为一个整数,排序之后输出两列,排序次位以及值。
import org.apach.spark.{SparkConf,SparkContext,SparkContext._}
object MySort{
def main(args:Array[String]){
val = conf new SparkConf().setAppName("MySort").setMaster('local')
val sc =new SparkContext(conf)
val data = sc.textFile('data',3)// 读取3个分区文件
var index =0
val result =data.filter(_.trim().length>0).map(n=>(n.trim.toInt,"")).sortByKey().map(t=>{
index+=1
(index,t._1)
})
result.saveAsTextfile("result")
}
}
- 二次排序
对于一个给定的文件,请对数据进行排序,首先根据第1列数据进行数据降序排列,如果第一列数据相等,则根据第2列数据降序排列。
package cn.edu.zhuht.spark
class SecondarySortKey(val first:Int, val second:Int) extends Ordered [SecondarySortKey] with Serializable{
def compare(other:SecondarySortKey):Int={
if(this.first - other.first!=0){
this.first -other.first
}else{
this.second - other.second
}
}
}
package cn.edu.zhuht.spark
import org.apach.spark.{SparkConf,SparkContext}
object SecondarySortApp{
def main(args:Array[String]){
val conf =new SparkConf().setAppName("SecondarySortApp").setMaster("local")
val sc = new SparkContext(conf)
val lines =sc.textFile("file.txt")
val pairWithSortKey =line.map(line=> (new SecondarySortKey(line.split(" ")(0).toInt,line.split(" ")(1).toInt),line))
val sorted = pairWithSortKey.sortByKey(false)
val sortedResult =sorted.map(sortedLine => sortedLine._2)
sortedResult.collect().foreach(println)
}
}
2. 数据读写
2.1 文本文件
// 1. 本地文件系统的数据读取:
val textFile = spark.textFile("file:///user/local/spark/mycode/writeback.txt")
// 2. 分布式文件系统HDFS:
val textfile = sc.textFile('word.txt')
// 3. 写入数据:textFile.saveAsTextFile("file:///user/local/spark/mycode/word.txt")
2.2 JSON文件数据
// 1. 读取:rdd中的元素就是一个个json字符串
val jsn = sc.textFile("file:///user/local/spark/mycode/people.json")
// 2. 解析:
jsn.map(s => JSON.parseFull(s))
2.3 3. HBase数据
定义:HBase是一个稀疏、多维度、排序的映射表,这张表的索引是行键、列簇、列限定符和时间戳
3. SparkSQL
数据类型:DataFrame
3.1创建
// 1. 使用SparkSession支持从不同的数据源加载数据,并把数据转换成DataFrame
val df=spark.read.josn(file_path)
// 2. rdd.toDF()
3.2 常用操作
// 1. 打印模型信息:
df.printSchema()
// 2. 选择多列:
df.select(df("name"),df("age")+1).show
// 3. 条件过滤:
df.filter(df("age")>=20).show()
// 4.分组聚合:
df.groupBy("age").count().show()
// 5.排序操作:
df.sort(df("age").desc,df("name").asc).show()
// 6.重命名:
df.select(df("name").as("userName"),df("age")).show()
4. Spark Streaming
原理:是将实时输入数据流以时间片(秒级)为单位进行拆分。分成一段一段的DStream,每一段数据转换为Spark中的rdd
4.1 spark streaming程序的基本操作
- 通过创建输入DStream来定义输入源
- 通过对DStream应用转换操作和输出操作来定义流计算
- 用streamingContext.start()来开始接收数据和处理流程
- 通过streamingContext.awaitTermination()方法来等待处理结束
- 可以通过streamingContext.stop() 来手动结束流计算进程
4.2 创建StreamingContext对象
如果要运行一个spark Streaming
程序,就需要首先生成一个StreamingContex
t对象,他是spark streaming
程序的主入口。
// 可以从sparkConf对象创建一个StreamningContext对象:
val ssc = new StreamingContext(sc,Seconds(1))
4.3 DStream转换操作
4.3.1 无状态转换操作
// 1. map、filter、reduce、flatMap、count、join、reduceByKey
//2. union、countByValue、transform(func)、cogroup
4.3.2 有状态转换操作
滑动窗口转换操作
// 1. window(): 基于源Dstream产生的窗口化的批数据,计算得到一个新的Dstream
// 2. countByWindow(): 返回流中元素的一个滑动窗口数
// 3. reduceByWindow():返回一个单元素流
// 4. reduceByKeyAndWindow():应用到一个(K,V)键值对组成的DStream上时,会返回一个有(K,V)键值对组成的新的DStream
updateStateByKey操作
5. SparkMLlib
5.1 简介
应用实例
// 引入包并构建数据集
import org.apache.spark.ml.feature._
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.ml.{Pipeline,PipelineModel}
import org.apache.spark.ml.linalg.Vector
import org.apache.spark.sql.Row
val training = spark.createDataFrame(Seq((0L,"a,b,c,spark",1.0),
(1L,"d,b",0.0),
(2L,"spark,d,h",1.0),
(3L,"hadoop,mapredcue",0.0)
)).toDF("id","text","label")
// 2. 定义Pipeline中的各个工作流阶段PipelineStage,包括转换器和评估器,具体的包含tokenizer,hashingTF和lr
// 转换器
val tokenizer = new Tokenizer().setInputCol("text").setOutputCol("word")
// 转换器
val hashingTF = new HashingTF().setNumFeatures(1000).setInputCol(tokenizer.getOututCol).setOutputCol("features")
// 评估器
val lr = new LogisticRegression().setMaxIter(10).setRegParam(0.01)
// 3. 按照具体的处理逻辑有序地组织PiplineStages,并创建一个pipeline
val pipeline =new Pipeline().setStages(Array(tokenizer,hashingTF,lr))
// 4. 运行fit,产生一个PipelineModel,即生成一个Transformer
val model = pipeline.fit(training)
// 5. 测试集测试,并使用pipelineModel的transform()方法
val test =spark.createDataFrame(Seq((4L,"spark,ih,k"),(5L,"l,m"),(6L,"spark,x,y"),(7L,"apache,ih,k"))).toDF("id","text")
model.transform(test).select("id","text","probability","prediction").collect().foreach{case Row(id:Long, text:String,prob:Vector,prediction:Double)=>println(s"($id,$text)--> prob=$prob, prediction =$prediction")}
5.2 特征处理
5.2.1 文本特征处理
- 分词
MLlib提供了Tokenization方法对文本进行分词,RegexTokenizer基于正则表达式匹配提供了更高级的分词。默认使用多个空格(\s+)作为分隔符。可以通过参数pattern指定分隔符。
import org.apach.spark.ml.feature.{RegexTokenizer,Tokenizer}
val sentenceDataFrame = spark.createDataFrame(seq(
(0,'Hi i heard about spark'),
(1,'I wish java could user case calsses')
)).toDF("label","sentence")
val tokenizer =new Tokenizer().serInputCol("sentence").setOutputCol("words")
val regexTokenizer = new RegexTokenizer()
.setInputCol('sentence')
.setOutputCol("words")
.setPattern("\\W")
.serGaps(false)
val tokenized = tokenizer.transform(sentenceDataFrame)
tokenized.select("word","label").take(2).foreach(println)
val regexTokenized = regexTokenizer.transform(sentenceDataFrame)
regexTokenized.select("word","label").take(2).foreach(println)
去除停用词
import org.apache.spark.ml.feature.StopWordsRemover
val remover = new StopWordsRemover()
.setInputCol("raw")
.setOutputCol("filtered")
val dataSet =spark.createDataFrame(seq(
(0,seq("I","saw","the","red","baloon")),
(1,seq("Mary","had","a","little","lamb"))
)).toDF("id","raw")
remover.transform(dataSet).show()
词稀疏编码
import org.apache.spark.ml.feature.StringIndexer
val df =spark.createDataFrame(
seq((0,"a"),(1,"b"),(2,"c"),(3,"a"),(4,"a"),(5,"c"))
).toDF("id","catagory")
val indexer =new StringIndexer()
.setInputCol("category")
.setOutputCol("categoryIndex")
val indexed =indexer.fit(df).transform(df)
indexed.show()
文本处理还有Ngram、TF-IDF、word2vec 等高级方法。
5.2.2 数值特征处理
特征归一化
Mllib提供了3种归一化方法:StandardScaler、MinMaxScaler和MaxAbsScaler
import org.apache.spark.SparkContext._
import org.apache.spark.mllib.feature.StandarScaler
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.util.MLUtils
val data = MLUtils.loadLibSVMFile(sc,"data/MLlib/sample_libsvm_data.txt")
val scaler1 = new StandarScaler().fit(data.map(x=> x.features))
val scaler2 = new StandarScaler(withMean = true,withStd = true).fit(data.map(x=> x.features))
// scaler2 和 scaler3 相同的模型,并且会产生相同的转换
val scaler3 = new StandarScalerModel(scaler2.std, scaler2.mean)
// data1是单位方差
val data1 = data.map(x=> (x.label,scaler1.transform(x.features)))
//如果不将这些特征转换成密度向量,那么零均值转换就会增加,稀疏向量例外
// data2 是单位方差和零均值
val data2 = data.map(x =>(x.label,scaler2.transform(Vectors.dense(x.features.toArray))))
正则化
import org.apache.spark.SparkContext._
import org.apache.spark.mllib.feature.Normalizer
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.util.MLUtils
val data = MLUtils.loadLibSVMFile(sc,"data/MLlib/sample_libsvm_data.txt")
// 默认计算2范数
val normalizer1 = new Normalizer()
val normalizer2 = new Normalizer(p = Double.PositiveInfinity) // p
// data1中每个样本将使用L2范数进行标准化
val data1 =data.map(x => (x.label,normalizer1.transform(x.features)))
// data2中每个样本将使用无穷范数进行标准化
val data2 =data.map(x => (x.label,normalizer2.transform(x.features)))
二值化
import org.apache.spark.ml.feature.Binarizer
val data = Array((0,0.1),(1,0.8),(2,0.2))
val dataFrame = spark.createDataFrame(data).toDF("label","feature")
val binarizer: Binarizer = new Binarizer()
.serInputCol("feature")
.setOutputCol('binarized_feature')
.setThreshold(0.5)
val binarizerdDataFrame = binarizer.transform(dataFrame)
val binarizerdFeatures = binarizerdDataFrame.select("binarized_feature")
binarizerdFeatures.collect().foreach(println)