zoukankan      html  css  js  c++  java
  • 【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经典算法案例解析(重点看线性回归、协同过滤

  • 相关阅读:
    LightOj1054
    LightOj1028
    Docker仓库(转载)
    Dockerfile(转载)
    Docker存储卷(转载)
    容器虚拟化网络和Docker容器网络(转载)
    Docker镜像管理基础(转载)
    Docker基础用法(转载)
    docker容器技术基础入门(转载)
    Redis 3种安装部署方式
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6226254.html
Copyright © 2011-2022 走看看