1. 程式人生 > >資料探勘---頻繁項集挖掘Apriori演算法的C++實現

資料探勘---頻繁項集挖掘Apriori演算法的C++實現

1 準備

2 作業粗糙翻譯內容

2.1 前言

  • 程式設計作業可能比書面作業花費更多的時間,而這也算是你最後成績的10%,所以請提前開始;
  • 這是個人作業,你可以與你的同學或者老師交流,但是不能夠共享程式碼和抄襲;
  • 類似的庫或頻繁模式挖掘演算法的程式,可以在網上找到,但你不允許直接使用這些資源,這意味著你可以不包括公共圖書館,或者修改現有的方案;
  • 你可以使用Java、C++和Python程式語言;
  • 你將會使用UNIX核心包作業系統下工作。它在Linux和MacOS系統上效果很好。如果你是一個Windows使用者,你需要:
    • 連線到一個EWS實驗室機器或找到其他包具有相同的功能;
  • 請你寫一個關於作業報告,所以要注意有“問題和思考”的地方;

2.2 目標

  • 探索頻繁模式挖掘如何應用到文字挖掘中,來發現一些有意思的詞語;
  • 在這個作業中,你首先將在文集上執行LDA(文件主題生成模型),這個文集來自5個領域的會議論文。基於LDA的結果,一個主題(代表一個特定的域)被分配給每個標題每個單詞。然後,你經常從每個主題中寫一個頻繁的挖掘演算法來獲取有意義的短語。頻繁挖掘模式可能不一定是這個主體中有意義的詞語。所以你會考慮如何從所有的頻繁模式中提取出有意義的詞語。最終的目標是為每個主題輸出高代表性的短語。

2.3 第一步:認識資料

  我們從5個領域的電腦科學會議,收集論文標題:Data Mining(DM), Machine Learning(ML), Database(DB),Information Retrieval(IR,資訊檢索)和Theory(TH)。你可以從連結中下載raw格式資料paper_raw.txt(作業已經給了,請注意:在這次作業中,我們不會直接使用這個檔案,但是你可以自行檢視這個檔案裡的內容,它裡面最原始的標題是什麼樣的)。每一行包含兩欄,每篇論文的PaperID

Title,然後用製表符('\t')分隔開來。回憶課堂上的所講的例子,對於檔案中的每一行,你可以把它視為一個例項。在標題的每個單詞是等同於在一個例項中的子項。需要注意的地方就是PaperID在整個資料集中是唯一的,所提供的檔案是所有資料集中的一個子集而已,有可能PaperID是不為0,也有可能不是連續的。原始資料格式如下:

原始資料格式

  在這個作業中,資料預處理過程中去除了停用詞(如一些功能詞:the、is、as、which等),只留下詞語和詞幹。你可以在這裡下載資料處理後的檔案paper.txt,這是我們將使用的實際資料集。在這個檔案中,每行是以一個PaperId,然後緊跟一些項,格式是:

PaperID '\t' term_1 ' ' term_2 ' ' ......

  paper.txt檔案內容部分如下:

預處理後資料

2.4 第二步:資料預處理(20points)

  這一步準備輸入LDA。你將生成基於paper.txt的兩個檔案。

2.4.1 生成一個目錄(10points)

  首先你需要從paper.txt中產生一個詞彙,並命名存放詞彙檔名為vocab.txt。在這個檔案中的每一行是一個從paper.txt提取出來的獨立的詞語,每個詞應出現一次,下面是vocal.txt的前五行,需要注意的是,詞語的順序可以不同:

    automatic 
    acquisition 
    proof 
    method 
    philosophical 
    ...

2.4.2 以字典形式標記文字(10points)

  在這一步,要求將paper.txt檔案中的每個標題轉換成如下格式:

[M] [term_1]:[count] [term_2]:[count] ...  [term_N]:[count]

  其中,[M]是每行的中的每個標題中的獨一無二的詞語的個數(如PaperID=7600,M=4)。[count]是指每個標題中的每個獨一無二的詞語出現的頻率(如PaperID=7600,[term_1]:[count]=1)。以PaperID=7600為例,會產生如下的資料格式:

"4 0:1 1:1 2:1 3:1"

  注意,[term_i]是一個整數,是索引在vocab.txt一個的某個詞語;下標從0開始。最後命名輸出檔案title.txt,將格式化的資料儲存到這個檔案中。

注:如果你使用任何其他的LDA包,而不是在接下來的一步中提到的,請確保你的資料格式能夠匹配你所使用的LDA包的需求。

2.5 第三步:分割槽(10points)

  回想我們在電腦科學中的五個領域收集的論文標題,如果在紙上直接執行頻繁模式挖掘演算法,模式將會是獨立的主題。因此,我們想要挖掘頻繁模式中的每個領域,標題和詞語也應該被劃分為五個領域。請注意,每個領域的的知識你是不知道的。相反的,我們應用的主題模型,將自動地去發現隱藏在標題和詞語後面的主題。具體來說,我們應用LDA(你不必理解主題模型具體是怎麼工作的)來為每個詞語指定一個主題。

2.5.1 為每個詞語(Term)指定一個主題(Topic)(5points)

  • 下載LDA包包。解壓後你可以看到原始碼列表。你可以參考這一頁,它對這個包進行了全面的介紹。
  • 開啟一個終端,進入原始碼的目錄,make,生成一個可執行的LDA檔案。
  • lda-c-dist目錄下,有一個settings.txt檔案,你可以使用下面的設定,如果你對LDA怎麼工作的很清楚,可以自己調整相關引數。
    var max iter 20 
    var convergence 1e-6 
    em max iter 100 
    em convergence 1e-4 
    alpha estimate
  • 用下面的命令執行LDA
<DIR>/lda-d-dist/lda est 0.001 5 <DIR>/lda-d-dist/settings.txt <DIR>/title.txt  random <DIR>/result

  0.001是給LDA主題的比率(這只是一個引數,如果你不是很瞭解,你不是必須改變它);5,代表5個主題;title.txt是在第二步產生的檔案,輸出的內容將被放到result資料夾中。DIR,是你當前的工作目錄。
- 檢查你的輸出
- 在result目錄中,開啟word-assignments.dat檔案,每一行的格式為:

[M] [term_1]:[topic] [term_2]:[topic] ... [term_N]:[topic]

  [M]是每行的中的每個標題中的獨一無二的詞語的個數(如PaperID=7600,M=4)。與每個詞語相關聯的[topic]是分配給它的主題。topic下標是從0開始的,如某一行可以是:004 0000:02 0003:02 0001:02 0002:02;這意味著在這個標題中所有的詞語都被分配給第三個主題(即topic 02)。注意,你不限於使用這個包,這裡還有另外一個選擇:http://mallet.cs.umass.edu/topics.php

2.5.2 重新組織主題(5points)

  要求重新組織五個主題的詞語。對於第i個主題,要求建立一個檔名為topic-i.txt的檔案。通過分配給詞語的主題,分離在word-assignment.dat檔案中的每一行。例如,在word-assignment.dat檔案中的每一行可以被認為是以下格式(注意:在這裡用實際的詞語替換整數是為了更好地說明):

    004 automatic:02 acquisition:02 proof:02 method:02 
    005 classification:03 noun:02 phrase:03 concept:01 individual:03

  然後輸出檔案應該是:

topic-0.txt

...
topic-1.txt
concept
...
topic-2.txt:
automatic acquisition proof method
noun
...
topic-3.txt
classification phrase individual
...

  在真正的檔案中,每一個詞語應該被表示為一個整數對應於第二步所產生的字典。topic-i.txt看起來像這樣:

[term1] [term2]....[termN]
[term1] [term2]....[termN]
...

2.6 第四步:挖掘出每個主題的頻繁模式(30points)

  在這一步,你需要實現一個頻繁模式挖掘演算法。你可以選擇任何你所喜歡的頻繁模式挖掘演算法,比如Apriori、FP-Growth、ECLAT等。請注意,你需要在相應的5個主題的用5個檔案來執行程式碼。執行輸出格式為([s] (space) [t1 (space) t2 (space) t3 (space) …]):

#Support          [frequent pattern] 
#Support          [frequent pattern] 
...

  並用頻繁模式以#Support開頭,從高到低進行排序,你的輸出檔案應該放在一個名為patterns的資料夾中。第i個檔案命名為pattern-i.txt。(提示:需要你自己算出最小支援度min_sup)
  思考問題A:你如何選擇該任務的min_sup?解釋你如何選擇你的min_sup的報告,任何合理的選擇都可以。

2.7 第五步:挖掘最大/閉合模式(20points)

  在此步驟中,您需要實現一個演算法來採掘最大頻繁項集和閉項集。您可以根據步驟4的輸出編寫的程式碼,或實現特定的演算法來挖掘最大頻繁項集和閉項集,如CLOSET,MaxMiner等。
  輸出的形式應和第四步中的輸出是一樣的。最大頻繁項集輸出到max目錄,第i個檔案命名為max-i.txt。閉項集輸出到closed目錄,第i個檔案命名為closed-i.txt。
  思考問題B:你能找出哪些主題對應於哪個領域的基礎上你所採掘出的模式?寫在你的觀察報告。
  思考問題C:比較頻繁模式,最大頻繁項集和閉項集的結果,是令人滿意的結果嗎?寫下你的分析。

最大頻繁項集:就是頻繁模式挖掘後的第k頻繁項集
閉項集:就是指一個項集X,它的直接超集的支援度計數都不等於它本身的支援度計數。如果閉項集同時是頻繁的,也就是它的支援度大於等於最小支援度閾值,那它就稱為閉頻繁項集。
直接超集:如最後一部分的test.txt中[BB DD],這一項的超集是[BB DD]和[AA BB DD],兩個超集的支援度都為1,而[BB DD]項支援度為2,所以[BB DD]是閉項集。

2.8 第六步:按純度排序(10points)

  在http://arxiv.org/pdf/1306.0271v1.pdf這篇文章中,純度被作為短語排名的措施之一。一個短語是純粹的主題,如果它是唯一經常在檔案(這裡的檔案是指標題)有關主題和不經常在檔案中有關其他主題。例如,“查詢處理”是資料庫主題中的一個更為純的短語。我們通過比較看到在topic-t集合D(t)中的一個短語的概率測度模式的純度(T),看到它在任何其他topic-t集的概率(t’ = 0,1,…,k,t‘ != t)。在我們的例子中,k = 4。相比其他的任何主題,純度本質上能夠測出模式在一個主題的不同。定義如下:

purity(p,t)=log [ f(t,p) / | D(t) | ] - log (max [ ( f(t,p) + f(t',p) ) / | D(t,t') | ] )

  這裡,f(t,p)是模式p出現在主題t的頻率,我們定義D(t)是一個檔案的集合,這些檔案至少有一個字被分配給主題t。D(t) = { d | 主題t被分配至少有一個字在d中。 D(t,t’) 是D(t)和D(t’)的聯合。|-|測量一個set的大小。事實上,|D(t)|是在topic-i.txt的行數,但是注意 | D(t,t’) | != | D(t) | + | D(t’) |。
  從步驟4獲得的模式重新排列。輸出形式應該是:

Purity         [frequent pattern] 
Purity         [frequent pattern] 
...

  通過結合支援度和純度的方式(這裡你需要提出如何結合的),頻繁模式從高到低被排序了。你的輸出檔案應該放在一個名字為purity目錄中,第i個檔案命名為purity-i.txt。

2.9 第七步:加分(20points)

2.10 第八步:報告(10points)

  現在你準備寫你的報告。你應該在報告中包含以下內容:
- 簡要說明第四步~第六步你所用的演算法。
- 回答所有的思考問題。
- 列出你的原始檔名及其相應的步驟。

2.11 專案組織和提交

  結構應該如下(<>這個括號裡面是你寫的程式碼,後面跟著“|——–”的是目錄):

    yourNetId_assign3|--------          title.txt
                                    <preprocessing source files>
                                    topic-0.txt~topic-4.txt
                                    <re-organizing source files>
                                    <frequent mining source files>
                                    patterns                   |--------pattern-0.txt~pattern-4.txt
                                    <max/closed mining source files>
                                   max                        |--------max-0.txt~max-4.txt
                                    closed                     |--------closed-0.txt~closed-4.txt
                                    <Re-rank source files>
                                    purity                     |------purity0.txt~purity-4.txt
                                    report.pdf

注:翻譯的不準勿怪!最後紅色字部分及後面沒翻譯。

3 程式設計實現

  在Linux系統上實現是不二選擇,因為作業裡面的lda可執行程式是要make編譯產生的,而在windows上去make的話,會缺少一些庫檔案,導致出現錯誤。不過,我電腦上剛好有個工具鏈(用來開發開源無人機Pixhawk的),裡面一些庫是跟Linux系統上的是一樣的,所以進入作業中lda-c-dist資料夾後make一下,產生了一個lda.exe可執行檔案。所以我的實現都是在windows上面實現的。
  仔細閱讀作業要求,可知要自己程式設計,根據提供的paper.txt檔案產生vocab.txt、title.txt這兩個檔案。然後用lda.exe產生word-assignments.dat檔案,又需根據這個dat檔案程式設計產生五個topic-i.txt檔案。然後用apriori演算法分別對這五個檔案進行頻繁項集挖掘。最後是完成作業中的問題與思考以及報告。

3.1 程式設計產生作業的檔案

  包括:vocab.txt、title.txt、topic-i.txt(i=0,1,2,3,4)。

//-----------------------------------------------------------------
//文 件 名:vocab.cpp
//建立日期:2015-10-15
//作    者:yicm
//功    能:由paper.txt產生vocab.txt,再由vocab.txt產生title.txt,
//          然後根據title.txt,由lda.exe產生的word-assignment.dat
//          作為輸入,產生topic-i.txt五個檔案
//說    明:此程式是連續處理的,word-assignment.dat是title.txt產生
//          之後再作為輸入的
//修改日期:
//  2015-11-1:
//-----------------------------------------------------------------

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include <cstdlib>
#include <stdexcept>
#include <sstream>
#include <string.h>

using namespace std;

ifstream& open_file(ifstream &in,const string &file);
void split(string& s, string& delim1,string &delim2, vector<string> &ret);

class VocabProcess{
/*處理paper.txt*/ 
public: 
    typedef vector<string>::size_type line_no;
    void read_file(ifstream &is);   
    ofstream& write_vocab(ofstream &,const string);

private:    
    void store_file(ifstream&);
    void build_paper_map();
    bool isNum(string);
    vector<string> lines_of_text;
    map< string, set<line_no> > word_map;
/*處理paper.txt和vocab.txt檔案*/
public:
    ofstream& write_title(ofstream &,const string);
    void build_vocab_map(string);
private:
    string get_vocab_of_the_line(int) const;
    int get_line_num_of_vacab(const string &) const;
    int get_vocab_num_of_title(string);
    int get_fre_of_vocab_form_title(string,string);
    map<string, int> vocab_map;
/*處理word-assignments.dat,按topic分為五類,放於五個檔案中*/   
public:
    void reorganize_terms_by_topic(string);
private:
    vector<string> lines_of_dat;
};

//-----------------------------------------------------------
//讀取檔案,將內容存放到map中
//-----------------------------------------------------------
void VocabProcess::read_file(ifstream &is)
{
    store_file(is);
    build_paper_map();
    is.close();
}

//-----------------------------------------------------------
//不重複的將詞語寫入到檔案中
//-----------------------------------------------------------
ofstream& VocabProcess::write_vocab(ofstream &out,const string fileName)
{
    out.close();
    out.clear();
    out.open(fileName.c_str(),fstream::out);
    map< string, set<line_no> >::iterator it = word_map.begin();
    while(it != word_map.end()){
        out << it->first.c_str() << endl;
        ++it;
    }
    out.close();
    return out;
}

int VocabProcess::get_line_num_of_vacab(const string &query_word) const
{
    map<string,int>::const_iterator
                    loc = vocab_map.find(query_word);
    if(loc == vocab_map.end()){
        return -1;//不存在這個string
    }
    else return loc->second;
}

int VocabProcess::get_vocab_num_of_title(string title)
{
    int num = 0;
    string word;
    istringstream line(title);
    while(line >> word){
        if(!isNum(word)){                               
            ++num;
        }
    }
    return num;
}

int VocabProcess::get_fre_of_vocab_form_title(string title,string vocab)
{
    int num = 0;
    string word;
    istringstream line(title);
    while(line >> word){
        if(!isNum(word)){                               
            if(word == vocab)++num;
        }
    }
    return num;
}

ofstream& VocabProcess::write_title(ofstream &out,const string fileName)
{
    out.close();
    out.clear();
    out.open(fileName.c_str(),fstream::out);

    for(line_no line_num = 0; line_num != lines_of_text.size(); ++line_num){
        //繫結行字串到istringstream
        istringstream line(lines_of_text[line_num]);
        string word;
        //迴圈從行字串讀取單詞到string型別word
        out << get_vocab_num_of_title(lines_of_text[line_num]) <<" ";
        while(line >> word){
            if(!isNum(word)){                                               
                out <<get_line_num_of_vacab(word) << ":" <<get_fre_of_vocab_form_title(lines_of_text[line_num],word) << " ";
            }
        }
        out << endl;
    }
    out.close();
}

//-----------------------------------------------------------
//將檔案中的每一行字串作為一個元素依次存放到vector中
//-----------------------------------------------------------
void VocabProcess::store_file(ifstream& is)
{
    string textline;
    while(getline(is,textline))
        lines_of_text.push_back(textline);
}
//-----------------------------------------------------------
//將存放每一行字串的vector的元素依次取出,將其分解為單詞,並將單詞的行數儲存到map< string, set<line_no> >中
//-----------------------------------------------------------
void VocabProcess::build_paper_map()
{
    for(line_no line_num = 0; line_num != lines_of_text.size(); ++line_num){
        //繫結行字串到istringstream
        istringstream line(lines_of_text[line_num]);
        string word;
        //迴圈從行字串讀取單詞到string型別word
        while(line >> word){
            if(!isNum(word)){               
                //將行號插入到鍵值為word,值為vector型別的map中
                word_map[word].insert(line_num);
            }
        }
    }
}
void VocabProcess::build_vocab_map(string file)
{
    ifstream in;
    in.close();
    in.clear();
    in.open(file.c_str());

    string vocab;
    int i = 0;

    while(getline(in,vocab)){
        vocab_map[vocab] = i++;
    }
}
//-----------------------------------------------------------
//判斷字串是否為數字
//-----------------------------------------------------------
bool VocabProcess::isNum(string str)
{
    stringstream sin(str);  
    int num;  
    char c;  
    if(!(sin >> num))  
        return false;  
    if (sin >> c)  
        return false;  
    return true;    
}

string VocabProcess::get_vocab_of_the_line(int line_num) const
{
    map<string,int>::const_iterator map_it = vocab_map.begin();

    while(map_it != vocab_map.end()){
        if(map_it->second == line_num){
            return map_it->first;
        }
        ++map_it;
    }
    return "";
}

void VocabProcess::reorganize_terms_by_topic(string datFileName)
{
    char topicFile[][16] = {"topic-0.txt","topic-1.txt","topic-2.txt","topic-3.txt","topic-4.txt"};

    fstream topicF[5];

    for(int i = 0; i < 5; ++i){
        topicF[i].open(topicFile[i],fstream::out);
    }

    /*讀取word-assignments.dat資料,存放到vector中*/

    ifstream infile;
    if(!open_file(infile,datFileName)){
        cerr << "open file is failed!" << endl;
        return ;
    }
    string datline;
    while(getline(infile,datline))
        lines_of_dat.push_back(datline);

    /*處理資料*/
    string delim1 = ":";
    string delim2 = " ";
    for(line_no line_num = 0; line_num != lines_of_dat.size(); ++line_num){
        {
            vector<string> ret;
            split(lines_of_dat[line_num],delim1,delim2,ret);            
            //cout <<lines_of_dat[line_num] << endl;        
            int vocab_num = atoi(ret[0].c_str());           
            //cout << "vocab_num=" <<vocab_num << endl;         
            int topic_num = 0;

            string topicLine[5] = "";
            for(int i = 1; i < (2*vocab_num+1); i+=2){  
                topic_num = atoi(ret[i+1].c_str());
                topicLine[topic_num] += get_vocab_of_the_line(atoi(ret[i].c_str())) + " ";
            }           
            for(int j =0; j < 5; ++j){
                if(topicLine[j].size() != 0)
                    topicF[j] << topicLine[j] << endl;
            }
        }
    }
    for(int i = 0; i < 5; ++i){
        topicF[i].close();
    }
}


//-----------------------------------------------------------
//開打一個檔案
//-----------------------------------------------------------
ifstream& open_file(ifstream &in,const string &file)
{
    in.close();
    in.clear();
    in.open(file.c_str());
    return in;
}

void split(string& s, string& delim1,string &delim2, vector<string> &ret)
{
    size_t last1 = 0;
    size_t last2 = 0;
    size_t index = 0;
    size_t last = 0;
    size_t index1 = s.find_first_of(delim1,last1);
    size_t index2 = s.find_first_of(delim2,last2);

    if(index1 > index2){
        last = last2;
        index = index2;
    }
    else {
        last = last1;
        index = index1;
    }
    //npos表示沒有查詢到
    while (index != string::npos)                   
    {
        //printf("%d %d\n",index,last);
        ret.push_back(s.substr(last,index-last));
        last = index + 1;

        size_t index1 = s.find_first_of(delim1,last);
        size_t index2 = s.find_first_of(delim2,last);               
        if(index1 > index2){
            index = index2;
        }
        else {
            index = index1;
        }           
    }   
    if (index-last>0)
    {
        ret.push_back(s.substr(last,index-last));
    }   
}

int main(int argc,char *argv[])
{
    ifstream infile;
    ofstream outVocab;
    ofstream outTitle;

    if(argc < 5 || !open_file(infile,argv[1])){
        //vocab.exe paper.txt vocab.txt title.txt word-assignments.dat
        //輸入檔案為:paper.txt word-assignments.dat
        //輸出檔案為:vocab.txt title.txt
        cerr << "usage:\t vocab.exe [input_file_name] [outout_vocab_file_name] [outout_title_file_name] [dat_file_name]" << endl;
        return EXIT_FAILURE;
    }
    //將paper.txt轉換成vocab.txt
    VocabProcess tq;
    tq.read_file(infile);
    tq.write_vocab(outVocab,argv[2]);

    //將vocab.txt轉換成title.txt
    tq.build_vocab_map(argv[2]);
    tq.write_title(outTitle,argv[3]);

    //將word-assignment.dat相關topic資料分類到五個檔案中
    tq.reorganize_terms_by_topic(argv[4]);
    return EXIT_SUCCESS;
}

3.2 Apriori演算法C++實現

  這個演算法是對資料探勘概念與技術一書中中的Apriori演算法虛擬碼的實現。

  Apriori演算法原理也可以參考這本書,講的很詳細。這裡就不講了。

  虛擬碼如下:

//【Apriori】
//  使用逐層迭代方法基於候選產生找出頻繁項集
//【輸入】
//  D:事務資料庫
//  min_sup:最小支援度閾值(絕對支援度)
//【輸出】
//  L,D中的頻繁項集。
//【方法實現】

/*找出頻繁1項集*/
     L1 =find_frequent_1-itemsets(D); 
     For(k=2;Lk-1 !=空集;k++){
//產生候選,並剪枝
        Ck =apriori_gen(Lk-1 ); 
//掃描 D 進行候選計數
        For each 事務t  包含於 D{    //掃描D,進行計數 
            Ct =subset(Ck,t);      //得到 t 的子集,他們是候選
            For each 候選 c 包含於 Ct
                c.count++;
        }
        //返回候選項集中不小於最小支援度的項集
        Lk ={c 屬於 Ck | c.count>=min_sup}
}
Return L= 所有的頻繁集;
第一步:連線(join)
Procedure apriori_gen (Lk-1 :frequent(k-1)-itemsets)
      For each 項集 l1 屬於 Lk-1
         For each 項集 l2 屬於 Lk-1
            If( (l1 [1]=l2 [1])&&( l1 [2]=l2 [2])&& ……&& (l1 [k-2]=l2 [k-2])&&(l1 [k-1]<l2 [k-1]) ) 
then{
                   c = l1 連線 l2     // 連線步:產生候選
                   //若k-1項集中已經存在子集c則進行剪枝
                   if has_infrequent_subset(c, Lk-1 ) then
                       delete c;     // 剪枝步:刪除非頻繁候選
                   else add c to Ck;
                   }
          Return Ck;
第二步:剪枝(prune)&nbsp;
 Procedure has_infrequent_sub (c:candidate k-itemset; Lk-1 :frequent(k-1)-itemsets)
         For each (k-1)-subset s of c
            If s 不屬於 Lk-1 then
               Return true;
        Return false;

  C++程式設計實現Apriori演算法:

apriori.h
#ifndef __APRIORI_H_
#define __APRIORI_H_


#include <iostream>
#include <cstdlib>
#include <map>
#include <set>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <utility>

using namespace std;

class Apriori{

public:
    Apriori(string dataFileName,float minSup){
        this->dataFileName = dataFileName;
        this->minSup = minSup;
    }

/*Functions*/
public:
    void printMapSet(map< set<string> ,int> &mapSet);
    void printsetSet(set< set<string> > &);
    void printSet(set<string> &);

    int buildData();
    map<string, int> getCandidate1ItemSet();
    map< set<string>, int > findFrequent1Itemsets();
    set< set<string> > aprioriGen(int m, set< set<string> > &);
    bool has_infrequent_subset(set<string> &, set< set<string> > &);
    map< set<string>, int > getFreqKItemSet(int k, set< set<string> > freqMItemSet);    
    set< set<string> > keySet(map< set<string>, int > &mapSet);
/*Functions*/
private:    
    set<string> retainAll(set<string> &set1, set<string> &set2);
    void removeAll(set<string> &set1, set<string> &set2);
    set<string> addAll(set<string> &set1, set<string> &set2);

/*Variables*/   
private:    
    string dataFileName;
    map<long, set<string> > textDatabase;   //事務資料庫
    float minSup;                           //最小支援度,(使用絕對支援度)
    long textDatabaseCount;                 //事務資料庫中的事務數
    map< set< set<string> >, int > freqItemSet;             //候選項集集合
    map< set< set<string> >, int > candidateItemSet;        //頻繁項集集合
};

#endif
apriori.cpp
#include "apriori.h"

void Apriori::printMapSet(map< set<string> ,int> &mapSet)
{
    map< set<string>, int >::iterator it = mapSet.begin();
    while(it != mapSet.end()){
        set<string>::iterator itSet = it->first.begin();
        cout << "[" ;
        while(itSet != it->first.end()){
            cout << *itSet << "," ;
            ++itSet;
        }
        cout << "]" << "  " << it->second << endl;
        ++it;
    }
}
void Apriori::printsetSet(set< set<string> > &setSet)
{
    set< set<string> >::iterator c2It = setSet.begin();
    while(c2It != setSet.end()){
       set<string>::iterator ckSetIt = (*c2It).begin();
       cout << "[";
        while(ckSetIt != (*c2It).end()){
            cout << *ckSetIt << "," ;
            ++ckSetIt;
        }
        cout << "]"<< endl;
        ++c2It;            
    }
}
void Apriori::printSet(set<string> &setS)
{
    set<string>::iterator setIt = setS.begin();
    cout << "[";
    while(setIt != setS.end()){
        cout <<*setIt << "," ;
        ++setIt;
    }
    cout << "]" << endl;
}

//---------------------------------------------------------
//將文字資料存入到Map中,產生事務資料庫D,即textDataBase
//---------------------------------------------------------
int Apriori::buildData()
{
    /*開啟文字檔案*/
    ifstream inFile;
    inFile.open(dataFileName.c_str());
    if(!inFile){
        cerr << "open " <<dataFileName << "is failed!" << endl;
        return EXIT_FAILURE;
    }
    /*讀取文字行*/
    string textline;
    vector<string> lines_of_text;

    while(getline(inFile,textline))
        lines_of_text.push_back(textline);
    /*產生事務資料庫*/
    int line_num ;
    for(line_num = 0; line_num != lines_of_text.size(); ++line_num){    
        istringstream line(lines_of_text[line_num]);
        string word;    
        while(line >> word){            
            textDatabase[line_num].insert(word);
        }
    }       
    textDatabaseCount = textDatabase.size();
    cout << "textDatabaseCount: " << textDatabaseCount << " " << line_num<< endl;
    return EXIT_SUCCESS;
}

//-------------------------------------------------------------------------
//獲取候選1項集
//-------------------------------------------------------------------------
map<string, int> Apriori::getCandidate1ItemSet()
{
    map<string, int> candidate1ItemSetTemp;
    map<long, set<string> >::iterator mapIter = textDatabase.begin();
    set<string>::iterator setIter = mapIter->second.begin();

    while(mapIter != textDatabase.end()){
        while(setIter != mapIter->second.end()){
            pair<map<string, int>::iterator, bool> ret = 
                candidate1ItemSetTemp.insert(make_pair(*setIter,1));
            if(!ret.second)
                ++ret.first->second;
            ++setIter;
        }
        ++mapIter;
        setIter = mapIter->second.begin();
    }
    return candidate1ItemSetTemp;
}

//-------------------------------------------------------------------------
//獲取頻繁1項集
//-------------------------------------------------------------------------
map< set<string>, int > Apriori::findFrequent1Itemsets()
{
    set<string> freq1Key;
    map< set<string>, int > freq1ItemSetMap;
    map<string, int> candidate1ItemSet = getCandidate1ItemSet();
    map<string, int>::iterator candIt = candidate1ItemSet.begin();
    while(candIt != candidate1ItemSet.end()){
        if(candIt->second >= minSup){
            freq1Key.erase(freq1Key.begin(),freq1Key.end());
            freq1Key.insert(candIt->first);
            freq1ItemSetMap[freq1Key] = candIt->second;
        }
        ++candIt;
    }
    return freq1ItemSetMap;
}

//-------------------------------------------------------------------------
//根據頻繁k-1項集鍵集獲取頻繁k項集
//k>1
//-------------------------------------------------------------------------
map< set<string>, int > Apriori::getFreqKItemSet(int k, set< set<string> > freqMItemSet)
{
    map< set<string>, int > freqKItemSetMap;
    map< set<string>, int> candFreqKItemSetMap;    
    set< set<string> > candFreqKItemSet = aprioriGen(k-1, freqMItemSet);

    //效率是根據min_sup的值的大小決定的,大,效率高,小效率高
    map<long, set<string> >::iterator mapIter = textDatabase.begin();
    //下面的while迴圈效率很低
    while(mapIter != textDatabase.end()){
        set<string> itValue = mapIter->second;
        set< set<string> >::iterator kit = candFreqKItemSet.begin();
        while(kit != candFreqKItemSet.end()){
            set<string> kSet = *kit;
            set<string> setTemp(kSet.begin(),kSet.end());
            removeAll(setTemp,itValue);            
            if(setTemp.size() == 0){                
                pair< map< set<string>, int >::iterator ,bool > ret = 
                            candFreqKItemSetMap.insert(make_pair(kSet,1));
                if(!ret.second)
                    ++ret.first->second;                    
            }
            ++kit;
        }
        ++mapIter;
    }

    map< set<string>, int>::iterator candIt = candFreqKItemSetMap.begin();

    while(candIt != candFreqKItemSetMap.end()){
        if(candIt->second >= minSup){            
            freqKItemSetMap[candIt->first] = candIt->second;
        }
        ++candIt;
    }

    return freqKItemSetMap;    
}

//-------------------------------------------------------------------------
//取交集
//-------------------------------------------------------------------------

            
           

相關推薦

資料---頻繁挖掘Apriori演算法C++實現

1 準備 2 作業粗糙翻譯內容 2.1 前言 程式設計作業可能比書面作業花費更多的時間,而這也算是你最後成績的10%,所以請提前開始; 這是個人作業,你可以與你的同學或者老師交流,但是不能夠共享程式碼和抄襲; 類似的庫或頻繁模式挖掘演算

海量資料MMDS week2: 頻繁挖掘 Apriori演算法的改進:基於hash的方法

海量資料探勘Mining Massive Datasets(MMDs) -Jure Leskovec courses學習筆記之關聯規則Apriori演算法的改進:基於hash的方法:PCY演算法, Multistage演算法, Multihash演算法 Apriori演

海量資料MMDS week2: 頻繁挖掘 Apriori演算法的改進:非hash方法

海量資料探勘Mining Massive Datasets(MMDs) -Jure Leskovec courses學習筆記之關聯規則Apriori演算法的改進:非hash方法 - 大資料集下的頻繁項集:挖掘隨機取樣演算法、SON演算法、Toivonen演算法 Apri

頻繁挖掘Apriori演算法及其Python實現

Apriori演算法是通過限制候選產生髮現頻繁項集。 Apriori演算法使用一種稱為逐層搜尋的迭代方法,其中k項集用於探索(k+1)項集。首先,通過掃描資料庫,累計每個項的計數,並收集滿足最小支援度的項,找出頻繁1項集的集合,記為L1。然後,使用L1找出頻繁

海量資料MMDS week2: Association Rules關聯規則與頻繁挖掘

海量資料探勘Mining Massive Datasets(MMDs) -Jure Leskovec courses學習筆記之association rules關聯規則與頻繁項集挖掘 {Frequent Itemsets: Often called "associatio

資料之關聯規則挖掘Apriori演算法實現

演算法細節見論文:Fast Algorithm for Mining Association Rules 控制檯版本C++程式碼如下: #include <iostream> #include <sstream> #include <fs

資料筆記六】挖掘頻繁模式、關聯和相關性:基本概念和方法

6.挖掘頻繁模式、關聯和相關性:基本概念和方法 頻繁模式(frequent pattern)是頻繁地出現在資料集中的模式。 6.1 基本概念 頻繁模式挖掘搜尋給定資料集中反覆出現的聯絡,旨在發現大型事務或關係資料集中項之間有趣的關聯或相關性,其典型例子就是購物籃分析。 購物

頻繁挖掘apriori和fp-growth

Apriori和fp-growth是頻繁項集(frequent itemset mining)挖掘中的兩個經典演算法,主要的區別在於一個是廣度優先的方式,另一個是深度優先的方式,後一種是基於前一種效率較低的背景下提出來的,雖然都是十幾年前的,但是理解這兩個演算法對資料探勘

頻繁挖掘演算法——Apriori演算法

前言        關聯規則就是在給定訓練項集上頻繁出現的項集與項集之間的一種緊密的聯絡。其中“頻繁”是由人為設定的一個閾值即支援度 (support)來衡量,“緊密”也是由人為設定的一個關聯閾值即置信度(confidence)來衡量的。這兩種度量標準是頻繁項集挖掘中兩個至關

資料之關聯規則挖掘Apriori演算法

一、概述本篇博文主要闡述資料探勘相關的關聯規則挖掘的演算法(Apriori演算法)。主要介紹關聯規則的基本概念、Apriori演算法原理和Apriori演算法例項,文章末尾處附加Apriori演算法源程式。二、關聯規則挖掘的基本概念關聯規則挖掘發現大量資料中項集之間有趣的關聯

python資料實戰筆記——文字挖掘(1):語料庫構建

什麼是文字挖掘 ?   文字挖掘是抽取有效、新穎、有用、可理解的、散佈在文字檔案中的有價值知識,並且利用這些知識更好地組織資訊的過程。 一、搭建語料庫 語料庫:要進行文字分析的所有文件的集合。 需要用到的模組:os、os.path、codecs、pandas 程

python資料實戰筆記——文字挖掘(4):詞雲繪製

概念: 詞雲:詞雲是指對文字中詞頻較高的分詞,給予視覺上的突出,形成“關鍵詞渲染”,從而過濾掉大量的文字資訊,使瀏覽者一眼掃過就可以領略文字的主旨。 需要用到的包:wordcloud、matplotlib wordcloud包下載地址:http://www.l

資料分析工具

大資料時代需要大資料探勘,我習慣把大資料分成四個領域:資料科學、網路科學、空間地理科學和視覺化技術。 最近的主要興趣在空間地理領域,學習如何獲取POI,Polygon,經緯度,空間匹配演算法和視覺化,一個全新領域有帶來諸多大資料分析工具的思考和整合。 恰巧看到一篇國外部落格列舉了大資料領域的

R語言包arules進行頻繁挖掘的最簡單例子

arules是進行頻繁項集挖掘(frequent itemset mining)的有效工具,不過我在使用的時候發現網上很多例子都比較繁瑣,這裡總結一下其中apriori方法的最簡單使用方法,這裡首先給出程式碼:files_change<-read.transaction

關聯規則(頻繁)——Apriori

1.該問題最初是對“購物籃”提出來的,著名例子是“尿布與啤酒”。 2.相關概念:  關聯規則的支援度:Support(A,B)=包含A和B的事務數/事務總數  關聯規則的置信度:Confidence(A,B)= 包含A和B的事務數/包含A事務數  頻繁項集:項集的頻率大於等

頻繁挖掘演算法——Eclat演算法

        前面介紹過的Apriori演算法和FP-growth演算法都是從TID項集格式(即{TID:itemset})的事務集中挖掘頻繁模式,其中TID是事務識別符號,而itemset是事務TID中購買的商品。這種資料格式稱為水平資料格式。或者,資料也可以用項-TID

資料——基於sklearn包的分類演算法小結

           目錄 一、分類演算法簡介 二、KNN演算法 三、貝葉斯分類演算法 四、決策樹演算法 五、隨機森林演算法 六、SVM演算法      一、分類演算法簡介 1、概念   1.1 監督學習(Super

資料筆記-聚類-KMeans-原理與簡單實現

K中心點演算法(K-medoids)提出了新的質點選取方式,而不是簡單像k-means演算法採用均值計演算法。在K中心點演算法中,每次迭代後的質點都是從聚類的樣本點中選取,而選取的標準就是當該樣本點成為新的質點後能提高類簇的聚類質量,使得類簇更緊湊。該演算法使用絕對誤差標準來定義一個類簇的緊湊程度。 如果

【python資料課程】十四.Scipy呼叫curve_fit實現曲線擬合

        前面系列文章講過各種知識,包括繪製曲線、散點圖、冪分佈等,而如何在在散點圖一堆點中擬合一條直線,也變得非常重要。這篇文章主要講述呼叫Scipy擴充套件包的curve_fit函式實現曲線擬

資料筆記-聚類-Canopy-原理與簡單實現

Canopy聚類演算法是一個將物件分組到類的簡單、快速、精確地方法。每個物件用多維特徵空間裡的一個點來表示。這個演算法使用一個快速近似距離度量和兩個距離閾值 T1>T2來處理。基本的演算法是,從一個點集合開始並且隨機刪除一個,建立一個包含這個點的Canopy,並在