在Spark1.2之后,Spark自带实现TF-IDF接口,只要直接调用就可以,但实际上,Spark自带的词典大小设置较于古板,如果设置小了,则导致无法计算,如果设置大了,Driver端回收数据的时候,容易发生OOM,所以更多时候都是自己根据实际情况手动实现TF-IDF。不过,在本篇文章中,两种方式都会介绍。
数据准备:
val df = ss.sql("select * from bigdatas.news_seg")
//如果hive表的数据没有切词,则先对数据进行切词操作(hive里面每一行是用空格将各个词连接的字符串,或者说是一篇文章,结尾使用##@@##标识),得到一个数组类型数据
val df_seg = df.selectExpr("split(split(sentence,'##@@##')[0],' ') as seg")
一、Spark自带TF-IDF
1、Spark自带TF实现
首先需要实例化HashingTF,这个类用于根据给传入的各篇已经分好词的文章,对里面的每个词进行hashing计算,每个hashing值对应词表的一个位置,以及对每个词在每篇文章中的一个统计;
这个类有一个方法setBinary()可以设置其统计时的计算方式:多项式分布计算和伯努利分布计算:
- setBinary(false):多项式分布计算,一个词在一篇文章中出现多少次,计算多少次;
- setBinary(true):伯努利分布计算,一个词在一篇文章中,不管多少次,只要出现了,就为1,否则为0
还有一个重要方法setNumFeatures(),用于设置词表的大小,默认是2^18。
实例化HashingTF之后,使用transform就可以计算词频(TF)。
TF代码实现:
// 多项式分布计算
val hashingTF = new HashingTF()
.setBinary(false)
.setInputCol("seg")
.setOutputCol("feature_tf")
.setNumFeatures(1<<18)
// 伯努利分布计算
val hashingTF_BN = new HashingTF()
.setBinary(true)
.setInputCol("seg")
.setOutputCol("feature_tf")
.setNumFeatures(1<<18)
/**
* hashingTF.transform(df_seg):转换之后会在原来基础上增加一列,就是setOutputCol("feature_tf")设置的列
* 新增列的数据结构为:(词表大小,[该行数据的每个词对应词表的hashCode],[该行数据的每个词在该行数据出现的次数,即多项式统计词频])
*/
val df_tf = hashingTF.transform(df_seg