【Spark】官方Tutorial笔记

Quick Start

官方文档推荐用Dataset,但是RDD还是要学
我觉得在linux虚拟机里用idea进行本地程序调试最舒服了,具体怎么做请看图解spark那本书

环境搭建

搭建不包含hadoop的单节点伪分布式spark!!!!
搭建不包含hadoop的单节点伪分布式spark!!!!
搭建不包含hadoop的单节点伪分布式spark!!!!
挺简单的,参照我另一篇博客里搭建spark一步一步来就好,把和hadoop相关的都去除就行

More on Dataset Operations

identity就是一个x=>x的函数
Dataset的groupByKey和RDD的groupByKey不太一样哦= =
在这里插入图片描述

Self-Contained Applications

在这里插入图片描述
如果是在idea里调试的时候一定要设置master,我写的是local,但是我写机器的master会报错
看了一下这个博客,这个人的出现这个问题的情况是这样的,他把自己的文件打成了一个jar包,然后提交上集群,报错这个错,但他是把本地的依赖包都给打进一个包里了,如果他没有打一个包,而是没有把依赖包也打进输出的jar里,就没有这个错误,这提醒了我,提交集群的时候,一定不要把依赖打进去。
那为啥本地在idea里提交到集群上也会报同样错呢,我猜测是以这种方式提交,跟“把依赖包同时打在一起”是一个道理,所以就报错了,所以本地调试的时候,尽量local
但要是打包扔到spark-submit上跑就可以不写master,默认会获得环境中的master

打包的教程就不讲了,注意指不指定main class,如果没有的话,spark-submit 要指定 --class作为mainclass,别忘了类名是“包.类”

RDD Programming Guide

突然觉得那本烂书就是在抄官方文档

Basics

不要把checkpoint和persist弄混了
反复提到了partition,这个分区是一个逻辑分区,不是物理上的分区

Closure

意思就是说,worker用的是闭包,所谓闭包就是接下来execute需要用到的一些值,但是这些值是copy
不过= =我跑了一下,我本地模式运行counter也是0,总之要注意这点吧。以后操作数据的时候,尽量直接操作RDD

Shuffle operations

背景里说的其实是宽依赖的故事,就是新生成的RDD里的每个元素都是基于之前的那个RDD里的多个分区生成的

做到这里的时候我发现em。。。idea里运行collect的时候的输出在这里插入图片描述
是这个样的,但是在spark-shell或者spark-submit里就是正常的,怪奇怪的

Shared Variables

再次提到了,These variables are copied to each machine, and no updates to the variables on the remote machine are propagated back to the driver program.

Broadcast Variables

var arr=Array(1, 2, 3)
val broadcastVar = sc.broadcast(arr)
然后尽量不要人为修改arr
read-only

Accumulators

write-only

val accum = sc.longAccumulator("My Accumulator")
sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum.add(x))
accum.value

把上面的accum换成 var naiveCount=0,之后就能看出差别了

Spark SQL

Getting start

Dataset是一种数据形式,而DataFrame实际是就是Dataset[Row],Row是spark中的一种数据类型
这个在API里可以看到,Row的数据结构还挺简单的,看API,那DF和DS的区别在哪呢,其实就在于Row提供了一些比较方便的功能,比如对DF做map的时候,就方便一些
可以用SparkSession对象创建Dataset
在这里插入图片描述

惊了

import org.apache.spark.sql.SparkSession

val spark = SparkSession
  .builder()
  .appName("Spark SQL basic example")
  .config("spark.some.config.option", "some-value")
  .getOrCreate()

// For implicit conversions like converting RDDs to DataFrames
import spark.implicits._

你注意下面的import,是从spark那个对象里import的

创建sparksession,下面不知道对不对

val conf = new SparkConf().setMaster("local").setAppName("for ss")
spark = SparkSession.builder().config(conf).getOrCreate()

Global Temporary View

em我用sparksession读文件的时候提示了一句,这是因为我没配默认的数据库用来存放元数据等数据的数据库,所以也就找不着global_temp这个变量,不过没关系= =反正我也不想配,只是想用而已
在这里插入图片描述

Creating Datasets

注意,这里用的是Seq来构建的数据,其实按理说makeRDD接收到额参数也是Seq类型的,Seq是scala的内置类型,调用Seq(1,2,3),生成的实际是个List

Seq(1,2,3)
#List(1,2,3)

不过调用toDS我猜是个隐式转换,把List转成可以toDS的对象,下面两句都能运行,目前toDS应只支持基本类型和case class类型
创建dataset或者dataframe可以用spark.createDataset等等

val caseClassDS=Seq(Person("Andy",32)).toDS()
val caseClassDS=List(Person("Andy",32)).toDS()

Inferring the Schema Using Reflection

在spark-shell里spark.sparkContext和sc是一个东西
在这里插入图片描述

toDF和toDS
在这里插入图片描述
在这里插入图片描述
下面的代码把一个DataFrame转换成了自定义的Dataset[T]
在这里插入图片描述

对Dataset使用map是有要求的,先描述前提条件

case class Person(name:String,age:Long)
val conf = new SparkConf().setMaster("local").setAppName("for ss")
val sc = new SparkContext(conf)
val spark = SparkSession.builder().config(conf).getOrCreate()

val people=sc.textFile("/home/spark/app/spark/examples/src/main/resources/people.txt").map(_.split(",")).map(a=>Person(a(0),a(1).trim.toInt))
import spark.implicits._
val peopleDS=people.toDS()
val peopleDF=people.toDF()
peopleDF.createOrReplaceTempView("people")
val teenagersDF=spark.sql("select * from people where age<=19")

例子1
下面代码就是乍看很naive

print(teenagersDF.map(a=>"name:"+a.get(1)).show())

例子2
下面代码编译器不通过,这是因为a是一个Row对象,get方法返回的是Any类型的对象

print(teenagersDF.map(a=>1+a.get(1)).show())

得按下面这样做类型转换才行,例子1里之所以能运行,应该是因为隐式转换把函数返回变成了String,内置基本类型

print(teenagersDF.map(a=>1+a.get(1).asInstanceOf[Long]).show())

另外Row类型还提供了另一个方法getAsT,做的事和上面是一样的
例子3
下面代码编译器不报错,但是但是运行阶段都会报错,为啥呢,首先我们知道的是,对Dataset[T]做map,输出的是一个新的Dataset[U],同时Dataset的map方法还有一个隐式参数,类型为Encoder[U],他的作用我理解就是确定map输出的目标Dataset的泛型类型,目前默认情况下只支持基本类型和样例类,所以假如说我们的map函数输出的是一个自定义类型或者Array类型之类的,那么需要我们手动指定Encoder[U]是什么

print(teenagersDF.map(a=>a.get(1)).show())
print(teenagersDF.map(a=>a).show())

按照下面的方式,把encoder显式传入,或者直接定义隐式变量也行,后面两句做的事情是一样的,最终返回的新Dataset是Dataset[Map[String,Any]]类型的

val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]
print(teenagersDF.map(teenager => teenager.getValuesMap[Any](List("name", "age")))(mapEncoder).collect())
print(teenagersDF.map(teenager => Array((""+teenager.get(0),teenager.get(1))).toMap)(mapEncoder).collect())

Programmatically Specifying the Schema

样例代码里没有导入Row,在org.apache.spark.sql.Row
注意schama里的type一定要和数据对应哦

Untyped User-Defined Aggregate Functions

这个例子贼好,下面是个人理解,不知道对不对,可以看到不是强类型的方法

  • initialize:初始化buffer,并且也定义了buffer的大小,图中定义了buffer(0)buffer(1),那么buffer大小就是2
  • update:不谈
  • merge:这个我理解是吧不同partition的结果归并起来

Type-Safe User-Defined Aggregate Functions

bufferEncoder是啥没看懂
Dataset的as方法也不错啊!!!
toColumn.name类似于转换成表中的一列吧

Partition Discovery

分区你懂得,不解释了,存储的时候会出现好多文件夹,读取的时候,直接读根文件夹就ok,因为Spark SQL will automatically extract the partitioning information from the paths.
从1.6后面添加的新特性很有趣奥

Schema Merging

样例的结果长这样
在这里插入图片描述

Broadcast Hint for SQL Queries

??干啥的

ok看完

MLlib

默认是基于数据帧的,推荐用基于数据帧的API,就是ML那个包

Basic Statistics

vector就是一个向量,sparse是稀疏向量,dense是稠密向量

val df = data.map(Tuple1.apply).toDF("features")

上面那个map就相当于把data里的所有Vector都变成了(Vector,),这样才能够进行toDF,toDF目前仅仅支持的是基本类型、array、tuple、case class等等,需要额外说明的是,尽管data经过map之后,每个元素都变成了(Vector,),但是toDF之后,该列的类型仍然是Vector哦,并不是(Vector,),为啥呢,请看下面的第三个例子你就懂了

val nums=List(1,2,3)
nums.toDF
val nums=List(Array(1),Array(2),Array(3))
nums.toDF
val nums=List((1,2),(3,4),(5,6))
nums.toDF.show
val Row(coeff1: Matrix)= Correlation.corr(df, "features").head

上面等号左面像是Row的unapply用法,但是api里没找到= =但是绝壁是
其中Correlation.corr(df, “features”)返回的是一个DataFrame对象,长下面这样
在这里插入图片描述
.head就是取第一个Row
如下代码是正经的创建带标签的DataFrame方法

val data = Seq(
  (0.0, Vectors.dense(0.5, 10.0)),
  (0.0, Vectors.dense(1.5, 20.0)),
  (1.0, Vectors.dense(1.5, 30.0)),
  (0.0, Vectors.dense(3.5, 30.0)),
  (0.0, Vectors.dense(3.5, 40.0)),
  (1.0, Vectors.dense(3.5, 40.0))
)

val df = data.toDF("label", "features")

Pipelines

DAG有什么用??

Example: Estimator, Transformer, and Param

例子很好,需要说道说道

  • val training这段最后的toDF是为了指定column名,这一步很有必要为什么一会儿说
  • val model1 = lr.fit(training)的model是一个LogisticsRegressionModel类的对象,其parent为The parent estimator that produced this model.是一个Estimator[LogisticsRegression]对象。Estimator:An Estimator abstracts the concept of a learning algorithm or any algorithm that fits or trains on data.指的就是那个算法lr。
  • model1 这个对象怎么知道的哪列是feature哪列是label,输出的样本中凭什么预测结果叫prediction后验概率叫probability(model2的是myProbability),因为这些都是在lr的参数里确定下来的,从lr.extractParamMap()或者model1.parent.extractParamMap()都能看到有
  • 在这里插入图片描述

Example: Pipeline

  • Tokenizer:是一个简单的空格分词器,需要制定输入列和输出列
  • HashingTF:Maps a sequence of terms to their term frequencies using the hashing trick.输出列内的类型是Vector类型
  • lr:通过参数来知道哪一列是feature和label
  • 最终输出类型可以通过sameModel.transform(test).schema)来看

TF-IDF

??? TFHashing算法我也没找到是具体咋解释,用到了再说吧

CountVectorizer

为啥是这样,因为texts[0],a出现了1次,b出现了1次;texts[1]里,a出现了2次,c出现了1次

idtextsvector
0Array(“a”, “b”, “c”)(3,[0,1,2],[1.0,1.0,1.0])
1Array(“a”, “b”, “b”, “c”, “a”)(3,[0,1,2],[2.0,2.0,1.0])

Tokenizer

意思就是说,如果把gaps变成false,regex中的pattern表示的就不是分隔符,而是找符合pattern的词作为一个单词

Alternatively, users can set parameter “gaps” to false indicating the regex “pattern” denotes “tokens” rather than splitting gaps, and find all matching occurrences as the tokenization result.

另外学到了udf和col的用法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值