Spark:基於jieba分詞的特徵向量提取

基於jieba分詞的對計算機課程名的特徵向量提取

首先引入包:

import org.apache.spark.sql.{DataFrame, SparkSession}//spark入口,DataFrame操作需要用到的包
import java.nio.file.{Path, Paths}//加入自定義詞庫時路徑需要的包

import com.huaban.analysis.jieba.{JiebaSegmenter, WordDictionary}//jieba分詞需要用到的包
import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer}//特徵向量提取需要用到的包

import scala.collection.mutable//java.util.List轉換成scala Aarray需要用到的包

連接數據庫得到DataFrame:

    // 創建Spark入口
    val spark = SparkSession.builder().getOrCreate()

    /*這一行很關鍵!引入scala Encoder,使得scala擁有自動的RDD / DataFrame類型轉換能力。*/
    /*後面的map操作需要用到。若不引入,則會拋出Encoder缺失異常*/
    // 關鍵!
    import spark.implicits._

    // 連接本項目測試數據庫
    val jdbcDF = spark.read.format("jdbc")
      .option("url", "xxx")//爲隱私就用xxx代替了
      .option("driver", "com.mysql.jdbc.Driver")
      .option("dbtable", "class_name")
      .option("user", "rec")
      .option("password", "cuclabcxg")
      .load()

使用DataFrame的select()取列操作和filter()的取行操作,來取出我們要提取特徵向量的DataFrame格式的數據。

//jieba加入自定義詞庫
    //val path = Paths.get("/home/maples/lab/sogou.txt")
    //WordDictionary.getInstance().loadUserDict(path)
    //val jieba = new JiebaSegmenter()
    //val res:String = jieba.sentenceProcess(text).toString()
    //特徵提取
    //id - 分詞 二維表
    val id_word :DataFrame= jdbcDF.select("id","name")
      .filter(jdbcDF("discipline")==="計算機")
      .map(row =>(row.getInt(0),(new JiebaSegmenter()).sentenceProcess(row.getString(1)):mutable.Buffer[String]))
      .toDF("id","wordVec")
      id_word.show()

【注意這裏】

1、我原先是這樣寫的:

val jieba = new JiebaSegmenter()
val id_word :DataFrame= jdbcDF.select("id","name")
      .filter(jdbcDF("discipline")==="計算機")
      .map(row =>(row.getInt(0),jieba.sentenceProcess(row.getString(1)):mutable.Buffer[String]))
      .toDF("id","wordVec")

我先將JiebaSegmenter()對象在取DataFrame操作外面進行實例化,然後在.map()裏調用jieba的分詞方法。

會報以下錯誤:

Exception in thread "main" org.apache.spark.SparkException: Task not serializable......

大概意思是map()裏的東西沒有序列化,查閱博客後得知,jieba是運行在Driver端的,而map()是運行在Executor端的,兩個操作是在不同地方執行的,所以我把new JiebaSegmenter()的操作搬到了map()裏就ok了。

2、注意這裏的id_word需要聲明它的類型:DataFrame,包括後面新建的DataFrame對象都要聲明它的類型爲DataFrame,否則在調用這些DataFrame對象的時候,會報以下錯誤:

hashingTF.transform cannot resolve method 'transform'......

就是我在對id_word對象計算哈希值的時候,transform報紅,識別不了我的id_word的類型。後面摸索發現需要在前面對這些對象聲明類型(然而《Spark編程基礎》幾乎都沒有聲明類型)。

3、由於我對第二個字段即課程名進行分詞後得到的是一個java.util.List類型,而特徵提取的操作需要這裏是一個scala的Array類型,所以需要引入包:

import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable

並且在分詞結果後面加上 :mutable.Buffer[String]

jieba.sentenceProcess(row.getString(1)):mutable.Buffer[String]

關於java.util.List類型和scala Array之間的互相轉換我找到一篇博客供參考:

https://www.jianshu.com/p/c4f6cbec8363

4、在聲明變量類型後,DataFrame在初始化的時候後面就不能直接加.show()來打印輸出,像這樣:

var df :DataFrame = .......
      .show()

因爲這樣=右邊的返回值就是Unit不是DataFrame,會報錯。

要這樣寫:

var df :DataFrame = .......
df.show()

計算向量的Hash值:

//計算hash值
    val hashingTF = new HashingTF()
      .setInputCol("wordVec")
      .setOutputCol("wordVecHash")
      .setNumFeatures(2000)
    val featureVec = hashingTF.transform(id_word)
      .show()

計算TF-IDF詞袋:

 //計算TF-IDF詞袋
    val idf = new IDF()
      .setInputCol("wordVecHash")
      .setOutputCol("TF-IDF")
    val idfModel = idf.fit(featureVec)
    val TFIDFResult = idfModel.transform(featureVec)
    TFIDFResult.show()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章