一些基本概念
支持度:support(A=>B)=P(A並B)
置信度:confidence(A=>B)=P(B|A)
頻繁k項集:假設項集I的支持度滿足提前定義的最小支持度閾值。則稱I為頻繁項集,包括k個項的項集稱為k項集。
算法思想
Apriori算法是Agrawal和R. Srikant於1994年提出。為布爾關聯規則挖掘頻繁項集的原創性算法。
通過名字能夠看出算法基於這樣一個事實:算法使用頻繁項集性質的先驗知識。
apriori算法使用一種成為逐層搜索的迭代算法,當中k項集用於探索(k+1)項集。首先,通過掃描數據庫,累計每一個項的計數。並搜集滿足最小支持度的項,找出頻繁1項集的集合。該集合記為L1。然后,使用L1找出頻繁2項集的集合L2,使用L2找出L3。如此下去,直到不能再找到頻繁k項集。
能夠想象。該算法基本思路計算復雜度是很大的。為了提高頻繁項集的產生效率,使用先驗性質(頻繁項集的全部非空子集也一定是頻繁的;換句話說。若某個集合存在一個非空子集不是頻繁項集,則該集合不是頻繁項集)來壓縮搜索空間。
怎樣在算法中使用先驗性質?為了理解這一點。我們考察怎樣使用Lk-1找出Lk,當中k>=2。
主要由兩步構成:連接步和剪枝步。
連接步:為找出Lk。通過將Lk-1與自身相連接產生候選集k項集的集合。
該候選集的集合記為Ck。設l1和l2是Lk-1中的項集。記號li[j]表示li的第j項(比如。l1[k-2]表示l1的倒數第2項)。為了有效實現。apriori算法假定事務或項集中的項按字典序排列。
對於(k-1)項集li,這意味着把項排序,使得li[1]<li[2]<...<li[k-1]。連接Lk-1和Lk-1;當中Lk-1的元素是可連接的。假設它們前(k-2)項同樣。即Lk-1的元素l1和l2是可連接的,假設(l1[1]=l2[1])^(l1[2]=l2[2])^...^(l1[k-2]=l2[k-2])^(l1[k-1]<l2[k-1])。條件l1[k-1]<l2[k-1]是簡單保證不產生反復。連接l1和l2產生的結果項集是{l1[1],l1[2],...,l1[k-1],l2[k-1]}
剪枝步: CK是LK的超集,也就是說,CK的成員可能是也可能不是頻繁的。
通過掃描全部的事務(交易),確定CK中每一個候選的計數,推斷是否小於最小支持度計數,假設不是。則覺得該候選是頻繁的。為了壓縮Ck,能夠利用Apriori性質:任一頻繁項集的全部非空子集也必須是頻繁的,反之,假設某個候選的非空子集不是頻繁的,那么該候選肯定不是頻繁的,從而能夠將其從CK中刪除。
(該步利用了標紅的先驗性質)
圖例

偽代碼
算法:Apriori
輸入:D - 事務數據庫;min_sup - 最小支持度計數閾值
輸出:L - D中的頻繁項集
方法:
L1=find_frequent_1-itemsets(D); // 找出全部頻繁1項集
For(k=2;Lk-1!=null;k++){
Ck=apriori_gen(Lk-1); // 產生候選,並剪枝
For each 事務t in D{ // 掃描D進行候選計數
Ct =subset(Ck,t); // 得到t的子集
For each 候選c 屬於 Ct
c.count++;
}
Lk={c屬於Ck | c.count>=min_sup}
}
Return L=全部的頻繁集;
Procedure apriori_gen(Lk-1:frequent(k-1)-itemsets)
For each項集l1屬於Lk-1
For each項集 l2屬於Lk-1
If((l1[1]=l2[1])&&( l1[2]=l2[2])&&…….
&& (l1[k-2]=l2[k-2])&&(l1[k-1]<l2[k-1])) then{
c=l1連接l2 //連接步:產生候選
if has_infrequent_subset(c,Lk-1) then
delete c; //剪枝步:刪除非頻繁候選
else add c to Ck;
}
Return Ck;
Procedure has_infrequent_sub(c:candidate k-itemset; Lk-1:frequent(k-1)-itemsets)
For each(k-1)-subset s of c
If s不屬於Lk-1 then
Return true;
Return false;
Java實現
該java代碼基本上是嚴格依照偽代碼的流程寫的。比較easy理解。
package com.zhyoulun.apriori;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Apriori2
{
private final static int SUPPORT = 2; // 支持度閾值
private final static double CONFIDENCE = 0.7; // 置信度閾值
private final static String ITEM_SPLIT = ";"; // 項之間的分隔符
private final static String CON = "->"; // 項之間的分隔符
/**
* 算法主程序
* @param dataList
* @return
*/
public Map<String, Integer> apriori(ArrayList<String> dataList)
{
Map<String, Integer> stepFrequentSetMap = new HashMap<>();
stepFrequentSetMap.putAll(findFrequentOneSets(dataList));
Map<String, Integer> frequentSetMap = new HashMap<String, Integer>();//頻繁項集
frequentSetMap.putAll(stepFrequentSetMap);
while(stepFrequentSetMap!=null && stepFrequentSetMap.size()>0)
{
Map<String, Integer> candidateSetMap = aprioriGen(stepFrequentSetMap);
Set<String> candidateKeySet = candidateSetMap.keySet();
//掃描D,進行計數
for(String data:dataList)
{
for(String candidate:candidateKeySet)
{
boolean flag = true;
String[] strings = candidate.split(ITEM_SPLIT);
for(String string:strings)
{
if(data.indexOf(string+ITEM_SPLIT)==-1)
{
flag = false;
break;
}
}
if(flag)
candidateSetMap.put(candidate, candidateSetMap.get(candidate)+1);
}
}
//從候選集中找到符合支持度的頻繁項集
stepFrequentSetMap.clear();
for(String candidate:candidateKeySet)
{
Integer count = candidateSetMap.get(candidate);
if(count>=SUPPORT)
stepFrequentSetMap.put(candidate, count);
}
// 合並全部頻繁集
frequentSetMap.putAll(stepFrequentSetMap);
}
return frequentSetMap;
}
/**
* find frequent 1 itemsets
* @param dataList
* @return
*/
private Map<String, Integer> findFrequentOneSets(ArrayList<String> dataList)
{
Map<String, Integer> resultSetMap = new HashMap<>();
for(String data:dataList)
{
String[] strings = data.split(ITEM_SPLIT);
for(String string:strings)
{
string += ITEM_SPLIT;
if(resultSetMap.get(string)==null)
{
resultSetMap.put(string, 1);
}
else {
resultSetMap.put(string, resultSetMap.get(string)+1);
}
}
}
return resultSetMap;
}
/**
* 依據上一步的頻繁項集的集合選出候選集
* @param setMap
* @return
*/
private Map<String, Integer> aprioriGen(Map<String, Integer> setMap)
{
Map<String, Integer> candidateSetMap = new HashMap<>();
Set<String> candidateSet = setMap.keySet();
for(String s1:candidateSet)
{
String[] strings1 = s1.split(ITEM_SPLIT);
String s1String = "";
for(String temp:strings1)
s1String += temp+ITEM_SPLIT;
for(String s2:candidateSet)
{
String[] strings2 = s2.split(ITEM_SPLIT);
boolean flag = true;
for(int i=0;i<strings1.length-1;i++)
{
if(strings1[i].compareTo(strings2[i])!=0)
{
flag = false;
break;
}
}
if(flag && strings1[strings1.length-1].compareTo(strings2[strings1.length-1])<0)
{
//連接步:產生候選
String c = s1String+strings2[strings2.length-1]+ITEM_SPLIT;
if(hasInfrequentSubset(c, setMap))
{
//剪枝步:刪除非頻繁的候選
}
else {
candidateSetMap.put(c, 0);
}
}
}
}
return candidateSetMap;
}
/**
* 使用先驗知識,推斷候選集是否是頻繁項集
* @param candidate
* @param setMap
* @return
*/
private boolean hasInfrequentSubset(String candidateSet, Map<String, Integer> setMap)
{
String[] strings = candidateSet.split(ITEM_SPLIT);
//找出候選集全部的子集,並推斷每一個子集是否屬於頻繁子集
for(int i=0;i<strings.length;i++)
{
String subString = "";
for(int j=0;j<strings.length;j++)
{
if(j!=i)
{
subString += strings[j]+ITEM_SPLIT;
}
}
if(setMap.get(subString)==null)
return true;
}
return false;
}
/**
* 由頻繁項集產生關聯規則
* @param frequentSetMap
* @return
*/
public Map<String, Double> getRelationRules(Map<String, Integer> frequentSetMap)
{
Map<String, Double> relationsMap = new HashMap<>();
Set<String> keySet = frequentSetMap.keySet();
for(String key:keySet)
{
List<String> keySubset = subset(key);
for(String keySubsetItem:keySubset)
{
//子集keySubsetItem也是頻繁項
Integer count = frequentSetMap.get(keySubsetItem);
if(count!=null)
{
Double confidence = (1.0*frequentSetMap.get(key))/(1.0*frequentSetMap.get(keySubsetItem));
if(confidence>CONFIDENCE)
relationsMap.put(keySubsetItem+CON+expect(key, keySubsetItem), confidence);
}
}
}
return relationsMap;
}
/**
* 求一個集合全部的非空真子集
*
* @param sourceSet
* @return
* 為了以后能夠用在其它地方。這里我們不是用遞歸的方法
*
* 參考:http://blog.163.com/xiaohui_1123@126/blog/static/3980524020109784356915/
* 思路:如果集合S(A,B,C,D)。其大小為4。擁有2的4次方個子集,即0-15,二進制表示為0000,0001。...,1111。
* 相應的子集為空集。{D},...。{A,B,C,D}。
*/
private List<String> subset(String sourceSet)
{
List<String> result = new ArrayList<>();
String[] strings = sourceSet.split(ITEM_SPLIT);
//非空真子集
for(int i=1;i<(int)(Math.pow(2, strings.length))-1;i++)
{
String item = "";
String flag = "";
int ii=i;
do
{
flag += ""+ii%2;
ii = ii/2;
} while (ii>0);
for(int j=flag.length()-1;j>=0;j--)
{
if(flag.charAt(j)=='1')
{
item = strings[j]+ITEM_SPLIT+item;
}
}
result.add(item);
}
return result;
}
/**
* 集合運算,A/B
* @param A
* @param B
* @return
*/
private String expect(String stringA,String stringB)
{
String result = "";
String[] stringAs = stringA.split(ITEM_SPLIT);
String[] stringBs = stringB.split(ITEM_SPLIT);
for(int i=0;i<stringAs.length;i++)
{
boolean flag = true;
for(int j=0;j<stringBs.length;j++)
{
if(stringAs[i].compareTo(stringBs[j])==0)
{
flag = false;
break;
}
}
if(flag)
result += stringAs[i]+ITEM_SPLIT;
}
return result;
}
public static void main(String[] args)
{
ArrayList<String> dataList = new ArrayList<>();
dataList.add("1;2;5;");
dataList.add("2;4;");
dataList.add("2;3;");
dataList.add("1;2;4;");
dataList.add("1;3;");
dataList.add("2;3;");
dataList.add("1;3;");
dataList.add("1;2;3;5;");
dataList.add("1;2;3;");
System.out.println("=數據集合==========");
for(String string:dataList)
{
System.out.println(string);
}
Apriori2 apriori2 = new Apriori2();
System.out.println("=頻繁項集==========");
Map<String, Integer> frequentSetMap = apriori2.apriori(dataList);
Set<String> keySet = frequentSetMap.keySet();
for(String key:keySet)
{
System.out.println(key+" : "+frequentSetMap.get(key));
}
System.out.println("=關聯規則==========");
Map<String, Double> relationRulesMap = apriori2.getRelationRules(frequentSetMap);
Set<String> rrKeySet = relationRulesMap.keySet();
for (String rrKey : rrKeySet)
{
System.out.println(rrKey + " : " + relationRulesMap.get(rrKey));
}
}
}
計算結果
=數據集合========== 1;2;5; 2;4; 2;3; 1;2;4; 1;3; 2;3; 1;3; 1;2;3;5; 1;2;3; =頻繁項集========== 1;2; : 4 1;3; : 4 5; : 2 2;3; : 4 4; : 2 2;4; : 2 1;5; : 2 3; : 6 2; : 7 1; : 6 1;2;5; : 2 1;2;3; : 2 2;5; : 2 =關聯規則========== 4;->2; : 1.0 5;->1;2; : 1.0 5;->1; : 1.0 1;5;->2; : 1.0 5;->2; : 1.0 2;5;->1; : 1.0
參考:
http://blog.csdn.net/zjd950131/article/details/8071414
http://www.cnblogs.com/zacard-orc/p/3646979.html
數據挖掘:概念與技術
轉載請注明出處:http://blog.csdn.net/zhyoulun/article/details/41978401
