Spark MLlib 入門學習筆記 - RDD基礎

RDD(Resilient Distributed Datasets)分佈式彈性數據集,將數據分佈存儲在不同節點的計算機內存中進行存儲和處理。RDD的任務被分成兩部分:Transformation和Action。Transformation用於對RDD的創建,即一個RDD轉換爲另一個RDD,Action是數據計算執行部分,如count、reduce、collect等。 Spark文檔裏有相關的說明,網上還有一個Spark文檔的中文翻譯,可以參考。從編程的角度來看,一開始把RDD當成一個數組,並記住它的運行任務由Transformation算子和Action算子共同完成,得到運算結果就好了。

以下是我在學習RDD時完成的一道練習題,通過這道練習,可以掌握RDD編程的基本思路和方法。

對機器學習數據iris.data數據集進行簡單的處理,包括filter、count、distince和分類統計(求和、最大值、最小值和平均值)。

1. 數據說明

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
第一個數據septal length;第二個數據sepal width;第三個數據petal length;第四個數據petal width;第五個數據class標籤;在程序中用name表示,相當於key。

2.源代碼

Iris.scala

package basic.iris

/**
  * Created by Oliver on 2017/5/18.
  */
//1. sepal length in cm
//2. sepal width in cm
//3. petal length in cm
//4. petal width in cm
//5. class:

class Iris extends java.io.Serializable {
  var sl: Double = 0
  var sw: Double = 0
  var pl: Double = 0
  var pw: Double = 0

  var sl_max: Double = 0
  var sw_max: Double = 0
  var pl_max: Double = 0
  var pw_max: Double = 0

  var sl_min: Double = 0
  var sw_min: Double = 0
  var pl_min: Double = 0
  var pw_min: Double = 0

  var name: String =""
  var count: Int = 1

  override def toString : String = {
    var s =  "(" + name + ", "
    s = s + "[ " + "%.1f".format(sl) + ", " + "%.1f".format(sl_min) + ", " + "%.1f".format(sl_max) + ", " + "%.1f".format(sl/count) + " ], "
    s = s + "[ " + "%.1f".format(sw) + ", " + "%.1f".format(sw_min) + ", " + "%.1f".format(sw_max) + ", " + "%.1f".format(sw/count) + " ], "
    s = s + "[ " + "%.1f".format(pl) + ", " + "%.1f".format(pl_min) + ", " + "%.1f".format(pl_max) + ", " + "%.1f".format(pl/count) + " ], "
    s = s + "[ " + "%.1f".format(pw) + ", " + "%.1f".format(pw_min) + ", " + "%.1f".format(pw_max) + ", " + "%.1f".format(pw/count) + " ], "
    s = s + count.toString + ")"

    return s
  }
}
IrisStat.scala

package basic.iris

import org.apache.spark.{SparkConf, SparkContext}

/**
  * Created by Oliver on 2017/5/18.
  */
//local
//E:/MyProject/SparkDiscover/data/iris.data
object IrisStat {

  def isValid(line: String): Boolean = {
    val parts = line.split(",")
    return parts.length == 5
  }

  def parseLine(line: String): (String, Iris) = {
    val parts = line.split(",")
    val iris = new Iris
    iris.sl = parts(0).toDouble
    iris.sw = parts(1).toDouble
    iris.pl = parts(2).toDouble
    iris.pw = parts(3).toDouble

    iris.sl_min = parts(0).toDouble
    iris.sw_min = parts(1).toDouble
    iris.pl_min = parts(2).toDouble
    iris.pw_min = parts(3).toDouble

    iris.sl_max = parts(0).toDouble
    iris.sw_max = parts(1).toDouble
    iris.pl_max = parts(2).toDouble
    iris.pw_max = parts(3).toDouble
    iris.name = parts(4)

    return (iris.name, iris)
  }

  def add(a: Iris, b: Iris): Iris = {
    val c = new Iris
    c.sl = a.sl + b.sl
    c.sw = a.sw + b.sw
    c.pl = a.pl + b.pl
    c.pw = a.pw + b.pw
    c.count = a.count + b.count
    c.name = a.name

    //比較大小
    c.sl_max = math.max(a.sl_max, b.sl_max)
    c.sw_max = math.max(a.sw_max, b.sw_max)
    c.pl_max = math.max(a.pl_max, b.pl_max)
    c.pw_max = math.max(a.pw_max, b.pw_max)

    c.sl_min = math.min(a.sl_min, b.sl_min)
    c.sw_min = math.min(a.sw_min, b.sw_min)
    c.pl_min = math.min(a.pl_min, b.pl_min)
    c.pw_min = math.min(a.pw_min, b.pw_min)

    return c
  }

  def printResult(res: (String, Iris)){
    println(res._2)
  }

  def main(args: Array[String]){
    val conf = new SparkConf().setMaster(args(0)).setAppName("Iris")
    val sc = new SparkContext(conf)
    val data = sc.textFile(args(1)).filter(isValid(_))

    // distinct
    println("---1---------------------------")
    data.map(_.split(",")(4)).distinct().foreach(println)

    // 簡單計數
    val c = data.count()
    val c_setosa = data.filter( "Iris-setosa" == _.split(",")(4) ).count()
    val c_versicolor = data.filter( "Iris-versicolor" == _.split(",")(4) ).count()
    val c_virginica = data.filter( "Iris-virginica" == _.split(",")(4) ).count()

    println("")
    println("---2---------------------------")
    println(c, c_setosa, c_versicolor, c_virginica)


    // mapreduce 分組求和、求平均、求最大最小
    //data.map(parseLine(_)).foreach(println)
    println("")
    println("---3---------------------------")
    data.map(parseLine(_)).reduceByKey(add(_,_)).collect().foreach(printResult)

  }
}

3.運行配置


4. 運行結果

---1---------------------------
Iris-setosa
Iris-versicolor
Iris-virginica

---2---------------------------
(150,50,50,50)

---3---------------------------
(Iris-setosa, [ 250.3, 4.3, 5.8, 5.0 ], [ 170.9, 2.3, 4.4, 3.4 ], [ 73.2, 1.0, 1.9, 1.5 ], [ 12.2, 0.1, 0.6, 0.2 ], 50)
(Iris-versicolor, [ 296.8, 4.9, 7.0, 5.9 ], [ 138.5, 2.0, 3.4, 2.8 ], [ 213.0, 3.0, 5.1, 4.3 ], [ 66.3, 1.0, 1.8, 1.3 ], 50)
(Iris-virginica, [ 329.4, 4.9, 7.9, 6.6 ], [ 148.7, 2.2, 3.8, 3.0 ], [ 277.6, 4.5, 6.9, 5.6 ], [ 101.3, 1.4, 2.5, 2.0 ], 50)
5.代碼說明

1)Iris.scala

Iris類存放數據文件的5個字段,當count爲1是單個對象的數據,即數據文件的一行。當count大於1時,表示對多行數據的對應字段進行求和運算後的結果,count行計數,_min和_max字段存放的是對應字段的最小值和最大值。重載了toString輸出結果。

2)IrisStat.scala 

isValid用於判斷數據是否是有效數據,無效則拋棄,完成一個RDD轉換,相當於數據清洗。

parseLine解析數據,將字符串轉換爲對象,方便後續處理。

add執行運算,包括求和、計數、最大值和最小值,平均值在輸出是用“和”除以“計數”就得到了。

main函數說明:

(1) data = sc.textFile(args(1)).filter(isValid(_)) 過濾無效數據,如果不過濾,只要數據集中有一行錯誤數據,程序就會出錯,如混入一個空行。

(2) distinct ,map是用split得到第5個字段,然後對第5個字段應用distince就可以了。

(3) filter,輸入過濾條件就可以了,然後在調用count計數。

(4)分組求和、最小值和最大值,調研map傳人parseLine函數轉換得到一個新數據集,這個數據集是(String, Iris)的<key, value>形式,再調用reduceByKey就實現了分組操作;在reduceByKey中傳入add執行所定義的運行,調用collect返回數據集,用foreach打印結果。 

(5)注意下劃線,理解爲要傳入的數據就好了,如filter(isValid(_))中,filter把上一個map的數據通過_傳給isValid。

發佈了96 篇原創文章 · 獲贊 26 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章