数据挖掘之FP-Tree算法

来源:互联网 发布:铃声编辑软件 编辑:程序博客网 时间:2024/04/24 11:11

算法细节见论文:Mining Frequent Patterns Without Candidata Generation

这篇论文是Jiawei Han的大作,又一个牛逼的中国人去了北美。


图形化界面的工程+测试用例戳这http://download.csdn.net/detail/michealtx/4266155


控制台版C++代码如下:

#include <iostream>#include <sstream>#include <fstream>#include <vector>#include <map>#include <list>#include <string>#include <algorithm>#include <ctime>using namespace std;struct FPNode{string nodeName;unsigned int count;FPNode *parentNode;vector<FPNode *> childNode;FPNode *siblingNode;bool operator < (const FPNode &other) const{return count>other.count;}};//读取文件获取整个数据库存储在database中,fileName必须为char*型,要是用string会报错,in()不认bool ObtainDatabase(vector<vector<string> > &database,char *fileName){ /*   vector<string> data;data.push_back(1);data.push_back(2);data.push_back(5);database.push_back(data);data.clear();data.push_back(2);data.push_back(4);database.push_back(data);data.clear();data.push_back(2);data.push_back(3);database.push_back(data);data.clear();data.push_back(1);data.push_back(2);data.push_back(4);database.push_back(data);data.clear();data.push_back(1);data.push_back(3);database.push_back(data);data.clear();data.push_back(2);data.push_back(3);database.push_back(data);data.clear();data.push_back(1);data.push_back(3);database.push_back(data);data.clear();data.push_back(1);data.push_back(2);data.push_back(3);data.push_back(5);database.push_back(data);data.clear();data.push_back(1);data.push_back(2);data.push_back(3);database.push_back(data);return true;*/ifstream in(fileName);if(!in){cout<<"文件打开失败!"<<endl;return false;}string s="";int i=0;while(getline(in,s)){//读取一行记录i++;vector<string> transaction;int len=s.length();string str="";for(int i=0;i<len;i++){//将记录中的数提取出来if(s[i]!=' '){str+=s[i];}else if(s[i]==' '||i==len-1){transaction.push_back(str);str="";}}database.push_back(transaction);s="";}cout<<i;system("pause");return true;}/////////////////////////////////////////////遍历一遍数据库,创建1-项大项集void CreateItemset(vector<vector<string> > &database,vector<FPNode> &largeItemset,unsigned int minSupport){map<string,int> dir;map<string,int>::iterator dirIt;vector<vector<string> >::iterator databaseIt;vector<string> temp;vector<string>::iterator tempIt;//根据数据库创建字典,字典形式为<item,count>for(databaseIt=database.begin();databaseIt!=database.end();databaseIt++){temp=*databaseIt;for(tempIt=temp.begin();tempIt!=temp.end();tempIt++){string item=*tempIt;dirIt=dir.find(item);if(dirIt==dir.end()){//item不在字典dir中dir.insert(pair<string,int>(item,1));}else{//item在字典dir中,则将其count值加1(dirIt->second)++;}}}//从字典中选出支持度超过minSopport的itemfor(dirIt=dir.begin();dirIt!=dir.end();dirIt++){if(dirIt->second>=minSupport){FPNode large;large.nodeName=dirIt->first;large.count=dirIt->second;large.parentNode=NULL;(large.childNode).clear();large.siblingNode=NULL;largeItemset.push_back(large);}}//根据每个节点的count值对大项集排序sort(largeItemset.begin(),largeItemset.end());}//输出一项大项集void OutputLargeItemset(vector<FPNode> &largeItemset,unsigned int i){cout<<"包含 "<<largeItemset.size()<<" 项的 "<<i<<"-项大项集:"<<endl;vector<FPNode>::iterator largeItemsetIt;int j=0;for(largeItemsetIt=largeItemset.begin();largeItemsetIt!=largeItemset.end();largeItemsetIt++){FPNode temp=*largeItemsetIt;cout<<"{ ";cout<<temp.nodeName<<" : "<<temp.count;cout<<" }";j++;if(j%4==0){cout<<endl;}}cout<<endl<<endl;}////////////////////////////////////////////////////////////对数据库某条交易trans筛选出在1-项大项集large1中出现的项,并按在1-项大项集large1出现的顺序排序,放到tempTrans中void SortItem(vector<string> &trans,vector<string> &tempTrans,vector<FPNode> &large1){unsigned int sizeLarge=large1.size();unsigned int sizeTrans=trans.size();for(int i=0;i<sizeLarge;i++){FPNode tempNode=large1[i];for(int j=0;j<sizeTrans;j++){if(tempNode.nodeName==trans[j]){tempTrans.push_back(tempNode.nodeName);}}}}//利用包括transTemp[k]在内及其以后的值为root节点创建子树int AddNode(FPNode *parentNode,vector<string> &transTemp,unsigned int k,vector<FPNode> &large1){unsigned int size=transTemp.size();if(size>0&&(k>=0&&k<size)){//cout<<"addNode 时父节点为:"<<parentNode->nodeName<<endl;FPNode *nodeTemp=new FPNode;//创建新节点if(nodeTemp==NULL){return 1;}nodeTemp->nodeName=transTemp[k];nodeTemp->count=1;nodeTemp->parentNode=parentNode;nodeTemp->siblingNode=NULL;(parentNode->childNode).push_back(nodeTemp);//将nodeTemp添加到它的兄弟节点上。unsigned int sizeLarge=large1.size();for(int i=0;i<sizeLarge;i++){if((large1[i]).nodeName==nodeTemp->nodeName){FPNode *temp=&(large1[i]);while(temp->siblingNode!=NULL){temp=temp->siblingNode;}temp->siblingNode=nodeTemp;//cout<<"brother";system("pause");break;}}k++;if(k<size){//此交易中还有其它项AddNode(nodeTemp,transTemp,k,large1);}}return 0;}//创建FP-Treeint CreateFPTree(vector<vector<string> > &database,vector<FPNode> &large1,FPNode **treeRoot){*treeRoot=new FPNode;FPNode *root=*treeRoot;if(root==NULL){return 1;}root->nodeName="-1";//-1表示空节点root->count=0;root->parentNode=NULL;(root->childNode).clear();root->siblingNode=NULL;unsigned int sizeDatabase=database.size();for(int i=0;i<sizeDatabase;i++){vector<string> trans=database[i];//取出数据库一条交易vector<string> transTemp;SortItem(trans,transTemp,large1);//把此交易包含于large1中的项筛选出来并排序,放到transTemp中/*//对比输出trans和transTempint da=trans.size();cout<<"trans:";for(int i=0;i<da;i++)cout<<trans[i]<<" ";cout<<endl<<"transTemp:";da=transTemp.size();for(int i=0;i<da;i++)cout<<transTemp[i]<<" ";cout<<endl;//system("pause");*/vector<FPNode *> childNode=root->childNode;//获取根的儿子节点集合//cout<<"此时树根有 "<<childNode.size()<<" 个儿子"<<endl;if(childNode.size()==0){//如果树根的儿子节点集合为空,说明这个树为空。则把此交易转换为树根root的子树。AddNode(root,transTemp,0,large1);//cout<<(root->childNode).size()<<"树不为空"<<endl;}else{//树不为空unsigned int sizeTrans=transTemp.size();FPNode *pt=NULL;for(int i=0;i<sizeTrans;i++){//先查找transTemp[i]是否在树中出现过string temp=transTemp[i];unsigned int sizeChild=childNode.size();int j=0;for(j=0;j<sizeChild;j++){if(temp==(childNode[j])->nodeName){pt=childNode[j];(pt->count)++;//transTemp[i]已经在树中出现过了,则把它的计数器加1//cout<<"此时transTemp[i]为"<<pt->nodeName<<endl;childNode=pt->childNode;break;}}if(j==sizeChild){if(pt==NULL){//transTemp[i]未在树中,则添加包括i节点之后的所有节点,添加到树根上AddNode(root,transTemp,i,large1);}else{//transTemp[i]已在树中出现过,但是它还没儿子节点,所以把它后面的节点挂在它后面AddNode(pt,transTemp,i,large1);//这个地方不是i+1,调试过后才醒悟!}break;}}}}return 0;}///////////////////////////////////////////////////////////////////获取large1除第一项之外的各项的conditional pattern base条件模式基。该过程即是建立每一项对应的子数据库。void CreateConditionalPatterBase(vector<FPNode> &large1,map<string,vector<vector<string> > > &cpbMap){//if(large1.empty()){cout<<"large1 is empty"<<endl;}else{cout<<"large1 is not empty"<<endl;}vector<string> condition;int sizeLarge=large1.size();//large1中除了第一项外(因为直接挂在树根上的节点肯定没有前缀,所以不用求了,免得浪费时间),全部求条件模式基for(int i=sizeLarge-1;i>0;i--){FPNode *sibling=(large1[i]).siblingNode;//if(sibling==NULL)cout<<"*****"<<i<<endl;system("pause");vector<vector<string> > pathSet;pathSet.clear();while(sibling!=NULL){unsigned int count=sibling->count;FPNode *parent=sibling->parentNode;vector<string> path;path.clear();while(parent!=NULL){//逆向向上,把除树根之外的父节点添加到路径path中if(parent->parentNode!=NULL){//parent的父节点为空,说明parent是树根,树根里不含项path.push_back(parent->nodeName);parent=parent->parentNode;}else{break;}}if(!(path.empty())){for(int j=0;j<count;j++){pathSet.push_back(path);}}sibling=sibling->siblingNode;}if(pathSet.size()!=0){cpbMap.insert(pair<string,vector<vector<string> > >((large1[i]).nodeName,pathSet));}}}//////////////////////////////////////////////////////////void DestroyTree(FPNode *root){if(root!=NULL){vector<FPNode *> childNode=root->childNode;unsigned int size=childNode.size();for(int i=0;i<size;i++){DestroyTree(childNode[i]);}delete root;}root=NULL;}//输出FP树void OutputFPTree(FPNode *root){if(root==NULL){//cout<<"root is null"<<endl;return;}//cout<<"root not null"<<endl;vector<FPNode*> childNode=root->childNode;cout<<"{"<<root->nodeName<<","<<root->count<<","<<childNode.size();if(childNode.size()!=0){cout<<"}儿子如下:"<<endl;for(int i=0;i<childNode.size();i++){OutputFPTree(childNode[i]);}}else {cout<<"}无子"<<endl;}}//由每一项的条件模式基得到对应的条件模式FP树,最终得到条件模式FP树字典void CreateCpbFPtree(map<string,vector<vector<string> > > &cpbMap,map<string,FPNode *> &cpbFPTreeMap,unsigned int minSupport){map<string,vector<vector<string> > >::iterator cpbMapIt;for(cpbMapIt=cpbMap.begin();cpbMapIt!=cpbMap.end();cpbMapIt++){string nodeName=cpbMapIt->first;vector<vector<string> > pathSet=cpbMapIt->second;vector<FPNode> cpbLarge;//根据nodeName项对应的条件模式基集合pathSet创建它的频繁一项集cpbLargeCreateItemset(pathSet,cpbLarge,minSupport);//输出刚刚创建的频繁一项集//cout<<"\n\n输出刚刚创建的 "<<nodeName<<" 的频繁一项集"<<endl;//OutputLargeItemset(cpbLarge,1);//if(pathSet.empty()){cout<<"pathSet is null"<<endl;}else{cout<<"pathSet not null"<<endl;}//if(cpbLarge.empty()){cout<<"cpbLarge is null"<<endl;}else{cout<<"cpbLarge not null"<<endl;system("pause");}FPNode *cpbRoot=NULL;if(!(pathSet.empty())&&!(cpbLarge.empty())){CreateFPTree(pathSet,cpbLarge,&cpbRoot);//创建nodeName对应的条件模式FP树//cout<<"\n\n输出刚刚创建的 "<<nodeName<<" 的条件模式FP树"<<endl;//OutputFPTree(cpbRoot);}if(cpbRoot!=NULL){//创建成功vector<FPNode *> *childNode=&(cpbRoot->childNode);vector<FPNode *>::iterator childIt=(*childNode).begin();unsigned int size=(*childNode).size();for(int i=0;i<size;i++){if((*childIt)->count< minSupport){//删掉最小支持度不满足的分支//cout<<endl<<(*childIt)->nodeName<<"   "<<(*childIt)->count<<"EEEEEEEEEEEERROR"<<endl;system("pause");DestroyTree(*childIt);childIt=(*childNode).erase(childIt);//cout<<endl<<(*childIt)->nodeName<<"   "<<(*childIt)->count<<"EEEEEEEEEEEERROR"<<endl;system("pause");}else{childIt++;}}if((*childNode).size()!=0){//如果这个条件模式树树根的儿子节点个数不为0,则把它加到条件模式FP树字典中cpbFPTreeMap.insert(pair<string,FPNode*>(nodeName,cpbRoot));}}}}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////根据每个关键字的条件模式FP树,创建出所有的路径并放到路径集合pathSet中void CreatePath(FPNode *root,vector<list<FPNode> > &pathSet){//vector<list<FPNode> > pathSet=new vector<list<FPNode> >();if(root!=NULL){if((root->childNode).empty()){//root没有分支list<FPNode> path;if(root->parentNode!=NULL){//不能把树根添加到路径中path.push_back(*root);//cout<<"root!=NULL"<<"--"<<root->nodeName<<"   "<<root->count<<"    "<<(root->childNode).size()<<endl;}if(path.size()!=0){pathSet.push_back(path);}}else{//root有分支vector<FPNode *> childNode=root->childNode;unsigned int size=childNode.size();if(size>1){//root有多个分支for(int i=0;i<size;i++){unsigned int count=childNode[i]->count;CreatePath(childNode[i],pathSet);unsigned int cnt=(childNode[i]->childNode).size();if(cnt==0){cnt=1;}if(root->parentNode!=NULL){unsigned int sizePathSet=pathSet.size();for(int j=sizePathSet-cnt;j<sizePathSet;j++){//cout<<"size>1"<<"--"<<root->nodeName<<"   "<<root->count<<"    "<<(root->childNode).size()<<endl;list<FPNode> *lis=&(pathSet[j]);FPNode *node=new FPNode;node->nodeName=root->nodeName;node->count=count;node->parentNode=NULL;lis->push_front(*node);}}}}else{//root只有一个分支for(int i=0;i<size;i++){unsigned int count=childNode[i]->count;CreatePath(childNode[i],pathSet);if(root->parentNode!=NULL){unsigned int sizePathSet=pathSet.size();for(int j=sizePathSet-1;j<sizePathSet;j++){//cout<<"size==1"<<"--"<<root->nodeName<<"   "<<root->count<<"    "<<(root->childNode).size()<<endl;list<FPNode> *lis=&(pathSet[j]);lis->push_front(*root);}}}}}}//return pathSet;}//输出每个项对应的全部前缀路径void OutputPathSet(vector<list<FPNode> > &pathSet){vector<list<FPNode> >::iterator pathSetIt;for(pathSetIt=pathSet.begin();pathSetIt!=pathSet.end();pathSetIt++){list<FPNode> path=*pathSetIt;list<FPNode>::iterator pathIt;for(pathIt=path.begin();pathIt!=path.end();pathIt++){cout<<(*pathIt).nodeName<<" ";}cout<<endl<<endl;}}//根据path创建fp,k初始值为path.size()-1map<list<string>,int>* CreateFP(list<FPNode> &path)//list<FPNode>::reverse_iterator pathIt,int k,vector<vector<string> > &fp){if(path.size()>0){//把路径path的第一个节点加入FPNode *temp=&(path.front());if(!temp){return NULL;}list<string> lis;string start=temp->nodeName;lis.push_back(start);map<list<string>,int> *fp=new map<list<string>,int>;if(!fp){return NULL;}fp->insert(pair<list<string>,int>(lis,temp->count));path.pop_front();map<list<string>,int> *others=CreateFP(path);if(others){map<list<string>,int>::iterator othersIt=others->begin();for(;othersIt!=others->end();othersIt++){fp->insert(pair<list<string>,int>(othersIt->first,othersIt->second));const list<string> *li=&(othersIt->first);list<string> tmp;list<string>::const_iterator liIt=li->begin();while(liIt!=li->end()){tmp.push_back(*liIt);liIt++;}tmp.push_front(start);fp->insert(pair<list<string>,int>(tmp,othersIt->second));}}return fp;}return NULL;}//根据条件模式FP树字典,生成每一个关键字对应的所有频繁模式void ObtainFrequenPattern(map<string,FPNode *> &cpbFPTreeMap,map<string,map<list<string>,int>* > &frequentPatternMap){//if(cpbFPTreeMap.empty()){cout<<"cpbFPTreeMap is empty"<<endl;}map<string,FPNode *>::iterator cpbTreeIt;for(cpbTreeIt=cpbFPTreeMap.begin();cpbTreeIt!=cpbFPTreeMap.end();cpbTreeIt++){string nodeName=cpbTreeIt->first;FPNode *cpbRoot=cpbTreeIt->second;//根据关键字nodeName对应的条件模式树把所有路径择出来放到pathSet中vector<list<FPNode> > pathSet;list<FPNode> path;//输出每个项对应的FP树//cout<<endl<<nodeName<<" 对应的FP树为:"<<endl;//OutputFPTree(cpbRoot);CreatePath(cpbRoot,pathSet);//输出每个项对应的全部前缀路径//cout<<endl<<nodeName<<" 对应的所有前缀路径为:"<<endl;//OutputPathSet(pathSet);system("pause");vector<list<FPNode> >::iterator pathSetIt;for(pathSetIt=pathSet.begin();pathSetIt!=pathSet.end();pathSetIt++){list<FPNode> pathTemp=*pathSetIt;//根据每条路径创建其对应的频繁模式集合,放到fp中map<list<string>,int>* fp=NULL;fp=CreateFP(pathTemp);//cout<<"CreateFP finish"<<endl;if(fp==NULL){//cout<<"fp is null"<<endl;continue;}map<string,map<list<string>,int>* >::iterator fpmIt;fpmIt=frequentPatternMap.find(nodeName);if(fpmIt==frequentPatternMap.end()){//关键字nodeName未在频繁模式字典中出现过,就把它和相应的频繁模式子集集合fp加入进来frequentPatternMap.insert(pair<string,map<list<string>,int>* >(nodeName,fp));}else{map<list<string>,int>::iterator fpIt=fp->begin();unsigned int sizeFp=fp->size();map<list<string>,int>* pt=fpmIt->second;map<list<string>,int>::iterator ptIt=pt->begin();unsigned int sizePt=pt->size();for(;fpIt!=fp->end();++fpIt){//查找fp中的list是否在pt中list表中出现过bool flag=true;int j=0;for(ptIt=pt->begin(),j=0;j<sizePt&&ptIt!=pt->end();++j,++ptIt){list<string>::const_iterator t1It=(fpIt->first).begin();unsigned int sizeT1=(fpIt->first).size();list<string>::const_iterator t2It=(ptIt->first).begin();unsigned int sizeT2=(ptIt->first).size();if(sizeT1!=sizeT2){flag=false;continue;}for(int k=0;k<sizeT1;k++){if(*t1It!=*t2It){flag=false;break;}t1It++;t2It++;if(k==sizeT1-1){flag=true;}}if(flag==true){break;}}if(flag==true){ptIt->second+=fpIt->second;}else{pt->insert(pair<list<string>,int>(fpIt->first,fpIt->second));}}}}}}//输出所有频繁模式void OutputFrequentPattern(map<string,map<list<string>,int>* > &frequentPatternMap,unsigned int minSupport){//if(frequentPatternMap.empty()){cout<<"frequentPatternMap is empty"<<endl;}map<string,map<list<string>,int>* >::iterator it;int geshu=0;for(it=frequentPatternMap.begin();it!=frequentPatternMap.end();it++){geshu++;string nodeName=it->first;map<list<string>,int>* fpSet=it->second;unsigned int size=fpSet->size();map<list<string>,int>::iterator fpSetIt=fpSet->begin();cout<<nodeName<<":"<<endl;for(int i=0;i<size;++i,++fpSetIt){const list<string> *fp=&(fpSetIt->first);unsigned int cnt=fpSetIt->second;if(cnt>=minSupport){unsigned int len=fp->size();list<string>::const_iterator fpIt=fp->begin();cout<<"{ ";for(int j=0;j<len;j++,++fpIt){cout<<*fpIt<<" ";}cout<<nodeName<<" }"<<endl;}}}cout<<geshu<<"---";}//输出每一项的全部条件模式基void OutputConditionalPatterBase(map<string,vector<vector<string> > > &cpbMap){map<string,vector<vector<string> > >::iterator cpbMapIt;for(cpbMapIt=cpbMap.begin();cpbMapIt!=cpbMap.end();cpbMapIt++){string nodeName=cpbMapIt->first;vector<vector<string> > cpbSet=cpbMapIt->second;vector<vector<string> >::iterator cpbSetIt;cout<<nodeName<<"有 "<<cpbSet.size()<<" 个条件模式基:"<<endl;for(cpbSetIt=cpbSet.begin();cpbSetIt!=cpbSet.end();cpbSetIt++){vector<string> cpb=*cpbSetIt;vector<string>::iterator cpbIt;for(cpbIt=cpb.begin();cpbIt!=cpb.end();cpbIt++){cout<<*cpbIt<<" ";}cout<<endl;}}}//输出每一项的条件模式FP树void OutputCpbFPTree(map<string,FPNode *> &cpbFPTreeMap){map<string,FPNode *>::iterator cpbFPTreeMapIt;for(cpbFPTreeMapIt=cpbFPTreeMap.begin();cpbFPTreeMapIt!=cpbFPTreeMap.end();cpbFPTreeMapIt++){string nodeName=cpbFPTreeMapIt->first;FPNode *root=cpbFPTreeMapIt->second;cout<<nodeName<<" 的条件模式FP树为:"<<endl;OutputFPTree(root);}}int main(){char *fileName="retail.dat";int minSupport=6171;//最小支持度     clock_t start=clock();vector<vector<string> > database;//数据库ObtainDatabase(database,fileName); vector<FPNode> large1;CreateItemset(database,large1,minSupport);int k=1;vector<FPNode> largeTemp=large1;//输出一项大项集OutputLargeItemset(largeTemp,k);FPNode *root=NULL;//创建FPTreeCreateFPTree(database,large1,&root);//输出FP树//OutputFPTree(root);//if(root==NULL)cout<<"root is null"<<endl;map<string,vector<vector<string> > > cpbMap;//获取large1除第一项之外的各项的conditional pattern base条件模式基。该过程即是建立每一项对应的子数据库。CreateConditionalPatterBase(large1,cpbMap);//输出每一项的全部条件模式基//OutputConditionalPatterBase(cpbMap);//if(cpbMap.empty()){cout<<"cpbMap is empty"<<endl;}///////////////////////////////////////////////////////////////////////////////////map<string,FPNode *> cpbFPTreeMap;//由每一项的条件模式基得到对应的条件模式FP树,最终得到条件模式FP树字典CreateCpbFPtree(cpbMap,cpbFPTreeMap,minSupport);//输出每一项的条件模式FP树//OutputCpbFPTree(cpbFPTreeMap);map<string,map<list<string>,int>* > frequentPatternMap;//根据条件模式FP数字典,生成每一个关键字对应的所有频繁模式ObtainFrequenPattern(cpbFPTreeMap,frequentPatternMap);//输出所有频繁模式OutputFrequentPattern(frequentPatternMap,minSupport);clock_t end=clock();cout<<"Finish!共用时:"<<(end-start)<<"ms"<<endl;system("pause");}


原创粉丝点击