基於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()