Java實現 TF-IDF算法

0寫在前面:

前些天研究所有個處理文本的任務,將文本分完詞然後利用tfidf計算每個文檔中單詞的tfidf值,其中文本(是一些輿情的新聞)已經有特定標籤,表示是正向、負向還是中性的文本。後續使用這些處理好的文檔來做一些輿情的情感預測。
在網上查找了一些有關java編寫的tfidf程序,發現有的寫的很混亂,而且沒有相關的註釋,閱讀起來有很大的不方便,遂自己重寫編寫了一下tfidf,大神輕噴代碼。

1.TFIDF的思想

TFIDF的主要思想是:如果某個詞或短語在一篇文章中出現的頻率TF高,並且在其他文章中很少出現,則認爲此詞或者短語具有很好的類別區分能力,適合用來分類。
TFIDF實際上是:TF * IDF,TF詞頻(Term Frequency),IDF逆向文件頻率(Inverse Document Frequency)。TF表示詞條在文檔d中出現的頻率。
IDF的主要思想是:如果包含詞條t的文檔越少,也就是n越小,IDF越大,則說明詞條t具有很好的類別區分能力。如果某一類文檔C中包含詞條t的文檔數爲m,而其它類包含t的文檔總數爲k,顯然所有包含t的文檔數n=m+k,當m大的時候,n也大,按照IDF公式得到的IDF的值會小,就說明該詞條t類別區分能力不強。
但是實際上,如果一個詞條在一個類的文檔中頻繁出現,則說明該詞條能夠很好代表這個類的文本的特徵,這樣的詞條應該給它們賦予較高的權重,並選來作爲該類文本的特徵詞以區別與其它類文檔。這就是IDF的不足之處. 在一份給定的文件裏,詞頻(term frequency,TF)指的是某一個給定的詞語在該文件中出現的頻率。這個數字是對詞數(term count)的歸一化,以防止它偏向長的文件。(同一個詞語在長文件裏可能會比短文件有更高的詞數,而不管該詞語重要與否。

2.程序實現

首先是計算tf的代碼:我們使用map來存儲tf的結果,這個地方需要注意的是,我們先通過分詞軟件將所有的文檔讀成一個map,Map< String,String >其中key是每個文檔的文檔名,對應的value是分完詞後的文檔內容,單詞以空格分割。這裏我們定義tf的方法傳的參數是map中的value值(也就是一個String),然後計算單詞的tf值。

舉例:參數wordAll=“合肥 工業 大學 簡稱 合 工大 位於 安徽省 省會 合肥市 創建 1945年 秋 1960年 10月 22日 中共中央 批准 全國 重點 大學 教育部 直屬 高校 工程 工程 優勢 學科 創新 平臺 項目 建設 高校 工科 主要 特色 工 理 文 經 管 法 教育 多 學科 綜合性 高等院校”

/**
     * 計算每個文檔的tf值
     * @param wordAll
     * @return Map<String,Float> key是單詞 value是tf值
     */
    public static Map<String,Float> tfCalculate(String wordAll){
        //存放(單詞,單詞數量)
        HashMap<String, Integer> dict = new HashMap<String, Integer>();
        //存放(單詞,單詞詞頻)
        HashMap<String, Float> tf = new HashMap<String, Float>();
        int wordCount=0;

        /**
         * 統計每個單詞的數量,並存放到map中去
         * 便於以後計算每個單詞的詞頻
         * 單詞的tf=該單詞出現的數量n/總的單詞數wordCount
         */
        for(String word:wordAll.split(" ")){
            wordCount++;
            if(dict.containsKey(word)){
                dict.put(word,  dict.get(word)+1);
            }else{
                dict.put(word, 1);
            }
        }

        for(Map.Entry<String, Integer> entry:dict.entrySet()){
            float wordTf=(float)entry.getValue()/wordCount;
            tf.put(entry.getKey(), wordTf);
        }
        return tf;
    } 

然後計算tfidf的值:這裏需要說明的是,在計算idf的時候會涉及到包含某個單詞的文檔數,所以這裏,會將分完詞後的map傳輸進來。
最後用一個Map來存儲最後的結果,Map< String>

/**
     * 
     * @param D 總文檔數
     * @param doc_words 每個文檔對應的分詞
     * @param tf 計算好的tf,用這個作爲基礎計算tfidf
     * @return 每個文檔中的單詞的tfidf的值
     * @throws IOException 
     * @throws FileNotFoundException 
     */
    public static Map<String,Float> tfidfCalculate(int D, Map<String,String> doc_words,Map<String,Float> tf) throws FileNotFoundException, IOException{

        HashMap<String,Float> tfidf=new HashMap<String, Float>();
        for(String key:tf.keySet()){
            int Dt=0;
            for(Map.Entry<String, String> entry:doc_words.entrySet()){

                String[] words=entry.getValue().split(" ");

                List<String> wordlist=new ArrayList<String>();
                for(int i=0;i<words.length;i++){
                    wordlist.add(words[i]);                }
                if(wordlist.contains(key)){
                    Dt++;
                }
            }
            float idfvalue=(float) Math.log(Float.valueOf(D)/Dt);
            tfidf.put(key, idfvalue * tf.get(key));

        }       
        return tfidf;  
  }


3.最後
tfidf的githup地址:https://github.com/xudongMk/tfidfkdong

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章