1 准备

  首先实现这个算法是基于中南大学软件学院数据挖掘课的上机作业。作业(全英文)下载地址:http://download.csdn.net/detail/freeape/9188451

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(作业已经给了,请注意:在这次作业中,我们不会直接使用这个文件,但是你可以自行查看这个文件里的内容,它里面最原始的标题是什么样的)。每一行包含两栏,每篇论文的PaperIDTitle,然后用制表符('\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)

  你能想出其他的过滤/排名标准来提高你的“挖掘”的短语列表的质量吗?执行你的算法,把你的分析放在你的报告中。
  (提示:一些相关的论文描述的策略来处理这个问题,通过平衡最大模式和封闭模式。CATHY: http://www.cs.uiuc.edu/~hanj/pdf/kdd13_cwang.pdf KERT: http://arxiv.org/abs/1306.0271. )

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.txtclosed                     |--------closed-0.txt~closed-4.txt<Re-rank source files>purity                     |------purity0.txt~purity-4.txtreport.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_iteratorloc = 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){//绑定行字符串到istringstreamistringstream line(lines_of_text[line_num]);string word;//循环从行字符串读取单词到string类型wordout << 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){//绑定行字符串到istringstreamistringstream line(lines_of_text[line_num]);string word;//循环从行字符串读取单词到string类型wordwhile(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.txtcerr << "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.txtVocabProcess tq;tq.read_file(infile);tq.write_vocab(outVocab,argv[2]);//将vocab.txt转换成title.txttq.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 包含于 Ctc.count++;}//返回候选项集中不小于最小支持度的项集Lk ={c 属于 Ck | c.count>=min_sup}
}
Return L= 所有的频繁集;
第一步:连接(join)
Procedure apriori_gen (Lk-1 :frequent(k-1)-itemsets)For each 项集 l1 属于 Lk-1For each 项集 l2 属于 Lk-1If( (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 ) thendelete 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 cIf s 不属于 Lk-1 thenReturn 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;
}//-------------------------------------------------------------------------
//取交集
//-------------------------------------------------------------------------
set<string> Apriori::retainAll(set<string> &set1, set<string> &set2)
{set<string>::iterator set1It = set1.begin();    set<string> retSet;while(set1It != set1.end()){set<string>::iterator set2It = set2.begin();while(set2It != set2.end()){if((*set1It) == (*set2It)){retSet.insert(*set1It);break;}++set2It;}        ++set1It;}return retSet;
}//-------------------------------------------------------------------------
//返回set1中去除了set2的数据集
//-------------------------------------------------------------------------
void Apriori::removeAll(set<string> &set1, set<string> &set2)
{set<string>::iterator set2It = set2.begin(); while(set2It != set2.end()){set1.erase(*set2It);++set2It;if(set1.size() == 0)break;}
}//-------------------------------------------------------------------------
//取并集
//-------------------------------------------------------------------------
set<string> Apriori::addAll(set<string> &set1, set<string> &set2)
{set<string>::iterator set1It = set1.begin();  set<string>::iterator set2It = set2.begin();  set<string> retSet(set1.begin(),set1.end());while(set2It != set2.end()){retSet.insert(*set2It);++set2It;}return retSet;
}//-------------------------------------------------------------------------
//根据频繁(k-1)项集获取候选k项集
//m = k-1
//freqMItemSet:频繁k-1项集
//-------------------------------------------------------------------------
set< set<string> > Apriori::aprioriGen(int m, set< set<string> > &freqMItemSet)
{set< set<string> > candFreqKItemSet;set< set<string> >::iterator it = freqMItemSet.begin();set<string> originalItemSet;set<string> identicalSetRetain;cout << "aprioriGen start" <<endl;while(it != freqMItemSet.end()){ originalItemSet = *it;/*itr其实就是当前it自加一次所指*/        set< set<string> >::iterator itr = ++it;while(itr != freqMItemSet.end()){set<string> identicalSet(originalItemSet.begin(),originalItemSet.end());            set<string> setS(*itr);            identicalSetRetain.erase(identicalSetRetain.begin(),identicalSetRetain.end());identicalSetRetain = addAll(identicalSet,setS);//是取originalItemSet和setS的交集if(identicalSetRetain.size() == m+1){if(!has_infrequent_subset(identicalSetRetain, freqMItemSet))                    candFreqKItemSet.insert(identicalSetRetain);}          ++itr;}}cout << "aprioriGen end" <<endl;return candFreqKItemSet;
}//-------------------------------------------------------------------------
//使用先验知识,剪枝。删除候选k项集中存在k-1项的子集
//-------------------------------------------------------------------------
bool Apriori::has_infrequent_subset(set<string> &candKItemSet, set< set<string> > &freqMItemSet)
{int occurs = 0;if(freqMItemSet.count(candKItemSet))return true;return false;
}
//-------------------------------------------------------------------------
//获取mapSet的键值,存放于set中
//-------------------------------------------------------------------------
set< set<string> > Apriori::keySet(map< set<string>, int > &mapSet)
{map< set<string>, int >::iterator it = mapSet.begin();set< set<string> > retSet;while(it != mapSet.end()){retSet.insert(it->first);++it;}return retSet;
}
main.cpp
//---------------------------------------------------
//创建日期: 2015-10-14
//修改日期: 2015-10-16
//作    者: yicm
//版    本:
//说    明: Apriori算法C++实现。本实现尽可能地去提高运行效率了,
//          在aprioriGen函数中运行时间是跟min_sup有关的,min_sup越
//          大则运行时间越短,min_sup越小则运行时间越长;在getFreqKItemSet
//          函数中运行时间主要都消耗在扫描事务数据库,并统计每个候选的个数。
//---------------------------------------------------#include <iostream>
#include "apriori.h"int main(int argc,char *argv[])
{if(argc < 2 || argc >4){cout << "usage: apriori.exe [min_sup] [data.txt]" << endl;return 0;}int min_sup = atoi(argv[1]);Apriori apriori(argv[2],min_sup);/*获取文本文件中原始数据*/apriori.buildData();
#if (1)map<int, set< set<string> > > L; map< set<string>, int > freq1ItemSetMap = apriori.findFrequent1Itemsets();    set< set<string> > freqKItemSet = apriori.keySet(freq1ItemSetMap);L.insert(make_pair(1,freqKItemSet));    //for循环退出条件为:得到频繁k项集为空集时for(int k = 2; ;++k){cout << "k= " << k <<endl;        map< set<string>, int> freqKItemSetMap = apriori.getFreqKItemSet(k, freqKItemSet);if(freqKItemSetMap.size() != 0) {set< set<string> > freqKItemSetTemp = apriori.keySet(freqKItemSetMap);L.insert(make_pair(k,freqKItemSetTemp));freqKItemSet = apriori.keySet(freqKItemSetMap);            }else {cout << "k= " << k <<endl;break;}}//打印所有满足min_sup的频繁集    map<int, set< set<string> > >::iterator allLIt = L.begin();while(allLIt != L.end()){cout << "频繁k" << allLIt->first << "项集: " << endl;apriori.printsetSet(allLIt->second);++allLIt;}
#endif#if (0)/*获取文本文件中原始数据*/apriori.buildData();cout << "----------------" << endl;/*获取候选集1*/map<string, int> candidate1ItemSet = apriori.getCandidate1ItemSet();cout << "候选1项集大小: " << candidate1ItemSet.size() << endl;/*获取频繁项集1*/map< set<string>, int > freq1ItemSetMap = apriori.findFrequent1Itemsets();cout << "频繁1项集大小: " << freq1ItemSetMap.size() << endl;/*打印频繁项集1*/cout << "-频繁1项集-" << endl;apriori.printMapSet(freq1ItemSetMap);/*获取候选集2*/set< set<string> > C2 = apriori.aprioriGen(1, apriori.keySet(freq1ItemSetMap));    cout << "-候选2项集-" << endl;apriori.printsetSet(C2);/*获取频繁2项集*/set< set<string> > C1 = apriori.keySet(freq1ItemSetMap);cout << "-频繁1项集键集--" << endl;apriori.printsetSet(C1);map< set<string>, int> L2 = apriori.getFreqKItemSet(2,C1);cout << "---频繁2项集----" << endl;apriori.printMapSet(L2);/*获取频繁3项集*/map< set<string>, int> L3 = apriori.getFreqKItemSet(3,C2);cout << "---频繁3项集----" << endl;apriori.printMapSet(L3);
#endif    return 0;
}

3.3 Apriori算法实现测试

  test.txt文件内容如下:

AA BB  EE
BB DD
BB CC
AA BB DD
AA CC
BB CC
AA CC
AA BB CC EE
AA BB CC

  以min_sup=2为例:


  频繁4项集为空集跳出循环。

数据挖掘---频繁项集挖掘Apriori算法的C++实现相关推荐

  1. 数据挖掘: 频繁项集挖掘(购物篮问题)

    大家恐怕都听说过著名的啤酒与尿布, 这是典型的购物篮问题, 在数据挖掘界叫做频繁项集(Frequent Itemsets). note: 数据类型写法按照Python的格式. 一. 目标与定义 1. ...

  2. python 频繁项集_关联分析之发现频繁项集--使用Apriori算法(1)

    从大规模数据集中寻找物品间的隐含关系被称为关联分析,最有名的案例应该是啤酒和尿布了.这些关系可以用两种方式来量化,一个是使用频繁项集,给出经常在一起出现的元素项:一个是关联规则,每条关联规则意味着元素 ...

  3. 关联分析/频繁项集挖掘:Apriori算法

    简介 Apriori是一种流行的算法,用于在关联规则学习中提取频繁项集.Apriori算法被设计用于对包含交易的数据库进行操作,例如商店客户的购买.如果项目集满足用户指定的支持阈值,则该项目集被视为& ...

  4. 频繁项集挖掘之apriori和fp-growth

    Apriori和fp-growth是频繁项集(frequent itemset mining)挖掘中的两个经典算法,主要的区别在于一个是广度优先的方式,另一个是深度优先的方式,后一种是基于前一种效率较 ...

  5. 频繁项集挖掘算法在告警关联中的应用

    # 技术黑板报 # 第十期 推荐阅读时长:15min 在上一篇技术黑板报中,我们介绍了频繁项集挖掘这一问题,并讲解了Apriori算法与FP-Growth算法的技术原理.本期技术黑板报我们将主要围绕频 ...

  6. java频繁项集挖掘数据关联_数据挖掘之关联分析二(频繁项集的产生)

    频繁项集的产生 格结构(lattice structure)常常用来表示所有可能的项集. 发现频繁项集的一个原始方法是确定格结构中每个候选项集的支持度.但是工作量比较大.另外有几种方法可以降低产生频繁 ...

  7. 数据挖掘之Apriori频繁项集挖掘

    本文的代码文件原件可以在我们的 "数据臭皮匠" 中输入"第六章1" 拿到 1.基本概念介绍 频繁项集和关联规则的挖掘首先需要了解一些概念, 如支持度, 置信度, ...

  8. 频繁项集挖掘算法——Apriori算法

    前言 关联规则就是在给定训练项集上频繁出现的项集与项集之间的一种紧密的联系.其中"频繁"是由人为设定的一个阈值即支持度 (support)来衡量,"紧密"也是由 ...

  9. 数据频繁项集挖掘算法

    ** Apriori算法 ** Apriori 采用广度优先的搜索方式,缩小搜索空间用到了一个称为apriori的性质,其性质为:频繁项集的所有非空子集必然也是频繁的.这是很显然的,比如 同时包含项A ...

最新文章

  1. oracle test就死,简单说明Oracle数据库中对死锁的查询及解决方法
  2. Spring Cloud Alibaba基础教程:几种服务消费方式(RestTemplate、WebClient、Feign)
  3. vue-cli构建的vue项目中引入stylus文件
  4. python函数应用_Python 函数及其应用
  5. C语言实现AVL树(附完整源码)
  6. Linux系统基础.作业
  7. koa2 导出excel表格设置样式_一调整Excel表格的行列宽度,图片又得重新调整?点这个设置就行...
  8. Web APIs概念详解(附图解)
  9. 网站压力测试工具,不用安装,在线进行【强烈推荐】
  10. 约束rmq_约束RMQ
  11. hightopo学习笔记--2D编辑器使用
  12. 拦截图片代码 精易web浏览器_精易Web浏览器 UChk验证源码
  13. python循环中释放内存的方法_我怎样才能在Python中明确释放内存?
  14. 带上问题来看:主流技术Java、Python怎么?基本功又该怎么学?(什么是MySQL、Linux、算法?又该怎么用?)
  15. rasa_nlu踩坑经历
  16. 为什么有的公司会规定所有接口都用 POST请求?
  17. CSS,HTML,JS 以及Vue前端面试题八股文总结【看完你就变高手】
  18. 手把手教你阿里云如何进行网站备案(图文教程)
  19. 罕见病、新药最新研究进展(2021年9月)
  20. 转载:从法律和商业角度看鸿蒙和安卓的关系

热门文章

  1. uni-app 小程序使用腾讯地图完成搜索功能
  2. 《中国网络安全产业分析报告(2020年)》全文发布
  3. Windows 安装 cygwin 详细步骤
  4. 也许是目前最全的计算机的网络模型讲解
  5. ROS配置MAC地址过滤
  6. MD5数字签名算法:生成签名和验签(附代码)
  7. 疾病研究:8岁男童成为美国首位接受基因治疗的杜氏进行性肌营养不良患者
  8. 空间计量xsmle命令遇到(3200)error,
  9. 人生三大主题:健康 财富 情感
  10. 计算机毕业设计之java+ssm医院资产管理系统