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

这段代码是使用 PySpark 实现 TF-IDF 特征提取,对文本进行分类。下面是对每行代码的详细解释: 1. `hashingTF = HashingTF()`:创建一个 HashingTF 对象,该对象将文本转换为 Term Frequency(TF)向量。 2. `idf = IDF()`:创建一个 IDF 对象,该对象用于计算逆文档频率(IDF)。 3. `data = sc.wholeTextFiles('hdfs://spark01:9000/project/data/*/*').map(lambda x: (x[0], ''.join(x[1].split())))`:读取数据集,使用 `wholeTextFiles` 方法读取指定目录下所有文件,返回 (filename, content) 的元组。将每个文件内容中的空白字符去除,并将结果作为元组中的第二个元素。 4. `.map(lambda x: (x[0].split('/')[-2], x[1]))`:将文件路径中的类别提取出来,作为元组的第一个元素。 5. `.map(lambda x: (x[0], [w for w in jieba.cut(x[1]) if w not in stopwords]))`:使用 jieba 分词对每个文件进行分词处理,并去除停用词。结果为 (类别,分词列表) 的元组。 6. `tf = hashingTF.transform(data.map(lambda x: x[1]))`:使用 HashingTF 将分词列表转换为 TF 向量。 7. `idfModel = idf.fit(tf)`:使用 IDF 对象拟合 TF 向量,计算出每个词的 IDF 值。 8. `tfidf = idfModel.transform(tf).zip(data.map(lambda x: x[0])).map(lambda x: LabeledPoint(label_mapping.get(x[1], default_label), x[0]))`:将 TF 向量和类别信息进行 zip 操作,将结果转换为 LabeledPoint 对象。其中,`label_mapping` 是一个字典,用于将类别名称映射为数字标签;`default_label` 是一个默认标签,用于处理没有匹配到的类别。 以上就是这段 PySpark 代码的详细解释。整个流程包括读取数据、分词、计算 TF-IDF 值等步骤。最后将结果转换为 LabeledPoint 对象,用于训练分类模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值