TF-idf 思想及其Java 实现

  信息时代,大数据成为一切操作的基石,即使是人工智能及深度学习相关领域也离不开数据的支撑。因此文本挖掘、图像处理等基础技术愈加重要,本文将着重介绍文本挖掘中的加权算法——TF-idf及其Java实现。

概况简介

  TF-idf常被作为一种统计算法对分词结果进行加权排序,以得出文本中重要的一些词汇,因此可以视作分类算法或评级算法。显然从其名字我们可以轻松地知道,该算法由两部分组成,TF及idf。
  TF(Term Frequency,词频)指的是在一段文本中,某一词汇出现的频率。idf(inverse document frequency,逆文本频率)指的是许多文件中某一词汇的重要程度。你可以前往维基中国查看更多详细的内容。

实现

  网上不乏该算法的实现,但均乏善可陈且本人钟情于Java,故开发了Java实现。

语料分词

  作者采用优秀的国产开源分词软件HanLP进行分词,对它有兴趣的同学可自行前往其Github主页学习。

1
NotionalTokenizer.segment(corpusText.toString());

TF值求取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 对单词表进行TF求取
*
* @param wordList 单词表
* @return 词频Map
*/
private static Map<String, Integer> countTFByWordList(List<String> wordList) {
Map<String, Integer> tfMap = new HashMap<>();
for (String word : wordList) {
if (tfMap.containsKey(word)) {
tfMap.put(word, tfMap.get(word) + 1);
} else {
tfMap.put(word, 1);
}
}
return tfMap;
}

/**
* 对文本进行分词后求取TF值
*
* @param textList 文本List
* @param hasNature 词频是否包含词性
* @return 词频Map
*/
public static Map<String, Integer> countTFByText(List<String> textList, boolean hasNature) {
StringBuilder textBuilder = new StringBuilder();
for (String text : textList) {
textBuilder.append(text).append("\n");
}
List<String> wordList = NotionalTokenizer.segment(textBuilder.toString())
.stream()
//词性过滤,如不需要过滤词性则不需要此filter
// .filter(term -> term.nature.startsWith("n")
// || term.nature.startsWith("rr")
// || term.nature.startsWith("v"))
.map(term -> term.word + ((hasNature) ? "\t" + term.nature : ""))
.collect(Collectors.toList());
return countTFByWordList(wordList);
}

idf值求取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
**
* 计算某一词汇的IDF值
*
* @param textList 文本List
* @param word 词汇
* @return 该词汇的IDF值(Double类型)
*/
private static Double calculateIDFByWord(List<String> textList, String word) {
int count = 0;
for (String text : textList) {
if (text.contains(word.split("\\s+")[0])) {
count++;
}
}
return Math.log10(textList.size() / (double) (count + 1));
}

public static String countIDF(List<String> textList, boolean hasNature) {
StringBuilder tfResult = new StringBuilder(), tf_idfResult = new StringBuilder(), allResult = new StringBuilder();
Map<String, Double> idfMap = new HashMap<>();
Map<String, Integer> tf_idfMap = countTFByText(textList, hasNature);

List<Map.Entry<String, Integer>> tfArrayList = new ArrayList<>(tf_idfMap.entrySet());
tfArrayList.sort(Comparator.comparing(Map.Entry::getValue));
for (Map.Entry<String, Integer> entry : tfArrayList) {
idfMap.put(entry.getKey(), entry.getValue() * calculateIDFByWord(textList, entry.getKey()));
tfResult.append(entry.getKey()).append("\t").append(entry.getValue()).append("\n");
}
List<Map.Entry<String, Double>> idfArrayList = new ArrayList<>(idfMap.entrySet());
idfArrayList.sort(Comparator.comparing(Map.Entry::getValue));
for (Map.Entry<String, Double> entry : idfArrayList) {
tf_idfResult.append(entry.getKey()).append("\t").append(entry.getValue()).append("\n");
}

allResult.append("--------------TF-IDF Result --------------\n");
allResult.append(tf_idfResult);
// allResult.append("--------------TF Result--------------\n");
// allResult.append(tfResult);

return allResult.toString();
}

测试

  我将使用纳兰性德的190+首诗词作品作为语料来源,并将其TF值及TF-idf结果分别输出。如果你需要类似的诗词语料,可以考虑使用我最近的开源工具进行爬取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class TF_IDFUtilsTests {
private List<String> fileList;

@Before
public void initialDate() throws IOException {
String route = Resources.getResource("text_mining/poems").getPath(), result = "";
List<File> files = FilesUtil.getAllFiles(route, 2);
fileList = new ArrayList<>();
for (File file : files) {
fileList.add(FileUtils.readFileToString(file, "UTF-8"));
}
}

@Test
public void countTF_IDF() {
// HanLP.Config.enableDebug();
String result = TF_IDFUtils.countIDF(fileList, true);
System.out.println(result);
}

@Test
public void countTF() {
StringBuilder result = new StringBuilder();
Map<String, Integer> tfMap = TF_IDFUtils.countTFByText(fileList, true);
List<Map.Entry<String, Integer>> tfArrayList = new ArrayList<>(tfMap.entrySet());
tfArrayList.sort(Comparator.comparing(Map.Entry::getValue));

result.append("--------------TF Result--------------\n");
for (Map.Entry<String, Integer> entry : tfArrayList) {
result.append(entry.getKey()).append("\t").append(entry.getValue()).append("\n");
}
System.out.println(result.toString());
}
}

结果展示:
TF Result
Tf-idf Result

其他

Yodes Yang wechat
扫描二维码与我联系

------ 本文结束 ------
  • 本文作者: Yodes Yang
  • 文章标题: TF-idf 思想及其Java 实现
  • 发布时间: 2018年3月30日 - 10时03分
  • 本文链接: http://blog.yodes.cn/post/631f.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 许可协议。转载请注明出处!
扫二维码
扫一扫,用手机访问本站

扫一扫,用手机访问本站