Java实现TF-IDF算法

Java实现TF-IDF算法

TF-IDF是一种用于信息检索与数据挖掘的常用加权技术。TF是词频,IDF是逆文本频率指数,说白了就是提取文章关键字。

原理:

简单来说就是一个词语在一篇文章中出现的次数TF,和该词语在语料库中所出现的频率IDF。

TF = 文章中该词出现次数/文章总词数
IDF = log(语料库中所有文章 / (语料库中出现该词汇的文章+1))

TF-IDF == TF*IDF,这个值越大,就说明这个词汇越可能是该文章的关键字,统计这类关键字以便于文章提出主旨。

Java代码实现:

首先我在写自己毕设的时候需要提出目标创新创业项目计划书的主旨,我就上网找了几篇计划书的模板范文填充语料库:(当然这个越多越好,做成txt是由于txt比较方便扫描)

接着我借到了学弟的创新创业项目的计划书(由于提交计划书肯定不能是txt形式,所以这里我做成了PDF:因为我写了识别PDF的代码) 记录了一下存放路径
在这里插入图片描述
然后创建Maven项目:
记得在pom.xml中导入这两个所需要的Maven包

<dependency>	<!-- 用来识别文章中词性并分词的 -->
    <groupId>org.ansj</groupId>
    <artifactId>ansj_seg</artifactId>
    <version>5.1.6</version>
</dependency>

<dependency>	<!-- 用来处理PDF文件的导入问题 -->
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.12</version>
</dependency>

<!-- 当然网上Maven仓库里也有不同的版本 -->

具体ansj的使用可以去康康链接: http://nlpchina.github.io/ansj_seg/.里面还有分词以及词性的相关说明。

这是我的Maven项目:
在这里插入图片描述
Word类

用来存找出来的词汇和存储最后的TF-IDF

package com.tfidf1;
public class Word implements Comparable<Word> {
	String name;
	Double tf;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getTf() {
		return tf;
	}
	public void setTf(Double tf) {
		this.tf = tf;
	}
	public Word(String name, Double tf) {
		super();
		this.name = name;
		this.tf = tf;
	}
	public Word() {
		super();
	}
	@Override
	public String toString() {
		return "Word [name=" + name + ", tf=" + tf + "]";
	} 
    public int compareTo(Word o) {    //重写排序方法,后面方便降序排序
        if (this.tf > o.tf)
            return -1;
        else if (this.tf < o.tf)
            return 2;
        return 0;
    }
}

TF类:(目标文件读取,并统计词频)

这块用来PDFBox这个包里的方法
参考:https://iowiki.com/pdfbox/pdfbox_quick_guide.html.

package com.tfidf1;

import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.util.*;

// 词频
public class TF {
	
	// 分词
	public static List<Word> ansj(String str) {

		Set<String> Nature = new HashSet<String>();
		Nature.add("n");
		Nature.add("vn");  //只取n和vn两种词性 即名词和一部分动词
		Result result = ToAnalysis.parse(str); // 分词结果的一个封装,主要是一个List<Term>的terms

		List<Term> terms = result.getTerms(); // 拿到terms
		// System.out.println(terms.size());

		Map<String, Double> map = new HashMap<String, Double>();
		// 定义map来存储词汇名称和词的数量
		
		for (int i = 0; i < terms.size(); i++) {
			String word = terms.get(i).getName(); // 拿到词
			String natureStr = terms.get(i).getNatureStr(); // 拿到词性
			if (Nature.contains(natureStr)) // 判断set里有没有该词性,只取自己需要的
				map.put(word, map.getOrDefault(word, 0.0) + 1.0);
		}
		
		List<Word> list = new ArrayList<Word>();
		//word类的list存词汇名称以及暂时存一下词频
		
		for (String key : map.keySet()) {	// 便利map 计算词频
			Double ans = map.get(key);
			double tf = ans / terms.size();
			Word word = new Word(key, tf);
			list.add(word);
		}
//		Collections.sort(list); 这个地方实际没必要排序
		return list;
	}

	public static List<Word> FileRead(String path) {		//这里是对pdfbox的运用
		// 对PDF文件的读取
		String str = "";
		try {
			File file = new File(path);
			PDDocument document = PDDocument.load(file);
			PDFTextStripper pdfStripper = new PDFTextStripper();
			String text = pdfStripper.getText(document);
			for (int i = 0; i < text.length(); i++) {
				if (text.charAt(i) >= 0x4e00 && text.charAt(i) <= 0x9fbb)
					str += text.charAt(i);			//这块把所有不是汉字的过滤掉了
			}
			document.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ansj(str);
	}
}

IDF类:

统计语料库里的逆文本频率指数,语料库不变的话,这个也是不变的,写到项目里时只需要统计一次把统计结果放到数据库里就行了

package com.tfidf1;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;

//逆向文件频率

public class IDF {
	
	static Map<String, Double>idf_map = new HashMap<String, Double>();
	// 存储词汇和该词汇的逆向文件频率
	
	static Double ant = 1.0; // 总文档数量
	
	private static void calculate() {  // 计算逆向文件频率
		for (String key : idf_map.keySet()) {
			idf_map.put(key, Math.log(ant/idf_map.get(key)));
		}
	}

	
	public static void idfFileRead() { // Java 的基础文件操作
		try { 
			File file = new File("C:\\Users\\37388\\Desktop\\corpus\\1.txt");
			int i = 2;
			while(file.isFile()) { 
				String str = "";
				InputStreamReader reader = null;
				reader = new InputStreamReader(new FileInputStream(file));
				int tempchar;
				while ((tempchar = reader.read()) != -1) {
					if ((char) tempchar >= 0x4e00 && (char) tempchar <= 0x9fbb)
						str += (char) tempchar;
				}
				ansj(str);		// 取出一个文件里所有汉字就进行一次分词
				reader.close();
				String s = Integer.toString(i);
				file = new File("C:\\Users\\37388\\Desktop\\corpus\\"+s+".txt");
				i++;
				//System.out.println(file);
				ant++;
			}
			// 参考我上面的语料库里的存储,可以这么循环所有文件
			// 写到项目里应该存一个自增的id和路径到数据库里进行遍历比较方便
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		calculate();
	}

	public static void ansj(String str) {  //分词操作同上TF类

		Set<String> Nature = new HashSet<String>();
		Nature.add("n");
		Nature.add("vn");
		Result result = ToAnalysis.parse(str); // 分词结果的一个封装,主要是一个List<Term>的terms

		List<Term> terms = result.getTerms(); // 拿到terms
		//System.out.println(terms.size());
 
		Map<String, Double> map = new HashMap<String, Double>();
		for (int i = 0; i < terms.size(); i++) {
			String word = terms.get(i).getName(); // 拿到词
            String natureStr = terms.get(i).getNatureStr(); //拿到词性
            if(Nature.contains(natureStr))
			map.put(word, map.getOrDefault(word, 0.0) + 1.0);
		}
		for (String key : map.keySet()) {
			idf_map.put(key, idf_map.getOrDefault(key, 0.0) + 1.0);
		}
	}
	
	public static  Map<String, Double> getIdf_map() { // 将统计结果返回
		idfFileRead();
		return idf_map;
	}
}

TF-IDF类

调用整合计算:

package com.tfidf1;

import java.util.ArrayList;
import java.util.Collections;
import java.util.*;

public class TF_IDF {
	public static List<Word> calcuate(String path) {
		List<Word> tf = TF.FileRead(path);
		
		Map<String, Double> idf = IDF.getIdf_map();
		
		List<Word> list = new ArrayList<Word>();
		System.out.println(tf.size());
		for(int i=0; i<tf.size(); i++) {
			Word e = new Word();
			String s = tf.get(i).getName();
			double tmp = tf.get(i).getTf();
			e.setName(s);
			if(idf.containsKey(s)){
				e.setTf(tmp * idf.get(s));
			}
			list.add(e);
		}
		Collections.sort(list);  // 降序排序list
		return list;
	}
	public static void main(String []args) {
		List<Word> list = calcuate("C:\\Users\\37388\\Desktop\\资料文献\\创新创业计划书\\创新创业计划书.pdf");
		//这块可以自定义字符串
		for(int i=0; i<list.size(); i++) {
			System.out.println(list.get(i).name + " " + list.get(i).tf);
		}
	}
}

最后附上运行结果:(越靠前越关键)
在这里插入图片描述

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
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表示该词在整个语料库中的逆文档频率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值