Apriori算法学习心得

来源:互联网 发布:如何找到监控软件 编辑:程序博客网 时间:2024/05/21 09:39

Apriori算法是我在数据挖掘方面接触的第一个算法,一直只知道他的大体步骤,对具体细节和代码实现知道的不是很清楚,最近有点时间就重新复习了这个算法。

首先复习一下Apriori算法的基本思路:Apriori算法的基本过程是:扫描一遍数据库,得到一阶频繁项;用一阶频繁项构造二阶候选项;扫描数据库对二阶候选项进行计数,删除其中的非频繁项,得到二阶频繁项;然后构造三阶候选项,以此类推,直到无法构造更高阶的候选项,或到达频繁项集的最大长度限制。其中,Apriori算法利用这样一条性质:任一频繁项集的所有非空子集也必须是频繁的。因为假如P(I)< 最小支持度阈值,当有元素A添加到I中时,结果项集(A∩I)不可能比I出现次数更多,因此A∩I也不是频繁的。

Apriori算法采用连接步和剪枝步两种方式来找出所有的频繁项集。
1)  连接步
为找出Lk(所有的频繁k项集的集合),通过将Lk-1(所有的频繁k-1项集的集合)与自身连接产生候选k项集的集合。候选集合记作Ck。设l1和l2是Lk-1中的成员。记li[j]表示li中的第j项。假设Apriori算法对事务或项集中的项按字典次序排序,即对于(k-1)项集li,li[1]<li[2]<……….<li[k-1]。将Lk-1与自身连接,如果(l1[1]=l2[1])&&( l1[2]=l2[2])&&……..&& (l1[k-2]=l2[k-2])&&(l1[k-1]<l2[k-1]),那认为l1和l2是可连接。连接l1和l2 产生的结果是{l1[1],l1[2],……,l1[k-1],l2[k-1]}。
2)  剪枝步
CK是LK的超集,也就是说,CK的成员可能是也可能不是频繁的。通过扫描所有的事务(交易),确定CK中每个候选的计数,判断是否小于最小支持度计数,如果不是,则认为该候选是频繁的。为了压缩Ck,可以利用Apriori性质:任一频繁项集的所有非空子集也必须是频繁的,反之,如果某个候选的非空子集不是频繁的,那么该候选肯定不是频繁的,从而可以将其从CK中删除。

感觉只看原理很难真正的理解算法,下面是C++的代码实现:

// Struct.h文件#ifndef STRUCT_H#define STRUCT_H#include <string>#include <vector>#include <algorithm>using namespace std;struct Item{    string key;    int value;};struct MutiItem{    vector<string> key;    int value;};#endif

//Compare.h 文件#ifndef COMPARE_H#define COMPARE_H#include "Struct.h"MutiItem mutiTemp;bool unaryPred(MutiItem &item){    vector<string> vec1=mutiTemp.key;    vector<string> vec2=item.key;    int count=0;    for(vector<string>::const_iterator iter=vec2.begin();iter!=vec2.end();++iter)    {        if(find(vec1.begin(),vec1.end(),*iter)<vec1.end())            ++count;    }    return count==vec1.size();}#endif

//Apriori.h 文件#ifndef APRIORI_H#define APRIORI_H#include "Struct.h"#include <fstream>#include <map>#include <iostream>#include <algorithm>#include <string>using namespace std;class Apriori{private:    int Min_support;//最小支持度    vector<string> vec_str;//保存从文件中读取的字符串    map<string,int> map_str_int;//一项备选集    vector<Item> vec_item;//一项频繁集    vector<MutiItem> vec_mutiItem_pre;//高项备选集    vector<MutiItem> vec_mutiItem;//高项频繁集public:    Apriori(void);    ~Apriori(void);    //从文件中读取每一行字符串存入向量vec_str中    void ReadFile(ifstream &infile, const string &filename, const char &separator='\\');    //统计一项备选集支持度    void CountWord(const char &separator='\\');    //生成一项频繁集    void Generate_1ItemSets();    //生成二项频繁项备选集    void GenerateAlternative2();    //生成高项频繁项备选集    void GenerateAlternative();    //统计备选集支持度    void CountSupport();    //生成高项频繁集    void Generate_ItemSets();    //生成所有项频繁集    void Generate_AllItemSets(ostream &outfile);    //输出一项频繁集到文件中    void Ouput1(ostream &outfile);    //输出高项频繁集到文件中    void Ouput(ostream &outfile);};#endif

// apriori.cpp 文件#include "Apriori.h"#include "Compare.h"Apriori::Apriori(void){    this->Min_support=5;//默认最小支持度}Apriori::~Apriori(void){}//从文件中读取每一行字符串存入向量vec_str中void Apriori::ReadFile(std::ifstream &infile, const std::string &filename, const char &separator){    infile.open(filename.c_str());//打开文件    if(!infile)    {        cout<<"Open file failed!"<<endl;        exit(1);    }    string word;    while(getline(infile,word))//每次从文件中读取一行字符串存入word中        this->vec_str.push_back(word);//在vec_str的末尾增加一个值为word的元素。    infile.close();}//统计一项备选集支持度void Apriori::CountWord(const char &separator){    string sentence,word;    for(vector<string>::const_iterator iter=this->vec_str.begin();iter!=this->vec_str.end();++iter)    {        sentence=*iter;//取出一行字符串        //分隔词语        while(sentence.find(separator) != -1)        {            word=sentence.substr(0, sentence.find(separator));            ++this->map_str_int[word];            sentence=sentence.substr(sentence.find(separator)+1, sentence.size()-1);        }        ++this->map_str_int[sentence];    }}//生成一项频繁集void Apriori::Generate_1ItemSets() {    Item item;    for(map<string,int>::const_iterator iter=this->map_str_int.begin();iter!=this->map_str_int.end();++iter)    {        //挑选支持度大于等于最小支持度的项        if(iter->second >= this->Min_support)        {            item.key=iter->first;            item.value=iter->second;            this->vec_item.push_back(item);        }    }}//生成二项频繁项备选集void Apriori::GenerateAlternative2(){    vector<string> vecTemp;    for(vector<Item>::const_iterator iter=this->vec_item.begin();iter!=this->vec_item.end()-1;++iter)    {        vecTemp.push_back(iter->key);        for(vector<Item>::const_iterator iter2=iter+1;iter2!=this->vec_item.end();++iter2)        {            vecTemp.push_back(iter2->key);            mutiTemp.key=vecTemp;            mutiTemp.value=0;            this->vec_mutiItem_pre.push_back(mutiTemp);//添加到二项频繁项备选集            vecTemp.pop_back();        }        vecTemp.clear();    }}//生成高项频繁项备选集void Apriori::GenerateAlternative(){    vector<string> vecTemp;//低项频繁项元素    int count;    vector<string>::const_iterator iterTemp;    this->vec_mutiItem_pre.clear();//将备选集清空    for(vector<MutiItem>::const_iterator iter=this->vec_mutiItem.begin();iter!=this->vec_mutiItem.end()-1;++iter)    {        vecTemp=iter->key;        for(vector<MutiItem>::const_iterator iter2=iter+1;iter2!=this->vec_mutiItem.end();++iter2)        {            count=0;            for(vector<string>::const_iterator iter3=iter2->key.begin();iter3!=iter2->key.end();++iter3)                if(find(vecTemp.begin(),vecTemp.end(),*iter3)==vecTemp.end())                {                    ++count;                    iterTemp=iter3;                }                if(count!=1)//判断两个频繁项是否只有一个元素不相等                    continue;                vecTemp.push_back(*iterTemp);                mutiTemp.key=vecTemp;                mutiTemp.value=0;                //判断备选集中是否已有此元素                if(count_if(this->vec_mutiItem_pre.begin(),this->vec_mutiItem_pre.end(),unaryPred)==0)                    this->vec_mutiItem_pre.push_back(mutiTemp);                vecTemp.pop_back();        }        vecTemp.clear();    }}//统计备选集支持度void Apriori::CountSupport(){    bool flag;    //迭代行字符串    for(vector<string>::const_iterator iter=this->vec_str.begin();iter!=this->vec_str.end();++iter)    {        //迭代备选集元素        for(vector<MutiItem>::iterator iter2=this->vec_mutiItem_pre.begin();iter2!=this->vec_mutiItem_pre.end();++iter2)        {            flag=true;            for(vector<string>::const_iterator iter3=iter2->key.begin();iter3!=iter2->key.end();++iter3)            {                if(iter->find(*iter3)==-1)                {                    flag=false;                    break;                }            }            if(flag==true)                ++iter2->value;        }    }}//生成高项频繁集void Apriori::Generate_ItemSets(){    this->vec_mutiItem.clear();    for(vector<MutiItem>::const_iterator iter=this->vec_mutiItem_pre.begin();iter!=this->vec_mutiItem_pre.end();++iter)    {        if(iter->value >= this->Min_support)            this->vec_mutiItem.push_back(*iter);    }}//生成所有项频繁集void Apriori::Generate_AllItemSets(ostream &outfile){    //生成一项频繁集    this->CountWord();    this->Generate_1ItemSets();    this->Ouput1(outfile);    //生成二项频繁项集    this->GenerateAlternative2();    this->CountSupport();    this->Generate_ItemSets();    this->Ouput(outfile);    //循环生成高项频繁集    while(1)    {        this->GenerateAlternative();        this->CountSupport();        this->Generate_ItemSets();        if(this->vec_mutiItem.size()==0)            return;        this->Ouput(outfile);    }}//输出一项频繁集到文件中void Apriori::Ouput1(ostream &outfile){    for(vector<Item>::const_iterator iter=this->vec_item.begin();iter!=this->vec_item.end();++iter)    {        outfile<<iter->key<<":\t"<<iter->value<<endl;    }    outfile<<endl;}//输出高项频繁集到文件中void Apriori::Ouput(ostream &outfile){    for(vector<MutiItem>::const_iterator iter=this->vec_mutiItem.begin();iter!=this->vec_mutiItem.end();++iter)    {        vector<string>::const_iterator iter2=iter->key.begin();        for(;iter2!=iter->key.end()-1;++iter2)            outfile<<*iter2<<"&";        outfile<<*iter2<<":\t"<<iter->value<<endl;    }    outfile<<endl;}

// main.cpp 文件#include "apriori.h"void main(){    Apriori ob;    ifstream infile;    //读取数据集    ob.ReadFile(infile,"in.txt");    ofstream outfile;    outfile.open("out.txt");    //生成所有项频繁集    ob.Generate_AllItemSets(outfile);    outfile.close();    cout<<"Accomplish!"<<endl;}

程序输入:in.txt亲爱\的\你慢慢飞小心\前面\带刺\的\玫瑰亲爱\的\你\张张嘴风中\花香\会\让\你\沉醉亲爱\的\你\跟我飞穿过\丛林\去\看\小溪水亲爱\的\来\跳个舞爱\的\春天\不会\有\天黑我\和\你\缠缠绵绵\翩翩飞飞跃\这\红尘\永相随追逐\你\一生爱恋\我\千回不\辜负\我\的\柔情你\的\美我\和\你\缠缠绵绵\翩翩飞飞跃\这\红尘\永相随等到\秋风尽\秋叶\落成堆能\陪\你\一起\枯萎\也\无悔亲爱\的\你\慢慢飞小心\前面\带刺\的\玫瑰亲爱\的\你\张张嘴风中\花香\会\让\你沉醉亲爱\的\你\跟我飞穿过\丛林\去\看\小溪水亲爱\的\来\跳个舞爱\的\春天\不会\有\天黑我\和\你\缠缠绵绵\翩翩飞飞跃\这\红尘\永相随追逐\你\一生\爱恋\我\千回不\辜负\我\的\柔情你\的\美我\和\你\缠缠\绵绵\翩翩飞飞跃\这\红尘\永\相随等到\秋风尽\秋叶\落成堆能\陪\你\一起枯萎\也\无悔我\和\你\缠缠绵绵\翩翩飞飞跃\这\红尘\永相随等到\秋风尽\秋叶\落成堆能\陪\你\一起\枯萎\也\无悔等到\秋风尽\秋叶\落成堆能\陪\你\一起\枯萎\也\无悔

程序输出:out.txt的:16飞跃:5和:5红尘:5你:17亲爱:8我:9这:5翩翩飞:5的&你:8的&亲爱:8飞跃&红尘:5飞跃&这:5和&你:5和&我:5和&翩翩飞:5红尘&这:5你&亲爱:6你&我:10你&翩翩飞:5我&翩翩飞:5的&你&亲爱:6飞跃&红尘&这:5和&你&我:5和&你&翩翩飞:5和&我&翩翩飞:5你&我&翩翩飞:5和&你&我&翩翩飞:5

仔细研究一下这个代码会发现里面没有剪枝步啊,是,这里面为了程序便于理解,略去了剪枝步,直接用支持度来判断是否满足支持度要求,在剪枝步一般要利用数据结构哈希树提高效率,关于哈希树的知识以后再学吧,先去happy一下了。



0 0
原创粉丝点击