运行结果 

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

统计 1

 最后结果:

统计 2

通知板实现观望者接口:

算法介绍

近些年要做领域概念的提取,TFIDF作为一个很经典的算法可以看成内部的一步处理。

有关TFIDF算法的牵线可以参照这篇博客http://www.ruanyifeng.com/blog/2013/03/tf-idf.html

总结公式相比简单,如下:

 统计 3

需要:WeatherData得到最新数据时,需要实时更新两个公告板的面貌数据。六个公告板分别是:近来场地、气象总结、天气预报。

结论

可以见到“离退休人士”在养老保险和社保领域,tfidf值相比较高,可以视作判断是否为世界概念的一个依照。

本来TF-IDF算法尽管很经典,但要么有广大相差,无法独立依赖其结果做出判断。

无数杂谈提议了改进措施,本文只是实现了最要旨的算法。

假如有另外思路和设法欢迎研讨。


转载请注明原文链接:http://www.cnblogs.com/justcooooode/p/7831157.html

1.WeatherData有五个getter方法getTemperature(),getHumidity(),getPressure()分别收获温度、湿度、气压值;

代码实现

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 2017年11月12日 下午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));
    }
}            

主题不需要通晓阅览者具体是谁,只需要精晓观察者实现了Observer接口,并不珍视观看者具体做了何等事,这种互动对象间的松耦合是规划中追求的尺度,下边看主题Subject和观望者Observer接口代码:

预处理

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

为了缩时辰间,首先举行分词,一个词输出为一行方便统计,分词工具采纳的是HanLp。

接下来,将一个天地的文档合并到一个文本中,并用“$$$”标识符分割,方便记录文档数。

统计 4

下边是采取的天地语料(PATH目录下):

统计 5

 1 /*目前状况布告板*/
 2 public class CurrentConditionsDisplay implements Observer, DisplayElement {
 3 
 4     private float temperature;
 5     private float humidity;
 6     private Subject weatherData;
 7     
 8     public CurrentConditionsDisplay(Subject subject) {
 9         this.weatherData = subject;
10         weatherData.registerObserver(this);
11     }
12     
13     public void removeObserver() {
14         weatherData.removeObserver(this);
15     }
16 
17     public void update(float temp, float humidity, float pressure) {
18         this.temperature = temp;
19         this.humidity = humidity;
20         display();
21     }
22     
23     public void display() {
24         System.out.println("CurrentConditionsDisplay conditions:" + temperature + "F degrees and " + humidity + "% humidity");
25     }
26 
27 }
28 
29 /*天气预报布告板*/
30 public class ForecastDisplay implements Observer, DisplayElement {
31 
32     private float temperature;
33     private float pressure;
34     private Subject weatherData;
35     
36     public ForecastDisplay(Subject subject) {
37         this.weatherData = subject;
38         weatherData.registerObserver(this);
39     }
40     
41     public void removeObserver() {
42         weatherData.removeObserver(this);
43     }
44 
45     public void update(float temp, float humidity, float pressure) {
46         this.temperature = temp;
47         this.pressure = pressure;
48         display();
49     }
50     
51     public void display() {
52         System.out.println("ForecastDisplay conditions:" + temperature + "F degrees and " + pressure + "Pa");
53     }
54 
55 }
56 
57 /*气象状况布告板略*/

一、整理分析:

 

 1 public class WeatherData {
 2     
 3     public void measurementsChanged() {
 4         float temperature = getTemperature();
 5         float humidity = getHumidity();
 6         float pressure = getPressure();
 7         //目前状况布告板更新
 8         currentConditionsDisplay.update(temperature, humidity, pressure);
 9         //气象统计布告板更新
10         statisticsDisplay.update(temperature, humidity, pressure);
11         //天气预报布告板更新
12         forecastDisplay.update(temperature, humidity, pressure);
13     }
14     
15     //WeatherData的其他方法
16     
17 }

观察者形式定义:定义了目的期间的一对多倚重,这样一来,当一个目标改变时,它的持有依赖者都会吸纳文告并自动更新。

 1 public class ObserverTest {
 2     
 3     @Test
 4     public void test() {
 5         WeatherData weatherDate = new WeatherData();
 6         CurrentConditionsDisplay ccDisplay = new CurrentConditionsDisplay(weatherDate);
 7         ForecastDisplay fcDisplay = new ForecastDisplay(weatherDate);
 8         weatherDate.setMeasurements(12.3f, 38.6f, 130f);
 9         ccDisplay.removeObserver();
10         weatherDate.setMeasurements(12.3f, 38.6f, 130f);
11         fcDisplay.removeObserver();
12         weatherDate.setMeasurements(12.3f, 38.6f, 130f);
13         System.out.println("notify ok");
14     }
15 }

Java
JDK中也设有已经定义好的观望者形式,在java.util包中(Observable(大旨),
Observer(观望者)),我们有趣味可以团结了然下。java中的观望者能够手动从主题中拉取数据,焦点也可以主动去推送数据。

气象站系统:气象站可以透过物理装置取得气象信息,WeatherData对象足以由此气象站提供的外部接口获取气象信息。

代码测试:

留存的题材:针对落实编程,会难以增加和掩护,假若急需新增或者去除公告板,必须修改该程序。

 

现今让WeatherData实现主旨接口:

运行结果:

 

3.系统需可扩充,可随时新增或者去除通知板。

诸如报社,订阅报纸后,报社会定期给您邮寄报纸,截止订阅后报社便停下邮寄。大家把报社改称为“核心Subject”,把订阅者改成为“观望者 Observer”来研究观看者情势。

1 /*观察者*/
2 public interface Observer {
3     public void update(float temp, float humidity, float pressure);
4 }

1 /*主题*/
2 public interface Subject {
3     /*注册成为观察者*/
4     public void registerObserver(Observer o);
5     /*移除观察者*/
6     public void removeObserver(Observer o);
7     /*通知所有观察者*/
8     public void notifyObservers();
9 }   

1 public interface DisplayElement {
2     /**
3      * 布告板都实现该接口,调用display来展示气象信息
4      */
5     public void display();
6 }
1 CurrentConditionsDisplay conditions:12.3F degrees and 38.6% humidity
2 ForecastDisplay conditions:12.3F degrees and 130.0Pa
3 com.project.design.observer.CurrentConditionsDisplay is removed.
4 ForecastDisplay conditions:12.3F degrees and 130.0Pa
5 com.project.design.observer.ForecastDisplay is removed.
6 notify ok

先是次尝试:

 1 public class WeatherData implements Subject {
 2     private List<Observer> observerList;
 3     private float temperature;
 4     private float humidity;
 5     private float pressure;
 6     
 7     public WeatherData() {
 8         observerList = new ArrayList<Observer>();
 9     }
10     public void registerObserver(Observer o) {
11         observerList.add(o);
12     }
13 
14     public void removeObserver(Observer o) {
15         int i = observerList.indexOf(o);
16         if (i >= 0) {
17             observerList.remove(i);
18             System.out.println(o.getClass().getName() + " is removed.");
19         }
20     }
21 
22     public void notifyObservers() {
23         for (Observer o : observerList) {
24             o.update(temperature, humidity, pressure);
25         }
26     }
27     /*数据更新时,通知所有观察者*/
28     public void measurementsChanged() {
29         notifyObservers();
30     }
31 
32 }

统计, 

2.当数码变动时,WeatherData有个方法measurementsChanged()会被调用(大家不关注如何被调用),此时需立异多个通告板的气象信息;