Apriori算法的C++实现

Apriori是经典的购物篮分析算法。该算法用SQL实现难度较大,所以考虑用C++实现。花了两天,代码如下,原创转载请注明出处

//Apriori.c
#include<iostream>
#include<set>
#include<map>
#include<string>
#include<vector>
using namespace std;
typedef map<set<string>,int> map_s;

map_s Ck ;//候选集Ck
map_s Lk ; //频繁项集Lk
vector<set<string> >  data; //原始数据
vector<set<string> >  L;    //频繁项集合
vector<string>        data2; //分割后的原始数据
int n,m;
int minSup = 2;
int minConf = 0.2;
set<string> s;
string in;
void Delete(map_s &Ck)
{
   for(  map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
   {
      if( l_it->second< minSup)
      {
        Ck.erase(l_it);
      }
   }
}

int compset(set<string> s1,set<string> s2)
{
  int flag=0;
  //判断集合s1是不是s2的子集
  for( set<string>::iterator it=s1.begin(); it!=s1.end();it++ )
  {
     //s1有元素不在s2中
     if( s2.find(*it)==s2.end() )
     {
         flag=10;
         break;
     }
  }
  for( set<string>::iterator it=s2.begin(); it!=s2.end();it++ )
  {
     //s2有元素不在s1中
      if( s1.find(*it)==s1.end() )
      {
         flag+=1;
         break;
      }
  }
  /*当flag==0,s1元素全部在s2中,s2元素也全部在s1中,s1==s2
    当flag==10,s1有元素不在s2中,s2所有元素都在s1中,s1包含了s2
    当flag==1,s1所有元素在s2中,s2有元素不在s1中,s2包含了s1
    当flag==11,s1 s2集合互不包含
  */
  return flag;
}

map_s apriori_gen(map_s &Ck,int k)
{
   //生成子集
   map_s Ck_temp;
   set<string> s_temp;
   for( map_s::iterator l_it1=Ck.begin();l_it1!=Ck.end();l_it1++ )
   {
      for( map_s::iterator l_it2=Ck.begin();l_it2!=Ck.end();l_it2++ )
      {
         //如果两个set一样,则说明是同一个KEY,跳过
         if(!((l_it1->first > l_it2->first)||(l_it1->first < l_it2->first)))
                   continue;
         //否则开始组装,遍历整个Ck
         for( set<string>::iterator s_it=l_it2->first.begin();s_it!=l_it2->first.end();s_it++ )
         {
               //如果该值在l_it1 set里面可以找到,不能组装
               if( l_it1->first.find(*s_it)!=l_it1->first.end())
                  continue;
               //否则进行组装,先把l_it1的set复制进去
               s_temp = l_it1->first;
               //再把l_it2的值放进去
               s_temp.insert(*s_it);
               //判断该组装的set是否已在生成集合中,如果之前已生成,则不需要往下运算
               if(Ck_temp.find(s_temp)!=Ck_temp.end())
                    continue;
               else  //否则放到生成的子集中
               {
                  Ck_temp.insert(pair<set<string>,int >(s_temp,0));
               }
         }
      }
   }

   //对于k=2的情况,需要扫描原始数据得出计数值
   if( 2 == k )
   {
     for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
      for(int i=0;i<data.size();i++)
       //l_it集合被data[i]完整包含,则计数值+1
       if( (10 == compset(data[i],l_it->first)) || (0 == compset(data[i],l_it->first))  )
                Ck_temp[l_it->first]++;

       //扫描完之后排除 非频繁项
     for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
         if( Ck_temp[l_it->first] < minSup )
             Ck_temp.erase(l_it);
   }
   //如果是大于2的情况,扫描k-1的频繁项子集
   if( 2 < k )
   {
      //每次都循环获取每个Ck的k-1子集元素
      //如{I1,I2,I3}C3的子集是{I1,I2} {I2,I3} {I3,I4}
      //如果Ck的子集不在k-1的频繁项子集中,则去掉该Ck项
      for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
      {
         int flag;
         for( set<string>::iterator s_it=l_it->first.begin();s_it!=l_it->first.end();s_it++ )
         {
           //开始求子集
           //首先把当前Ck项的集合保存
           s_temp=l_it->first;
           //去掉一个元素,即是它的k-1子集
           s_temp.erase(*s_it);
           //遍历频繁项集合L,看看是不是在频繁集中
           flag=1;
           for( int i=0;i<L.size();i++  )
           {
             //如果K-1子集在频繁项集中存在,则保留
             if( 0 == compset(s_temp,L[i]) )
             {
                  flag=0;
                  break;
             }
           }
           //如果找到了哪怕一个k-1子集项不在频繁项集中,直接退出
           if( flag ) break;
         }
         //只有所有的k-1子集在频繁项集中,才保留该Ck项
         if( flag ) Ck_temp.erase(l_it);
      }
   }

   cout<<"由L"<<k-1<<"产生的候选集C"<<k<<"   "<<"cout数(k=2以上不做计数)"<<endl;
   for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
   {
        for( set<string>::iterator s_it=l_it->first.begin();s_it!=l_it->first.end();s_it++ )
             cout<<*s_it<<"  ";
        cout<<l_it->second<<endl;
   }
   return Ck_temp;
}

int main()
{
  cout<<"请输入事务,第一行输入事务数。每行第一个值输入该事务的item数"<<endl;
  //生成原始数据集
  cin>>n;
  for(int i=0;i<n;i++)
  {
     s.clear();
     cin>>m;
     for(int j=0;j<m;j++)
     {
        cin>>in;
        s.insert(in);
        data2.push_back(in);
     }
     data.push_back(s);
  }
  //扫描数据集D,生成C1
     //对于每个候选集里面的元素
     for( int j=0; j<data2.size();j++ )
     {
      int flag=1;
      //如果C1中存在该元素,计数值加1
      for( map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
      {
        if( (l_it->first).find(data2[j]) != (l_it->first).end() )
        {
           Ck[l_it->first]++;
           flag=0;
           break;
        }
      }
     //不存在,插入到C1集合中
     if(flag)
     {
          s.clear();
          s.insert(data2[j]);
          Ck.insert(pair<set<string>,int>(s,1));
     }
    }
    //去掉支持度不足的
    for(  map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
    {
      if( l_it->second< minSup)
        Ck.erase(l_it);
    }

  cout<<"C1候选集:"<<endl;
  cout<<"项集"<<"     "<<"支持度计数"<<endl;

  for( map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
  {
      for( set<string>::iterator s_it=(l_it->first).begin(); s_it!=(l_it->first).end(); s_it++)
         cout<<*s_it<<" "<<l_it->second<<endl;
  }

  int f_count=2;
  while( f_count )
  {
      //将Ck内的k-1频繁集全部保存到L集中
      for( map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
        L.push_back(l_it->first);
      //获取Ck集,已清除掉小于支持度的候选集
      Ck=apriori_gen(Ck,f_count);     

      if( Ck.empty() )
      {
          break;
      }else{ f_count++; }
  }

  cout<<"最终的频繁集集合"<<endl;
  for( int i=0; i<L.size(); i++ )
  {
      for( set<string>::iterator s_it=L[i].begin(); s_it!=L[i].end(); s_it++)
         cout<<*s_it<<" ";
      cout<<endl;
  }

}

目前只实现到产生频繁集合,支持度默认计数2。在g++环境下编译 g++ -g Apriori.c -o apr

测试数据如下:

4

3 I1 I2 I6

4 I1 I2 I3 I5

3 I2 I3 I7

5 I1 I3 I5 I6 I2

输出如下:

用ORACLE格式化数据导出成文件,C++处理后返回文件给ORACLE,传到推荐数据库中即可完成这个算法的全流程。

当然目前还只是儿童玩具,因为所有的数据都是放在内存里的,数据量一大这个代码就不实用了。C++牛逼的地方在于可以直接调用linux底层的接口处理这一类问题,比如当内存不足时数据存到磁盘。我们这边内存有几十G,订单总量不超过1G,所以这个代码应该够用了,不够用时可以再扩展

时间: 2024-10-18 22:00:13

Apriori算法的C++实现的相关文章

机器学习(八)—Apriori算法

摘要:本文对Apriori算法进行了简单介绍,并通过Python进行实现,进而结合UCI数据库中的肋形蘑菇数据集对算法进行验证. “啤酒与尿布”的例子相信很多人都听说过吧,故事是这样的:在一家超市中,人们发现了一个特别有趣的现象,尿布与啤酒这两种风马牛不相及的商品居然摆在一起.但这一奇怪的举措居然使尿布和啤酒的销量大幅增加了.这可不是一个笑话,而是一直被商家所津津乐道的发生在美国沃尔玛连锁超市的真实案例.原来,美国的妇女通常在家照顾孩子,所以她们经常会嘱咐丈夫在下班回家的路上为孩子买尿布,而丈夫

Apriori算法--关联规则挖掘

我的数据挖掘算法代码:https://github.com/linyiqun/DataMiningAlgorithm 介绍 Apriori算法是一个经典的数据挖掘算法,Apriori的单词的意思是"先验的",说明这个算法是具有先验性质的,就是说要通过上一次的结果推导出下一次的结果,这个如何体现将会在下面的分析中会慢慢的体现出来.Apriori算法的用处是挖掘频繁项集的,频繁项集粗俗的理解就是找出经常出现的组合,然后根据这些组合最终推出我们的关联规则. Apriori算法原理 Aprio

机器学习实战精读--------Apriori算法

关联分析(关联规则学习):从大规模数据集中寻找物品间的隐含关系, Apriori算法:一种挖掘关联规则的频繁项算法,其核心是通过候选集生成和情节的向下封闭检测ll阶段来挖掘频繁项集,它是最具影响的挖掘布尔关联规则频繁集的算法 Aprior算法缺点:① 可能产生大量候选集:② 可能需要重复扫描数据库. 频繁项集:经常出现在一块的物品的集合 关联规则:暗示两种物品之间可能存在很强的关系 一个项集的支持度:数据集中包含该项集的记录所占的比例:支持度是针对项集来说的. 可信度(置信度):针对一条诸如{尿

Apriori算法

APRIORI Apriori算法是一种挖掘关联规则的频繁项集算法,其核心思想是通过候选集生成和情节的向下封闭检测两个阶段来挖掘频繁项集.而且算法已经被广泛的应用到商业.网络安全等各个领域. Apriori算法 是一种最有影响的挖掘布尔关联规则频繁项集的算法.其核心是基于两阶段频集思想的递推算法.该关联规则在分类上属于单维.单层.布尔关联规则.在这里,所有支持度大于最小支持度的项集称为频繁项集,简称频集. 算法思想 该算法的基本思想[2]  是:首先找出所有的频集,这些项集出现的频繁性至少和预定

关联规则挖掘(基本概念和Apriori算法)

关联规则挖掘的研究一直是数据挖掘领域的一个重要方向. 关联规则挖掘的目的是从大型事务数据库中挖掘出不同项目之间的关联关系 ,找出潜在的行为模式. 关联规则概念是由 Agrawal 等人在1993年率先提出的, 并随后提出了Apriori算法. 基本概念: 定义1  关联规则挖掘的事务数据库记为TDB,TDB={T1,T2,…,Tk},Tk={i1,i2,…,ip},Tk称为事务,ip称为项目. 定义2  设I={i1,i2,…,im}是TDB中全体项目组成的集合.每一个事务T是I中一组项目的集合

频繁模式挖掘-Apriori算法

DM实验,写的比较二,好多情况还没有考虑,后续有时间会修改. 开始的时候数据结构没设计好导致写到后面费了很大的劲.不过还好python的列表有起死回生的功效... 数据集:database.txt I1,I2,I5 I2,I4 I2,I3 I1,I2,I4 I1,I3 I2,I3 I1,I3 I1,I2,I3,I5 I1,I2,I3 apriori.py #coding=utf-8 """ author:messiandzcy apriori.py date:2014.12.

Apriori算法关联分析与pyhon实现

算法中核心性质:频繁项集的所有非空子集也必须是频繁的.逆反命题 也成立:如果一个项集是非频繁的,那么所有它的超集也是非频繁. 一.Apriori算法简介:  Apriori算法是一种挖掘关联规则的频繁项集算法,其核心思想是通过候选集生成和情节的向下封闭检测两个阶段来挖掘频繁项集. Apriori(先验的,推测的)算法应用广泛,可用于消费市场价格分析,猜测顾客的消费习惯:网络安全领域中的入侵检测技术:可用在用于高校管理中,根据挖掘规则可以有效地辅助学校管理部门有针对性的开展贫困助学工作:也可用在移

数据挖掘中的模式发现(二)Apriori算法

基本概念 对于A→B 支持度(support): P(A∩B),既有A又有B的概率 置信度(Confidence Strength): conf(A→B)=sup(A∪B)sup(A)=P(B|A) 即,在A发生的事件中同时发生B的概率 例如购物篮分析:牛奶?面包 例子:[支持度:3%,置信度:40%] 支持度3%:意味着3%顾客同时购买牛奶和面包 置信度40%:意味着购买牛奶的顾客40%也购买面包 候选集(Candidate itemset): 通过向下合并得出的项集. 定义为C[k]. 频繁

推荐系统第4周--- 基于频繁模式的推荐系统和关联规则挖掘Apriori算法

数据挖掘:关联规则挖掘 关联规则挖掘:Apriori算法 提高Apriori的效率 基于散列的算法基于FP tree的算法

机器学习day16 机器学习实战Apriori算法进行关联分析

上一章学习了非监督学习的聚类,聚类算法可以将不同性质的分类分开.这两天学习了apriori算法进行关联分析,感觉是目前最难理解的一章了,并且书中还有个很坑爹的错误,作者存在很大的疏忽. Apriori算法关联分析:从大规模数据集中寻找物品间的隐含关系被称作关联分析或者关联规则学习. 关联分析应用1:我们以前学习的是根据特性进行分类或者回归预测,并没有挖掘特性之间的关系,关联分析可以用于分析数据集中特性之间的关系,可以得到哪些特性频繁的共同出现或者特性之间的关系(比如出现特性A就会很大几率出现特性