Hanlp分词实例:Java实现TFIDF算法

简介:


算法介绍

最近要做领域概念的提取,TFIDF作为一个很经典的算法可以作为其中的一步处理。

关于TFIDF算法的介绍可以参考这篇博客http://www.ruanyifeng.com/blog/2013/03/tf-idf.html

计算公式比较简单,如下:

9770967555d135a232911c78e1aba3be8148e7a3

预处理

由于需要处理的候选词大约后3w+,并且语料文档数有1w+,直接挨个文本遍历的话很耗时,每个词处理时间都要一分钟以上。

为了缩短时间,首先进行分词,一个词输出为一行方便统计,分词工具选择的是HanLp

然后,将一个领域的文档合并到一个文件中,并用“$$$”标识符分割,方便记录文档数。

af8278f2be0a8ba2d042515c80f2183f887009a8

下面是选择的领域语料(PATH目录下):

4e55027808c0827276f709222ef204896d3e9e9f

代码实现

package edu.heu.lawsoutput;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileReader;

import java.io.FileWriter;

import java.util.HashMap;

import java.util.Map;

import java.util.Set;

 

/**

 * @ClassName: TfIdf

 * @Description: TODO

 * @author LJH

 * @date 20171112日 下午3:55:15

 */

 

public class TfIdf {

 

    static final String PATH = "E:\\corpus"; // 语料库路径

 

    public static void main(String[] args) throws Exception {

 

        String test = "离退休人员"; // 要计算的候选词

 

        computeTFIDF(PATH, test);

 

    }

 

    /**

    * @param @param path 语料路经

    * @param @param word 候选词

    * @param @throws Exception

    * @return void

    */

    static void computeTFIDF(String path, String word) throws Exception {

 

        File fileDir = new File(path);

        File[] files = fileDir.listFiles();

 

        // 每个领域出现候选词的文档数

        Map<String, Integer> containsKeyMap = new HashMap<>();

        // 每个领域的总文档数

        Map<String, Integer> totalDocMap = new HashMap<>();

        // TF = 候选词出现次数/总词数

        Map<String, Double> tfMap = new HashMap<>();

 

        // scan files

        for (File f : files) {

 

            // 候选词词频

            double termFrequency = 0;

            // 文本总词数

            double totalTerm = 0;

            // 包含候选词的文档数

            int containsKeyDoc = 0;

            // 词频文档计数

            int totalCount = 0;

            int fileCount = 0;

            // 标记文件中是否出现候选词

            boolean flag = false;

 

            FileReader fr = new FileReader(f);

            BufferedReader br = new BufferedReader(fr);

            String s = "";

 

            // 计算词频和总词数

            while ((s = br.readLine()) != null) {

                if (s.equals(word)) {

                    termFrequency++;

                    flag = true;

                }

 

                // 文件标识符

                if (s.equals("$$$")) {

                    if (flag) {

                        containsKeyDoc++;

                    }

                    fileCount++;

                    flag = false;

                }

                totalCount++;

            }

 

            // 减去文件标识符的数量得到总词数

            totalTerm += totalCount - fileCount;

            br.close();

            // key都为领域的名字

            containsKeyMap.put(f.getName(), containsKeyDoc);

            totalDocMap.put(f.getName(), fileCount);

            tfMap.put(f.getName(), (double) termFrequency / totalTerm);

 

            System.out.println("----------" + f.getName() + "----------");

            System.out.println("该领域文档数:" + fileCount);

            System.out.println("候选词出现词数:" + termFrequency);

            System.out.println("总词数:" + totalTerm);

            System.out.println("出现候选词文档总数:" + containsKeyDoc);

            System.out.println();

        }

        

        //计算TF*IDF

        for (File f : files) {

 

            // 其他领域包含候选词文档数

            int otherContainsKeyDoc = 0;

            // 其他领域文档总数

            int otherTotalDoc = 0;

 

            double idf = 0;

            double tfidf = 0;

            System.out.println("~~~~~" + f.getName() + "~~~~~");

 

            Set<Map.Entry<String, Integer>> containsKeyset = containsKeyMap.entrySet();

            Set<Map.Entry<String, Integer>> totalDocset = totalDocMap.entrySet();

            Set<Map.Entry<String, Double>> tfSet = tfMap.entrySet();

 

            // 计算其他领域包含候选词文档数

            for (Map.Entry<String, Integer> entry : containsKeyset) {

                if (!entry.getKey().equals(f.getName())) {

                    otherContainsKeyDoc += entry.getValue();

                }

            }

 

            // 计算其他领域文档总数

            for (Map.Entry<String, Integer> entry : totalDocset) {

                if (!entry.getKey().equals(f.getName())) {

                    otherTotalDoc += entry.getValue();

                }

            }

 

            // 计算idf

            idf = log((float) otherTotalDoc / (otherContainsKeyDoc + 1), 2);

 

            // 计算tf*idf并输出

            for (Map.Entry<String, Double> entry : tfSet) {

                if (entry.getKey().equals(f.getName())) {

                    tfidf = (double) entry.getValue() * idf;

                    System.out.println("tfidf:" + tfidf);

                }

            }

        }

    }

 

    static float log(float value, float base) {

        return (float) (Math.log(value) / Math.log(base));

    }

}

 

运行结果

测试词为离退休人员,中间结果如下:

501705bbea89b5e1b5abf299b10625f496616572

最终结果:

7048de9fe37e3c132e72d9550c87c2cf70a0037c

结论

可以看到离退休人员在养老保险和社保领域,tfidf值比较高,可以作为判断是否为领域概念的一个依据。当然TF-IDF算法虽然很经典,但还是有许多不足,不能单独依赖其结果做出判断。很多论文提出了改进方法,本文只是实现了最基本的算法。如果有其他思路和想法欢迎讨论。

 

文章转载自  没课割绿地 的博客

 

相关文章
|
17天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
50 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
27天前
|
Java
Java关键字 —— super 详细解释!一看就懂 有代码实例运行!
文章详细解释了Java关键字`super`的用途,包括访问父类的成员变量、调用父类的构造方法和方法,并提供了相应的代码实例。
70 5
Java关键字 —— super 详细解释!一看就懂 有代码实例运行!
|
6天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
18天前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
66 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
27天前
|
存储 Java 数据安全/隐私保护
Java中的域,什么是域?计算机语言中的域是什么?(有代码实例)
文章解释了Java中域的概念,包括实例域、静态域、常量域和局部域,以及它们的特点和使用场景。
31 2
|
2月前
|
Java
Java——接口的使用实例
Comparable接口用于自定义类的对象比较。通过实现此接口并重写`compareTo`方法,可以定义自定义类型的比较规则。 接下来介绍了Comparator接口,它提供了一种更灵活的比较方式。通过实现Comparator接口并重写`compare`方法,可以根据不同属性定义不同的比较规则。例如,定义一个`BrandComparator`类来比较汽车的品牌。 最后,介绍了Cloneable接口,用于实现对象的克隆。实现该接口并重写`clone`方法后,可以创建对象的浅拷贝或深拷贝。浅拷贝仅复制对象本身,深拷贝则会递归复制所有成员变量。
30 4
Java——接口的使用实例
|
27天前
|
Java
Java关键字 —— super 与 this 详细解释!一看就懂 有代码实例运行!
本文介绍了Java中this和super关键字的用法,包括在构造方法中使用this来区分参数和成员变量、使用super调用父类构造方法和方法,以及它们在同一个方法中同时使用的场景。
92 0
Java关键字 —— super 与 this 详细解释!一看就懂 有代码实例运行!
|
27天前
|
Java
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
这篇文章详细解释了Java中static和final关键字的用法,包括它们修饰类、方法、变量和代码块时的行为,并通过代码示例展示了它们的具体应用。
115 0
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
|
2月前
|
存储 Java
Java之静态(static)与实例(instance)
Java之静态(static)与实例(instance)
|
18天前
|
算法 Java Linux
java制作海报一:java使用Graphics2D 在图片上写字,文字换行算法详解
这篇文章介绍了如何在Java中使用Graphics2D在图片上绘制文字,并实现自动换行的功能。
48 0