數據挖掘十大經典算法之apriori算法&源代碼

數據挖掘十大經典算法
國際權威的學術組織the IEEE International Conference on Data Mining
(ICDM) 2006年12月評選出了數據挖掘領域的十大經典算法:C4.5, k-Means,
SVM, Apriori, EM, PageRank, AdaBoost, kNN, Naive Bayes, and CART.
不僅僅是選中的十大算法,其實參加評選的18種算法,實際上隨便拿出一種來都
可以稱得上是經典算法,它們在數據挖掘領域都產生了極爲深遠的影響。

 

4. The Apriori algorithm
Apriori算法是一種最有影響的挖掘布爾關聯規則頻繁項集的算法。其核心是基於
兩階段頻集思想的遞推算法。該關聯規則在分類上屬於單維、單層、布爾關聯規
則。在這裏,所有支持度大於最小支持度的項集稱爲頻繁項集,簡稱頻集。

 

 VC 界面的源代碼見  http://download.csdn.net/source/2963738

 

Agrawal等人提出的Apriori是經典的關聯規則和頻繁項集挖掘算法,圍繞着它的改進和實現有大量的文獻。該算法是挖掘產生布爾關聯規則頻繁項目集的經典算法,從其產生到現在對關聯規則挖掘方面的研究有着很大的影響。

爲了提高頻繁項目的挖掘效率,Apriori算法利用了兩個重要的性質,用於壓縮搜索的空間。

1】若X爲頻繁項目集,則X的所有子集都是頻繁項目集。

2】若X爲非頻繁項目集,則X的所有超集均爲非頻繁項目集。

Apriori算法的處理流程爲:寬度優先搜索整個項集空間,從k=0開始,迭代產生長度爲k+1的候選項集的集合Ck+1。候選項集是其所有子集都是頻繁項集的項集。C1I0中所有的項構成,在第k層產生所有長度爲k+1的項集。這由兩步完成:第一步,Fk自連接。將Fk中具有相同(k-1)-前綴的項集連接成長度爲k的候選項集。第二步是剪枝,如果項集的所有長度爲k的子集都在Fk中,該項集才能作爲候選項集被加入Ck+1中。爲了計算所有長度爲k的候選項集的支持度,在數據庫水平表示方式下,需要掃描數據庫一遍。在每次掃描中,對數據庫中的每條交易記錄,爲其中所包含的所有候選k-項集的支持度計數加1。所有頻繁的k-項集被加入Fk中。此過程直至Ck+1等於空集時結束。

算法  Apriori

Input:          Transaction DataBase DMinimum support threshold minsup

Output      Frequent pattern L

(1) L1=search_frequent_1-itemsets( D );

(2) for(k=2;Lk-1φ;k++) do

(3) begin

(4)    Ck=apriori-gen(Lk-1);

(5)    for all transactions t D do

(6)    begin

(7)      Ct=subset(Ckt);

(8)      for all candidates c Ct do

(9)        c.count++;

(10)    end

(11)    Lk ={c Ck|c.countminsup}

(12) end

(13) Answer L=kLk;

Procedure Search_frequent_1-itemsets( D )

(1) begin

(2)  for all transactions t D do

(3)  begin

(4)    for each item ik t do

(5)      ik.count++;

(6)  end

(7)  L1 ={ i I | i.countminsup}

(8)  return L1;

(9) end

Procedure apriori_gen(Lk)

(1) begin

(2)   for each itemset l1 Lk do

(3)     for each itemset l2 Lk do

(4)     begin

(5)       if ( l1[1]=l2[1]) ( l1[2]=l2[2]) ( l1[k-1]=l2[k-1]) ( l1[k]<l2[k]) then

(6)       begin

(7)          c= l1 l2;

(8)          if Is_include_infrenquent_subset(c,Lk) then

(9)             delete c;

(10)         else add c to Ck+1;

(11)       end

(12)      end

(13)    return Ck+1 ;

(14) end

Procedure Is_include_infrenquent_subset(c,Lk)

(1)begin

(2)  for each k-subset s of c

(3)     if s Lk then

(4)       reture TURE;

(5)  return FALSE;

(6)end

在主程序中,第一步首先掃描整個交易數據庫D,統計每個項目(item)的支持數,計算其支持度,將支持度大於等於最小支持度minsup的項目構成的集合放入到L1 中;從第2步到第11步,用k-1頻繁項目集構成的Lk-1生成候選集的集合Ck,以便從中生成Lk,其中apriori_gen函數(4)用來從Lk-1中生成Ck,然後對數據庫進行掃描(5),對於數據庫中的每一個交易,subset函數用來發現此交易包含的所有候選集(7),併爲這些候選集的計數器加1(8-9)。最後滿足minsup的候選集被放入到Lk中。

apriori_gen過程完成兩種操作:並(join)和剪枝(prune)。在並運算步驟中,Lk-1 Lk-1 進行並運算生成潛在的候選集(2-7),條件l1[k-1]<l2[k-1]保證不會有重複的候選集生成(5)。在剪枝步驟中(8-10),利用性質2.1,刪除那些存在子集不是頻繁項目集的候選集,測試子集是否爲頻繁項目集由過程Is_include_infrenquent_subset完成。

爲了清楚的闡述Apriori算法的挖掘過程,現舉例如下:

【例1設事務數據庫D如表2.1所示,D中包含4個事務,即|D|=4,最小支持數mincount=2,即最小支持度minsup=2/4=50%。挖掘頻繁項目集的具體過程如下所述:C1={{A},{B},{C},{D},{F}},第一次循環產生L1={{A},{B},{C},{F}},由Apriori_gen(L1)生成C2,掃描數據庫,計算C2中每個候選集得到L2。依此循環,得到L3。整個挖掘過程如圖2.1所示。

1 事務數據庫D

Tid

事務

100

200

300

400

B,C,F

A,C,D

B,F

A,B,C,F

  

 

1 Apriori算法的執行過程

在找到了事務數據庫中的所有頻繁項集後,利用這些頻繁項集可以產生關聯規則,產生關聯規則的步驟如下:

(1) 對於每個頻繁項目集l,產生l的所有非空子集。

(2) 對於l的每個非空子集m,如果support(l)/support(m)≥minconf,則輸出規則m (l-m)”

例如,在上例中產生的頻繁項目集l{B,C,F}l的非空子集有{B,C}{B,F}{C,F}{B}{C}{F},則運用上述產生關聯規則的方法可以得到以下關聯規則:

          B C F    confidence=(2/4)/(4/4)=1

          B F C    confidence=(2/4)/(3/4)=0.667

          C F B    confidence=(2/4)/(2/4)=1

          F B C    confidence=(2/4)/(3/4)= 0.667

          C B F    confidence=(2/4)/(3/4)= 0.667

          B C F    confidence=(2/4)/(3/4)= 0.667

 

 

 

源代碼  apriori.c

//////////////////////////////////////////////////////////////////////////
/*
*
*
*
* 文件名稱:apriori.c

*
* 摘 要:apriori的最簡單實現

*
* 當前版本:1.0
* 完成日期:2006.05
*
*/////////////////////////////////////////////////////////////////////////

#include<stdio.h>
typedef  struct
{
int item[100];  //數據項
} D_Node;      //數據庫D


typedef  struct
{
int item[100];  //數據項,用item[0]保存支持度

} C_Node; //候選集

typedef  struct
{
int item[100];  //數據項,用item[0]保存支持度
} L_Node;//頻繁集

C_Node C[100][100];
L_Node L[100][100];
D_Node D[100];

int min_supp;  //最小支持度

 

void  InPut()
{
 int i,j,n,n1;
 printf("請輸入最小支持度:");
 scanf("%d",&min_supp);
  printf("請輸入交易集的大小");
  scanf("%d",&D[0].item[0]);
  n=D[0].item[0];

     for(i=1;i<=n;i++)  //for1
     {  
        printf("請輸入交易[%d]中記錄的個數(n)",i);
         scanf("%d",&n1);
         D[i].item[0]=n1;

           for(j=1;j<=n1;j++)  //for2
           {
              printf("請輸入交易[%d]中記錄項,直接輸入數字:",i);
              scanf("%d",&D[i].item[j]);                  
           }//for2
                
           }  //for1
    
     }//end of InPut


void  C1()
{
 //功能:掃描數據集D生成1項候選集C1
 //輸入:數據集D
 //輸出1項候選集C1
 //初始條件 數據集D 非空
   int i,j,k;
   int no=1,temp=0;
   C[1][0].item[0]=0;  //1 項集的個數,在本算法中,用C[n][k].item[0]來保存候選集Cn的第k項的支持度
   if(D[0].item[0]!=0)
   { 
      C[1][1].item[1]=D[1].item[1];
         
   }
     
    for(i=1;i<=D[0].item[0];i++)  //for1 
    {
  
         for(j=1;j<=D[i].item[0];j++)  //for2  
      {
               temp=1;
               for(k=1;k<=no;k++)  //for3
      {
                  if(C[1][k].item[1]==D[i].item[j])
            {
                     C[1][k].item[0]++;  //支持度加1
                      temp=0;  //
                          
                  }   //if

               }//end for3
           
         
              if(temp)//生成新的項集
           {
                    C[1][++no].item[1]=D[i].item[j];
                    C[1][no].item[0]=1;    
           }
      
         }//end for2
  
    } // end  for1

    C[1][0].item[0]=no;//數據項的個數 

}  //end of  C1()


void Cn( int n)

 //用頻繁集Ln-1爲基礎,通過連接得到n項候選集Cn
 
 int i,j,k,p,q,s,t,num;
 int no=0,temp=0,count;

 C[n][0].item[0]=0;  //初始化

//printf("in Cn(%d) n=%d/n",n,n);
//printf("in Cn(%d) C[%d][0].item[0]=%d/n",n,n,C[n][0].item[0]);

num=L[n-1][0].item[0];  //num是Ln-1項集的數據個數

   for(i=1;i<=num;i++) 
  
      for(j=i+1;j<=num;j++)   //for2
         {
  
      temp=1;  //測試是否滿足聯結條件
              if(n>2)//if 1
              {
                  for(k=1;k<n-1;k++)     //for3  
                 {
      if(L[n-1][i].item[k]!=L[n-1][j].item[k])
                   {  temp=0;
                     break; }//if 1

                    }//end for3

                 }//end if1

                      if(temp==1)//滿足聯結條件
                     {
                          // printf("in if 2  no=%d/n",no);
                     no++;

                    for(p=1;p<=n-1;p++) 
                    C[n][no].item[p]=L[n-1][i].item[p];
                    C[n][no].item[p]=L[n-1][j].item[p-1];
                    C[n][no].item[0]=0;
                         for(q=1;q<=D[0].item[0];q++)  //for5  測試其支持度

       {   
       
        count=0; //count用來記數,當所測試的項存在時,count加1,當count=n時,則子集存在

                               for(s=1;C[n][no].item[s]!=0;s++)  //for6
                          {
                                      for(t=1;t<=D[q].item[0];t++)  //for7
           {
                                             if(C[n][no].item[s]==D[q].item[t])

            {   count+=1;
                      break;
            }
           }//end for7
                                    
          }//end for 6
                           if(count==n) C[n][no].item[0]+=1;//子集存在,第no項的支持度加1
                  
       }//end for5
                          
                          
                      C[n][0].item[0]+=1;
       }//end if2
           }//end for2

     /* num=C[n][0].item[0];
     printf("in Cn(%d) num=%d/n",n,num);
   for(i=1;i<=num;i++)
    for(j=0;j<=n;j++)
    {
     printf("in Cn(%d) C[%d][%d].item[%d]=%d/n",n,n,i,j,C[n][i].item[j]);
    }
   printf("in Cn(%d) C[%d][0].item[0]=%d/n",n,n,C[n][0].item[0]);  */
  
}//end of Cn()

void L1()
{  
    int i,j,k;
 j=0;
   L[1][0].item[0]=0;
  
   //printf("C[1][0].item[0]=%d/n",C[1][0].item[0]);

   for(i=1;i<=C[1][0].item[0];i++)
   {   
       if(C[1][i].item[0]>=min_supp)
       {
       j+=1;
        for(k=1;k<=1;k++)
         L[1][j].item[k]=C[1][i].item[k];
        L[1][j].item[0]=C[1][i].item[0];
 // printf("L[1][%d].item[1]=%d   ",j,L[1][j].item[1]);  測試功能時加的
 // printf("  -------------%d/n",L[1][j].item[0]);
  
       }
   }//end for1
   L[1][0].item[0]=j;
}//end of L1()

void Ln(int n)
{  
    int i,j,k;
 Cn(n);
    j=0;
    L[n][0].item[0]=0;

   // printf("in Ln(%d) C[%d][0].item[0]=%d/n",n,n,C[n][0].item[0]);

   for(i=1;i<=C[n][0].item[0];i++)  //for 1
   {
       if(C[n][i].item[0]>=min_supp)
       {
         j+=1;
        for(k=1;k<=n;k++)
         L[n][j].item[k]=C[n][i].item[k];
        L[n][j].item[0]=C[n][i].item[0]; 
       }  //end if


   }//end for1

   /*  for(i=1;i<=j;i++)
       for(k=0;k<=n;k++)
  {printf("L[%d][%d].item[%d]=%d /n",n,i,k,L[n][i].item[k]);
  
  }   */

L[n][0].item[0]=j; //保存數據的個數

}//end of Ln(int n)


  void  OutPut(int n)
  {
     int i,j,k;
     printf("頻繁項目集L%d如下:/n",n);
     k=L[n][0].item[0];
         if(k!=0)
         {
             for(i=1;i<=k;i++)
          {
                printf("{");
               for(j=1;j<=n;j++)     
        printf("  I%d ",L[n][i].item[j]);
        printf("}        支持度:%d/n",L[n][i].item[0]);
          
      
             }//for
           
          }
         else                printf("項目集爲空/n");
          
     
  }


  void main()
  {
 int i;
 int n=1;
    InPut();
 C1();//初始化,生成1項候選集C1
 L1();//得到1項頻繁集L1
    while(L[n][0].item[0]!=0)
 {
  n+=1;
        Ln(n);
 }
    for(i=1;i<=n;i++)
        OutPut(i);

 char ch;
   scanf("%d",&i);
 
  }

 

 

--------------------------------------------------------------------------------------

FAST apriori.cpp

//////////////////////////////////////////////////////////////////////////
/*
*
*
*
* 文件名稱:FAST apriori.cpp

*
* 摘 要:採用位運算提高算法的效率

*
* 當前版本:1.0

* 完成日期:2006.06.20
*
*/////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>

typedef  struct
{
char item[10];  //數據項
int min_supp_count;//最小支持度數
} C_Node;      //候選集


typedef  struct
{
char item[10];  //數據項
int min_supp_count;//最小支持度數
} L_Node;     //頻繁集

char D[10][10];
L_Node L[100];
C_Node C[100];
int min_supp_count=2;
int num=100;
void  InPut()
{

 strcpy(D[1],"abe");
 strcpy(D[2],"bd");
 strcpy(D[3],"bc");
 strcpy(D[4],"abd");
 strcpy(D[5],"ac");
 strcpy(D[6],"bc");
 strcpy(D[7],"ac");
 strcpy(D[8],"abce");
 strcpy(D[9],"abc");

 
}//end of InPut
int  * DB=new int[num];

 

void suppDB()
{
  int m='e';
  int n;
  int k;
  for (int i=1;i<=9;i++)
  {
   n=strlen(D[i]);
   DB [i]=0;
   for (int j=0;j<n;j++)
   {
    k=1;
    DB [i]+=k<<(int)(m-D[i][j]); 
   } 
  }

 

}

void check_supp(int num,int no)
{
int i,j,k,m;
    int check;
   m='e';
  
     for(i=1;i<=num;i++)
  {    check=0;
       C[i].min_supp_count=0;
       for (j=0;j<no;j++)
    {
   k=1;
   check+=(int)(k<<(m-C[i].item[j]));
      }
   for (j=1;j<=9;j++)
   {
    if (check==(check&DB[j]))
    {
      C[i].min_supp_count+=1;//子集存在,支持度數加1
    }
   }
  }

}

void  C1()
{
 //功能:掃描數據集D生成1項候選集C1
 //輸入:數據集D
 //輸出1項候選集C1
 //初始條件 數據集D 非空
 strcpy(C[1].item,"a");
 strcpy(C[2].item,"b");
 strcpy(C[3].item,"c");
 strcpy(C[4].item,"d");
 strcpy(C[5].item,"e");
  
   C[0].min_supp_count=5;  //1 項候選集的個數,在本算法中,用C[0].min_supp_count來保存候選集Cn的個數  

  check_supp(5,1);
 
  
 }  //end of  C1()


void Cn( int n)

 //用頻繁集Ln-1爲基礎,通過連接得到n項候選集Cn
 
 int i,j,k,p,num;
 int no=0,temp=0;

 C[0].min_supp_count=0;  //初始化


num=L[0].min_supp_count;  //num是Ln-1項集的數據個數

   for(i=1;i<=num;i++) 
  
      for(j=i+1;j<=num;j++)   //for2
         {
  
      temp=1;  //測試是否滿足聯結條件
              if(n>2)//if 1
              {
                  for(k=0;k<n-2;k++)     //for3  
                 {
      if(L[i].item[k]!=L[j].item[k])
                   {  temp=0;
                     break; }//if 1

                    }//end for3

                 }//end if1

                      if(temp==1)//滿足聯結條件
                     {
                          // printf("in if 2  no=%d/n",no);
                     no++;

                    for(p=0;p<=n-2;p++) 
                    C[no].item[p]=L[i].item[p];
                    C[no].item[p]=L[j].item[p-1];
                    C[no].min_supp_count=0;
                    C[0].min_supp_count+=1;
       }//end if2
           }//end for2
num=C[0].min_supp_count;
   check_supp(num,n);//測試支持度
}//end of Cn() 


void L1()
{   int n=1;
    int i,j,k;
 j=0;
   L[0].min_supp_count=0;//頻繁集的個數,初始爲0
   
   for(i=1;i<=C[0].min_supp_count;i++)
   {   
       if(C[i].min_supp_count>=min_supp_count)
       {
       j+=1;
        strcpy(L[j].item,C[i].item);  
  L[j].min_supp_count=C[i].min_supp_count;
       }
   }//end for1
   L[0].min_supp_count=j;///頻繁集的個數,最後爲j個

    printf("頻繁項目集L%d如下:/n",n);
     k=L[0].min_supp_count;
         if(k!=0)
         {
             for(i=1;i<=k;i++)
          {
                printf("{");
               for(j=0;j<n;j++)     
        printf("  %c ",L[i].item[j]);
        printf("}        支持度:%d/n",L[i].min_supp_count);
          
      
             }//for
           
          }
         else                printf("項目集爲空/n");

}//end of L1()

void Ln(int n)
{  
    int i,j,k;
 Cn(n);
    j=0;
    L[0].min_supp_count=0;

  
   for(i=1;i<=C[0].min_supp_count;i++)  //for 1
   {
       if(C[i].min_supp_count >=min_supp_count)
       {
         j+=1;
        strcpy(L[j].item,C[i].item);  
  L[j].min_supp_count=C[i].min_supp_count;
         
       }  //end if


   }//end for1

  

L[0].min_supp_count=j; //保存數據的個數
 printf("頻繁項目集L%d如下:/n",n);
     k=L[0].min_supp_count;
         if(k!=0)
         {
             for(i=1;i<=k;i++)
          {
                printf("{");
               for(j=0;j<n;j++)     
        printf("  %c ",L[i].item[j]);
        printf("}        支持度:%d/n",L[i].min_supp_count);
          
      
             }//for
           
          }
         else                printf("項目集爲空/n");

}//end of Ln(int n)


 void main()
{

  
      int n=1;
    InPut();
 suppDB();
 C1();//初始化,生成1項候選集C1
 L1();//得到1項頻繁集L1
  while(L[0].min_supp_count!=0)
 {
  n+=1;
        Ln(n);
  }
  char ch;
  printf("press any key to eixe/n");
  scanf("%c",&ch);
  
   }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章