TF-IDF算法java实现【转载】

文章原地址:https://blog.csdn.net/lionel_fengj/article/details/53699903(侵权立删)

TF-IDF算法

TF-IDF(词频-逆文档频率)算法是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。该算法在数据挖掘、文本处理和信息检索等领域得到了广泛的应用,如从一篇文章中找到它的关键词。

TFIDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上就是 TF*IDF,其中 TF(Term Frequency),表示词条在文章Document 中出现的频率;IDF(Inverse Document Frequency),其主要思想就是,如果包含某个词 Word的文档越少,则这个词的区分度就越大,也就是 IDF 越大。对于如何获取一篇文章的关键词,我们可以计算这边文章出现的所有名词的 TF-IDF,TF-IDF越大,则说明这个名词对这篇文章的区分度就越高,取 TF-IDF 值较大的几个词,就可以当做这篇文章的关键词。

计算步骤

  1. 计算词频(TF)

    =词频=某个词在文章中的出现次数文章总次数

  2. 计算逆文档频率(IDF)

    =log+1逆文档频率=log⁡语料库的文档总数包含该词的文档数+1

  3. 计算词频-逆文档频率(TF-IDF) 
    =词频−逆文档频率=词频∗逆文档频率

代码示例

为了建立自己的语料库,我从网上爬了100个 url 存放在本地的 txt 文件中, 
存放格式如下: 
这里写图片描述 
这就相当于存放了100篇文章,即语料库的的文档总数是100。

  • TF-IDF 算法代码
package com.myapp.ml.nlp;

import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.ansj.util.FilterModifWord;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by lionel on 16/12/15.
 */
public class TFIDFAlgorithm {
    /**
     * 根据文件路径,文件中存放的100个网址的 url,获取 url 路径列表
     *
     * @param path 本地文件路径
     * @return 路径列表
     */
    public List<String> readUrlFromText(String path) {
        if (StringUtils.isBlank(path)) {
            return null;
        }
        List<String> urls = new ArrayList<String>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(path));
            String line;
            while ((line = reader.readLine()) != null) {
                urls.add(line.trim());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return urls;
    }

    /**
     * 利用 Jsoup 工具,根据网址获取网页文本
     *
     * @param url 网址
     * @return 网页文本
     */
    public String getTextFromUrl(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }

        String text = "";
        try {
            Document document = Jsoup.connect(url).get();
            text = document.text();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return text.replace(" ", "");
    }

    /**
     * 运用 ansj 给文本分词
     *
     * @param text 文本内容
     * @return 分词结果
     */
    public List<Term> parse(String text) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        List<Term> terms = FilterModifWord.modifResult(ToAnalysis.parse(text));
        if (terms == null || terms.size() == 0) {
            return null;
        }
        return terms;
    }

    /**
     * 计算一篇文章分词后除去标点符号后词的总数
     *
     * @param terms 分词后的集合
     * @return 一篇文章分词后除去标点符号后词的总数
     */
    private Integer countWord(List<Term> terms) {
        if (terms == null || terms.size() == 0) {
            return null;
        }
        for (int i = 0; i < terms.size(); i++) {
            if ("null".equals(terms.get(i).getNatureStr()) || terms.get(i).getNatureStr().startsWith("w")) {
                terms.remove(i);
            }
        }
        return terms.size();
    }

    /**
     * 计算词频 IF
     *
     * @param word  词
     * @param terms 分词结果集合
     * @return IF
     */
    public double computeTF(String word, List<Term> terms) {
        if (StringUtils.isBlank(word)) {
            return 0.0;
        }
        int count = 0;
        for (Term term : terms) {
            if (term.getName().equals(word)) {
                count += 1;
            }
        }
        return (double) count / countWord(terms);
    }

    /**
     * 统计词语的逆文档频率 IDF
     *
     * @param path 存放 url 的文件路径
     * @param word IDF
     */
    public double computeIDF(String path, String word) {
        if (StringUtils.isBlank(path) || StringUtils.isBlank(word)) {
            return 0.0;
        }

        List<String> urls = readUrlFromText(path);
        int count = 1;
        for (String url : urls) {
            String text = getTextFromUrl(url);
            if (text.contains(word)) {
                count += 1;
            }
        }
        return Math.log10((double) urls.size() / count);
    }

    /**
     * 计算词频-逆文档频率 TF—IDF
     *
     * @param filePath 存放url的文件路径
     * @param terms    分词结果集合
     * @param word     词
     * @return TF—IDF
     */

    public Double computeTFIDF(String filePath, List<Term> terms, String word) {
        return computeTF(word, terms) * computeIDF(filePath, word);
    }
}

  • 测试代码
package com.myapp.ml.nlp;

import org.ansj.domain.Term;
import org.junit.Test;

import java.util.List;

/**
 *测试词语“语言”的 TF-IDF
 * Created by lionel on 16/12/15.
 */
public class TFIDFAlgorithmTest {
    @Test
    public void test() {
        TFIDFAlgorithm tfidfAlgorithm = new TFIDFAlgorithm();
        String filePath = "/Users/lionel/PycharmProjects/python-app/com/pythonapp/spider/output.txt";
        String url = "http://baike.baidu.com/item/Java/85979";
        String word = "语言";
        List<Term> terms = tfidfAlgorithm.parse(tfidfAlgorithm.getTextFromUrl(url));
        System.out.println("[【" + word + "】词频 ] " + tfidfAlgorithm.computeTF(word, terms));
        System.out.println("[【" + word + "】逆文档频率 ] " + tfidfAlgorithm.computeIDF(filePath, word));
        System.out.println("[【" + word + "】词频-逆文档频率 ] "+tfidfAlgorithm.computeTFIDF(filePath,terms,word));

    }
}
  • 测试结果 
    这里写图片描述

本篇博客描述的是我对 TF-IDF 算法的理解,代码是对算法过程简单的实现,若有失偏颇,还请指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TF-IDF(Term Frequency-Inverse Document Frequency)是一种常用于信息检索与文本挖掘的算法,用于评估一个词对于一篇文档或一个语料库的重要程度。 在Java实现TF-IDF算法可以借助一些常用的开源库,例如: 1. Lucene Lucene是一个全文检索引擎的Java实现。它提供了一个非常完整的文本搜索和分析库,可以方便地实现TF-IDF算法。Lucene具有良好的性能和可扩展性,并且有广泛的社区支持。 2. Apache Commons Math Apache Commons Math是一个常用的Java数学库,其中包含了计算TF-IDF所需的一些基本数学函数,例如对数函数和向量运算函数。使用Apache Commons Math可以方便地实现TF-IDF算法。 3. Stanford CoreNLP Stanford CoreNLP是斯坦福大学开发的一个Java自然语言处理库。它提供了丰富的文本处理功能,包括词性标注、分词、命名实体识别、依存分析等。使用Stanford CoreNLP可以很方便地对文本进行预处理,然后计算TF-IDF值。 下面是一个使用Lucene实现TF-IDF算法的示例代码: ```java import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; public class TfIdfDemo { public static void main(String[] args) throws Exception { // 创建分析器 Analyzer analyzer = new SmartChineseAnalyzer(); // 创建索引 Directory directory = new RAMDirectory(); IndexWriterConfig config = new IndexWriterConfig(analyzer); IndexWriter writer = new IndexWriter(directory, config); Document doc1 = new Document(); doc1.add(new TextField("content", "我们是好朋友", Field.Store.YES)); writer.addDocument(doc1); Document doc2 = new Document(); doc2.add(new TextField("content", "我们是同学", Field.Store.YES)); writer.addDocument(doc2); Document doc3 = new Document(); doc3.add(new TextField("content", "我们是同学和好朋友", Field.Store.YES)); writer.addDocument(doc3); writer.close(); // 计算TF-IDF值 IndexReader reader = DirectoryReader.open(directory); IndexSearcher searcher = new IndexSearcher(reader); Query query = new TermQuery(new Term("content", "好朋友")); TopDocs topDocs = searcher.search(query, 10); double tf = 1.0 / 3; double idf = Math.log(3.0 / (double)(topDocs.totalHits + 1)); System.out.println("TF-IDF值为:" + tf * idf); } } ``` 这段代码使用Lucene创建了一个包含三个文档的索引,然后计算了包含“好朋友”这个词的文档的TF-IDF值。其中,tf表示该词在文档中出现的频率,idf表示该词在整个语料库中的逆文档频率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值