【Todo】【读书笔记】大数据Spark企业级实战版 & Scala学习

目录:/Users/baidu/Documents/Data/Interview/Hadoop-Spark-Storm-Kafka

下了这本《大数据Spark企业级实战版》,

另外还有一本《Spark大数据处理:技术、应用与性能优化(全)》

先看前一篇。

根据书里的前言里面,对于阅读顺序的建议。先看最后的Scala实践三部曲吧。

 scala学习,我觉得这一段写的很好:

object Hello{
  def main(args: Array[String]): Unit = {
    val ret = sum(x=> x*x)(1)(2)
    println(ret)
  }

  def sum(f: Int => Int)(a: Int)(b: Int): Int =
      if (a > b) 0 else f(a) + sum(f)(a+1)(b)


}

能够看出,上面是求出从a加到b的平方和。很巧妙。

Scala中有两点需要注意

1. 函数体的最后一行的值就是整个函数的返回值;

2. 类型的声明位于变量、函数或者类的后面。

当函数不带参数时候,我们调用的时候,可以不加括号。

函数还可以这样定义:

def add = (x:Int, y:Int)=>x+y

要注意一下Scala的柯里化,currying,允许函数定义时候有多个括号,每个括号里面一个参数。在 Haskell 中,所有的函数都是柯里化的.

科里化(柯里化)这种现象是随着函数被当做一等公民自然而然地产生的。不然高阶函数会很麻烦。

注意Java和Python里面,都有可变参数的,可以看我的这篇文章:http://www.cnblogs.com/charlesblc/p/6226667.html

Scala里面也有可变函数

如下:

def abc(s: String*) = {

    s.foreach(x=>println(x))

}

然后就可以调用了

abc("I", "love", "you")

默认参数是这样的:

def abc(name :String = "default") : String = {

...

}

for循环

查看我的这篇文章:http://www.cnblogs.com/charlesblc/p/6065424.html

Scala里面的面向对象

面向对象几点:

抽象和封装,继承,多态(多态也称为一个名字,多种方法)

注意 Scala里面的下划线,用的非常多,有个名字叫作Placeholder,下面这篇文章介绍了十几种用法

https://my.oschina.net/leejun2005/blog/405305

没怎么看懂,慢慢领会。

文中用到了其中的第11中用法:

11、初始化默认值:default value var i: Int = _

另外,还有private[this]的使用:

class A{

private[this] val gender = "male"

}

那么在外面访问 val a = new A

a.gender 就会报错

看看主构造器的用法。有如下特点:

1. 主构造器直接跟在类名后面,参数会被编译成类的字段。

2. 执行时会执行类中的所有不包含在方法体中的语句。

class Person(val name: String, val age: Int) {

  println("this is constructor!")

}

运行 val p = new Person("Rocky", 27) 会打印语句。

注意:

class Person(name: String, val age: Int) {

}

这样的话,运行会报错,找不到name,说明没有用val或者var加载主构造器函数的话,那么这个变量是 private[this]的,只能内部访问。(那类岂不是不能初始化?)

附属构造器

1) 附属构造器是通过this来声明的

2) 附属构造器必须调用主构造器或者其他附属构造器。

class Person(var name : String, val age: Int) {

  var gender : String = _

  def this(name: String, age: Int, gender: String) {

    this(name, age)

    this.gender = gender

  }

}

继承

Scala继承用 extends来进行, 覆盖用 override来处理。

抽象类

abstract class A {

  def speak

}

另外

object AAAClass extends App {

  val worker = new Worker

  worker.speak

}

App是trait的子类,内部帮助我们实现了main方法。

Scala的trait

trait支持部分实现,也就是说可以在scala的trait中可以实现部分方法。

trait可以有实现的方法,也可以有抽象方法。使用trait的方式是with而混入类中。不懂。

子trait可以覆盖父trait中的方法,如果父trait中已经实现了方法,子trait就必须用override关键字。

如果既要继承类,又要继承trait,可以用 with关键字,如下:

class MyAccount extends Account with FLogger {

  def save {

    log("10000")

  }

}

其中 log 是定义在trait Flogger里面的函数。

然后在定义的时候,要这样写:

val acc = new MyAccount with MessageLogger

acc.save

上面的MessageLogger是实现了 Flogger 的trait,如下:

trait MessageLogger extends Flogger {

  override def log(msg: String) {

  }

}

另外,放在object里面的方法都是static方法,直接调用:

object abc {

  def func{

  }

}

abc.func

apply在object和class里面的应用

object里面可以定义apply

class A {

  def test() {

    println("test")

  }

}

object A {

  def apply() = new A

}

val a = A()

a.test

上面的 A() 直接调用 object A里面的apply(),返回了 class A,所以最后打印 "test"

class里面也可以定义apply

class A {

  def apply() = "hi"

}

val a = new A

println(a())

上面的 () 调用了 apply(),所以打印了"hi"

因为object里面的方法和属性都是static的,所以用来实现单例,很方便。

object A {

  def apply() = new A

  var count = 0

  def incr = {

    count = count + 1

  }

}

用法:

for (i <- 1 to 10) {

  A.incr

}

println(A.count)

打印了10

总结:object本身就是一个单例对象!!!

 

Scala函数式编程

P773

函数式编程的核心特点之一,就是把函数作为参数传给函数、在函数内部可以定义函数等。

1. 函数式编程定义:其实是方法论(programming paradigm)

5个特点:

1. 函数是第一等公民

2. 总用表达式 expression,不用语句statement,(意思是总有返回值)

3. 没有副作用 (避免全局变量)

4. 不修改状态(只返回新的值,不修改系统变量)

5. 引用透明(运行只依赖于输入的参数)

好处:

1. 代码简洁,开发迅速; 2. 接近自然语言; 3. 方便的代码管理(不依赖外界状态)4. 并发编程方便 5. 易于热升级(只要接口不变,内部实现外部无关,erlang就是为了不关机升级)

Scala的函数形式:

def func(var : type) : retType = {}

返回类型,有时候可以直接推断出;但是写出来更好。如果是递归的,那么返回类型必须明确写出来。

如果函数体只是一句,也可以不加花括号。

Unit是返回类型,指的是没有有效的返回值,有点类似于Java的void。Java中返回void的方法,会被映射成Scala返回Unit的方法。

值函数

值函数指的就是将一个函数赋值给一个变量进行保存,这时候变量就变成了一个函数,用的时候跟函数一样用就可以了。

def add(x:Int, y:Int):Int = (x+y)

var result = add _   (注意,把函数赋值给变量的时候,必须在后面加上空格和_)

result(1, 2) 得到3

匿名函数

(x:Int) => x + 3

可以赋值给一个常量:  val fun = (x: Int) => x + 3

这就相当于  def fun(x: Int) = x + 3

调用是这样的 fun(7)

主要用途是作为参数传递,比如:

scala.collection.mutable.ArrayBuffer(1,2,3,4).map((x:Int)=>x+3)

Scala中的闭包

闭包 = 代码 + 用到的非局部变量

var y = 1

val sum = (x : Int) => x + y

sum(15)

这时候y是外部变量。

Scala中的SAM

Java中有些接口只有单个抽象方法(Single Abstract Method),在Java里面被称为 SAM类型。

为了在传入Java ActionListener对象的地方,传入 (ActionEvent)=>Unit函数参数,需要加一个隐式转换。

Scala中的Curry

柯里化,指的就是都变成一个参数的函数,新的函数返回一个以原有第二个参数为参数的函数。

def multi(x:Int) = (y: Int) => x * y

multi(6)(7)

柯里化可以简写成:

def multi(x:Int)(y:Int) = x * y

控制抽象 + 换名调用参数

可以组成不带参数也没有返回值的函数:

def runInThread(block: ()=>Unit) {

  new Thread {

    override def run() {block()}

  }.start()

}

注意:如果方法的返回类型为Unit,则可以忽略result type 和 = 号。

runInThread{ ()=> println("Hi"); Thread.sleep(10000); println("Bye")}

可以去掉()=>,

def runInThread(block: =>Unit) {

  new Thread {

    override def run() {block}

  }.start()

}

runInThread {println("Hi"); Thread.sleep(10000); println("Bye")}

Scala程序员可以构建控制抽象,看起来就像是关键字:

def until(condition: => Boolean) (block: =>Unit) {

  if (!condition) {

    block

    until(condition)(block)

  }

}

// 使用

var x = 10

until(x == 0) {

  x -= 1

  println(x)

}

这样的函数就叫做换名调用函数(常规的参数叫作换值调用参数)。函数在调用的时候,换名调用参数的表达式不会求值,表达式会被当做参数传递下去。

return表达式

P783 略

 

高阶函数

函数作为参数或作为返回值的函数称为 高阶函数

val a = List(1,2,3)

这里能够直接使用List实例化对象,其实是用了List的object对象的apply方法。

val newList = l.map((x:Int)=>2*x)

类型一样的话可以省略类型   l.map((x)=>2*x)

只有一个参数,可以省略括号  l.map(x=>2*x)

只有一个参数,可以继续省略  l.map(_*2)

常见的高阶函数有 map, filter, reduce

1. map

array.map(1 + _) 其中的 _代表列表里面的每一个元素

2. filter

array.filter( _ > 33) 大于33的

3. reduce 

array.reduce(_ - _) 是第一个减去第二个,然后结果再减去第三个

Scala中的集合

主要有 List, Set, Tuple, Map等

关于 Array, List, Tuple的区别,可以看这篇文章:

https://my.oschina.net/u/1034176/blog/512314

在Scala 2.7中,Array、List都不能混合类型,只有Tuple可以;而在Scala以上版本中,3者的元素都可以混合不同的类型(转化为Any类型),只不过是当使用混合类型时,Array和List会将元素类型转化为Any类型,而Tuple则保留每一个元素的初始类型;

    1. 关于初始化

      1) Array:val array= new Array[String](3) // Array(null, null, null)相当于声明了3个null值的空元素 

    2. val array= Array("a","b","c","d") //  相当于 Array.apply("a","b","c","d") 

   定义一个类型为Any的Array:

    val aa = Array[Any](1, 2)或:val aa: Array[Any] = Array(1, 2)或:val aa: Array[_] = Array(1, 2)

2) List:

val list:List[Int] = List(1,3,4,5,6) // 或者 val list = List(1,3,4,5,6)

       (:::)实现叠加List,(::)cons:将新元素组合到列表的最前端。元素合并使用::,集合合并使用:::,示例如下:其中Nil代表空元素

val list2 = "a"::"b"::"c"::Nil // Nil

    val list4 = list2:::list3

3) Tuple:

元组也是不可变的,但是元组可以是不同类型的数据,实例化:var a = (,),可以通过点号,下划线,-N(N从1开始)的索引访问元素

对Tuple而言,如果只有两个元素,还可以通过下面的方式创建:

"a" -> "b"

得到:res7: (String, String) = (a, b)

Map类型

 Map("a"->"b", "c"->"d")

Option类型

Option代表一个可有可无的值。

Option有两个子类:Some 和 None . 下面我们看一下Option的使用。

Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None 。

优点大概是让有值和无值的操作变得统一吧。

filter的处理

 下面两个是等价的:

l.filter(x=>x %2 ==0)

l.filter(x%2 == 0)

看一下zip的操作

 

partition的操作 

 flatten 和 flatMap 的操作

Scala中的泛型

p790

在Scala中用 [] 来代替Java中的 <> 来表现类型参数表。

 

Scala中的隐式转换、隐式参数、隐式类

P784

泛型和隐式转换都看不懂。以后再看吧。

回到第13页,第一章,开始看起。

Spark术语

Spark的容错主要是 Lineage机制。 分布式数据容错主要方式有:数据检查点,和记录数据的更新。 Spark是粗粒度的记录数据的更新,只记录数据怎么从其他RDD转换而来。

 

 RDD依赖关系

Stage DAG

通常Shuffle是Stage的边界

看到第36页。

持久化与persist

通常每次运行会重新计算,如果不想重复计算,可以用 RDD.persist().  会存储在内存里(或者磁盘?)

不是必须,不应该持久化,因为浪费空间。

如果多次需要结果,可以持久化,如下:

right.persist()

right.first()

right.count()

另外关于 cache(),查了一下,就是全内存化的persist(),是用persist()实现的,即 persist(StorageLevel.MEMORY_ONLY)

创建RDD

Spark提供了两种创建RDD的方式:加载外部数据集,和在驱动程序中平行化集合。

分别是:

val lines = spark.textFile("hdfs://master:9000/input/in.txt")

val lines = spark.parallelize(List("pandas", "i like pandas"))

RDD操作

P38 下面这段讲的非常好:

转换和动作的示例

用take()可以检索一个小数目的结果。

还有一个collect()会输出全部结果。但是量比较大。可以使用 saveAsTextFile() 或者 saveAsSequenceFile()来存储到HDFS上。

惰性评估(Lazy Evaluation)

比如 sc.textFile(),数据没有被加载,只有到动作需要执行时候,才会真正加载。 

Spark子框架解析

P30 

Spark GraphX 看了一会 不懂。

Spark Stream

是按照时间节点,比如2秒,分成一段一段的,作为Dstream(Discretized Stream),然后这一段数据转换成RDD,那么Spark Streaming对于Dstream的操作就转换成了 Spark对于RDD的操作。

流程图如下:

 

Spark Streaming编程模型

P55 主要是这一句:

val wordCount = words.map(x=>(x,1)).reduceByKeyAndWindow(_+_, Seconds(5s), seconds(1))

P57

Kafka 和 Spark Streaming结合。

Spark SQL

P58

Spark MLlib

P61

上面提到的L1和L2,在下面这篇文章讲得很好:

http://blog.csdn.net/jinping_shi/article/details/52433975

L1正则和L2正则,其实都是加在损失函数后面的一个额外项。L1正则化和L2正则化可以看做是损失函数的惩罚项。

对于线性回归模型,使用L1正则化的模型建叫做Lasso回归,使用L2正则化的模型叫做Ridge回归(岭回归)。下图是Python中Lasso回归的损失函数,式中加号后面一项α||w||1即为L1正则化项。

下图是Python中Ridge回归的损失函数,式中加号后面一项α||w||22即为L2正则化项。

一般回归分析中回归w表示特征的系数,从上式可以看到正则化项是对系数做了处理。L1正则化和L2正则化的说明如下:

  • L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为||w||1
  • L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为||w||2

一般都会在正则化项之前添加一个系数,Python中用α表示,一些文章也用λ表示。这个系数需要用户指定。

那添加L1和L2正则化有什么用?下面是L1正则化和L2正则化的作用,这些表述可以在很多文章中找到。

  • L1正则化可以产生稀疏权值矩阵,即产生一个稀疏模型,因此可以用于特征选择
  • L2正则化可以防止模型过拟合(overfitting);一定程度上,L1也可以防止过拟合

原因和解释,可以看上面那篇文章的原文。不细说了。

聚类

聚类是一种非监督学习。聚类常被用于探索性分析,或者作为层次化监督学习的一部分(聚类之后再对不同的类簇采用不同的分类器或者回归模型)。

MLLib 实现了 kmeans.

协同过滤

注意显性反馈与隐性反馈。

目前Spark里面可用的算法:

ALS

基础算法-梯度下降算法

P63

二元分类 线性回归 聚类 协同过滤ALS 例子

 P64

第四章 Spark RDD与编程API实战

P171

 

P193

通过 toDebugString 函数可以查看 lineage信息。

P195

有一个实战搜狗日志的例子,不知道是不是跟之前的实战例子类似。

P198

实例,按条件搜索:  

实例,排序,按照val排序的方式

P206

Spark支持的Transformation操作

Spark支持的Action操作

P219

Spark运行的主要流程,包括Master,Driver,Worker,DAGScheduler各自参与的工作。

P259

6.1 Spark内核核心术语

Application 等术语的解释和描述。

看到P299 GraphX 图运算 先跳过不看

先看P431里面的 Spark MLLib吧

还有 P665的第14章 性能调优 可以看,锦上添花那种。

MLLib实际对应 P443

P444介绍了机器学习的基本概念

讲了各种机器学习算法。

P455 介绍了一个基于Spark MLLib的SVM的实例

代码非常简洁明了。

P474 

MLLib经典算法案例解析(重点看线性回归、协同过滤

原文地址:https://www.cnblogs.com/charlesblc/p/6226254.html