定義
??關聯分析是一種簡單、實用的分析技術,就是發現存在于大量數據集中的關聯性或相關性,從而描述了一個事物中某些屬性同時出現的規律和模式。
??關聯分析是從大量數據中發現項集之間有趣的關聯和相關聯系。關聯分析的一個典型例子是購物籃分析。該過程通過發現顧客放人其購物籃中的不同商品之間的聯系,分析顧客的購買習慣。通過了解哪些商品頻繁地被顧客同時購買,這種關聯的發現可以幫助零售商制定營銷策略。其他的應用還包括價目表設計、商品促銷、商品的排放和基于購買模式的顧客劃分。
??可從數據庫中關聯分析出形如“由于某些事件的發生而引起另外一些事件的發生”之類的規則。如“67%的顧客在購買啤酒的同時也會購買尿布”,因此通過合理的啤酒和尿布的貨架擺放或捆綁銷售可提高超市的服務質量和效益。又如“‘C語言’課程優秀的同學,在學習‘數據結構’時為優秀的可能性達88%”,那么就可以通過強化“C語言”的學習來提高教學效果。
相關概念
支持度與置信度
關聯規則可以描述成:項集 → 項集。項集X出現的事務次數(亦稱為support count)定義為:
σ(X)=|ti|X?ti,ti∈T|
其中,ti表示某個事務(TID),T表示事務的集合。關聯規則X?Y的支持度(support):
support(X?Y)=σ(X∪Y)|T|
支持度刻畫了項集X∪Y的出現頻次。置信度(confidence)定義如下:
confidence(X?Y)=σ(X∪Y)σ(X)
對概率論稍有了解的人,應該看出來:置信度可理解為條件概率p(Y|X),度量在已知事務中包含了X時包含Y的概率。
??對于靠譜的關聯規則,其支持度與置信度均應大于設定的閾值。那么,關聯分析問題即等價于:對給定的支持度閾值min_sup、置信度閾值min_conf,找出所有的滿足下列條件的關聯規則:
支持度>=min_sup
置信度>=min_conf
把支持度大于閾值的項集稱為頻繁項集(frequent itemset)。因此,關聯規則分析可分為下列兩個步驟:
- 生成頻繁項集F=X∪Y;
- 在頻繁項集F中,找出所有置信度大于最小置信度的關聯規則X?Y。
舉個例子:
TID | Items |
---|---|
001 | Cola, Egg, Ham |
002 | Cola, Diaper, Beer |
003 | Cola, Diaper, Beer, Ham |
004 | Diaper, Beer |
??TID代表交易流水號,Items代表一次交易的商品。我們對這個數據集進行關聯分析,可以找出關聯規則{Diaper}→{Beer}。
??它代表的意義是:購買了Diaper的顧客會購買Beer。這個關系不是必然的,但是可能性很大,這就已經足夠用來輔助商家調整Diaper和Beer的擺放位置了,例如擺放在相近的位置,進行捆綁促銷來提高銷售量。
??1、事務:每一條交易稱為一個事務,例如示例1中的數據集就包含四個事務。
2、項:交易的每一個物品稱為一個項,例如Cola、Egg等。
3、項集:包含零個或多個項的集合叫做項集,例如{Cola, Egg, Ham}。
4、k?項集:包含k個項的項集叫做k-項集,例如{Cola}叫做1-項集,{Cola, Egg}叫做2-項集。
5、支持度計數:一個項集出現在幾個事務當中,它的支持度計數就是幾。例如{Diaper, Beer}出現在事務 002、003和004中,所以它的支持度計數是3。
6、支持度:支持度計數除于總的事務數。例如上例中總的事務數為4,{Diaper, Beer}的支持度計數為3,所以它的支持度是3÷4=75%,說明有75%的人同時買了Diaper和Beer。
7、頻繁項集:支持度大于或等于某個閾值的項集就叫做頻繁項集。例如閾值設為50%時,因為{Diaper, Beer}的支持度是75%,所以它是頻繁項集。
8、前件和后件:對于規則{Diaper}→{Beer},{Diaper}叫做前件,{Beer}叫做后件。
9、置信度:對于規則{Diaper}→{Beer},{Diaper, Beer}的支持度計數除于{Diaper}的支持度計數,為這個規則的置信度。例如規則{Diaper}→{Beer}的置信度為3÷3=100%。說明買了Diaper的人100%也買了Beer。
10、強關聯規則:大于或等于最小支持度閾值和最小置信度閾值的規則叫做強關聯規則。關聯分析的最終目標就是要找出強關聯規則[1] 。
挖掘過程
頻繁項集挖掘
??在進行頻繁項集挖掘之前,我們需要根據現有數據的項集items得到所有可能的項集組合。假設現在有個只包含a,b,c,d四個項集的事務,其可能的項集組合如下:
??根據支持度的定義,得到如下的先驗定理:
- 定理1:如果一個項集是頻繁的,那么其所有的子集(subsets)也一定是頻繁的。
?這個比較容易證明,因為某項集的子集的支持度一定不小于該項集。
- 定理2:如果一個項集是非頻繁的,那么其所有的超集(supersets)也一定是非頻繁的。
??定理2是上一條定理的逆反定理。根據定理2,可以對項集樹進行如下剪枝:
頻繁項集的挖掘算法思路大致如下:
關聯規則挖掘
??關聯規則是由頻繁項集生成的,即對于Fk,找出項集hm,使得規則fk?hm?hm的置信度大于置信度閾值。同樣地,根據置信度定義得到如下定理:
- 定理3:如果規則X?Y?X不滿足置信度閾值,則X的子集X′,規則X′?Y?X′也不滿足置信度閾值。
根據定理3,可對規則樹進行如下剪枝:
生成關聯規則的算法如下:
頻繁項集算法
Apriori
Apriori算法過程
??挖掘頻繁項集的過程如上圖所示:
??1. 由數據集生成候選項集C1(1表示每個候選項僅有一個數據項);再由C1通過支持度過濾,生成頻繁項集L1(1表示每個頻繁項僅有一個數據項)。
??2. 將L1的數據項兩兩拼接成C2。
??3. 從候選項集C2開始,通過支持度過濾生成L2。L2根據Apriori原理拼接成候選項集C3;C3通過支持度過濾生成L3……直到Lk中僅有一個或沒有數據項為止。
Apriori算法的代碼實現:
package com.qqmaster.com.machineLearning;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.qqmaster.com.machineLearning.node.AprioriNode;
public class Algorithm_Apriori {
public static void main(String[] args) {
List<AprioriNode> data = new ArrayList<AprioriNode>();
String[] names = {"p1", "p2", "P3", "P4", "P5"};
AprioriNode.attNames = names;
AprioriNode d0 = new AprioriNode();
boolean[] f0 = {true,true,false,false,true};
d0.setFeatures(f0);
AprioriNode d1 = new AprioriNode();
boolean[] f1 = {false,true,false,true,false};
d1.setFeatures(f1);
AprioriNode d2 = new AprioriNode();
boolean[] f2 = {false,false,true,true,false};
d2.setFeatures(f2);
AprioriNode d3 = new AprioriNode();
boolean[] f3 = {true,true,false,true,false};
d3.setFeatures(f3);
AprioriNode d4 = new AprioriNode();
boolean[] f4 = {true,false,true,false,false};
d4.setFeatures(f4);
AprioriNode d5 = new AprioriNode();
boolean[] f5 = {false,true,true,false,false};
d5.setFeatures(f5);
AprioriNode d6 = new AprioriNode();
boolean[] f6 = {true,false,true,false,false};
d6.setFeatures(f6);
AprioriNode d7 = new AprioriNode();
boolean[] f7 = {true,true,true,false,true};
d7.setFeatures(f7);
AprioriNode d8 = new AprioriNode();
boolean[] f8 = {true,true,true,false,false};
d8.setFeatures(f8);
AprioriNode d9 = new AprioriNode();
boolean[] f9 = {true,false,true,false,true};
d9.setFeatures(f9);
data.add(d0);
data.add(d1);
data.add(d2);
data.add(d3);
data.add(d4);
data.add(d5);
data.add(d6);
data.add(d7);
data.add(d8);
data.add(d9);
for(double support = 0.1;support <= 0.9; support += 0.1){
System.out.println("support -> " + support);
Algorithm_Apriori apriori = new Algorithm_Apriori();
System.out.println("\nFrequent Items -> " + apriori.aprior(data, support));
System.out.println("\n--------------------------------\n");
}
}
/**
* Aprior 算法入口
*
* @param data
* @param support
* @return
*/
public List<Set<Integer>> aprior(List<AprioriNode> data, double support){
List<Set<Integer>> freqItems = new ArrayList<Set<Integer>>();
//1. 獲取meta items
List<Set<Integer>> metaItems = metaFreqItems(data, support);
//2. 迭代的獲取item集合超集合。
do{
freqItems.addAll(metaItems);
System.out.println("step--" + freqItems);
metaItems = supFreItems(data, metaItems, support);
}while(metaItems != null && !metaItems.isEmpty());
return freqItems;
}
/**
* 初始化meta item項集,并根據support進行篩選,剪枝
*
* @param data
* @param support
* @return
*/
private List<Set<Integer>> metaFreqItems(List<AprioriNode> data,
double support){
List<Set<Integer>> metaItems = new ArrayList<Set<Integer>>();
for(int i=0; i < AprioriNode.attNames.length; i++){
Set<Integer> set = new HashSet<Integer>();
set.add(i);
metaItems.add(set);
}
rmWithSupport(data, metaItems,support);
return metaItems;
}
/**
*
* @param data
* @param supFreItems
* @param support
* @return
*/
private List<Set<Integer>> supFreItems(List<AprioriNode> data,
List<Set<Integer>> subFreItems,
double support){
if(subFreItems == null || subFreItems.size()<=1){
return null;
}
int size = subFreItems.size();
List<Set<Integer>> list = new ArrayList<Set<Integer>>();
for(int i = 0; i < size - 1; i++){
for(int j = i + 1; j < size; j++){
Set<Integer> s = mergeSet(subFreItems.get(i),
subFreItems.get(j));
if(s!=null)
if(!list.contains(s))
list.add(s);
}
}
rmWithSupport(data,list,support);
return list;
}
/**
* 求兩個集合的并集
*
* @param set1
* @param set2
* @return
*/
private Set<Integer> mergeSet(Set<Integer> set1, Set<Integer> set2){
if(set1 == null || set2 == null)
return null;
Set<Integer> newSet = new HashSet<>(set1);
set2.forEach(it -> {
newSet.add(it);
});
if(newSet.size() > set1.size() + 1)
return null;
return newSet;
}
/**
* 根據支持度,移除不滿足支持度的項集
*
* @param data
* @param items
* @param support
* @return
*/
private void rmWithSupport(List<AprioriNode> data,
List<Set<Integer>> items,
double support){
for(int i = items.size() - 1; i >= 0; i--){
if(support(data, items.get(i)) < support)
items.remove(i);
}
}
/**
* 獲取指定item項集的支持度-support
* @return
*/
private double support(List<AprioriNode> data, Set<Integer> items){
if(data == null || data.size() < 1)
return 0.0;
int count = 0;
for(AprioriNode d:data){
if(isFreqItems(d.getFeatures(), items)) count++;
}
return 1.0 * count / data.size();
}
/**
* 判斷集合是否中的item是否均命中。
*
* @param features
* @param items
* @return
*/
private boolean isFreqItems(boolean[] features, Set<Integer> items){
if(items == null || items.size() < 1) return false;
for(Integer i : items)
if(!features[i]) return false;
return true;
}
}
挖掘結果展示:
??假設我們現在有以下數據記錄:
TID | P1 | P2 | P3 | P4 | P5 |
---|---|---|---|---|---|
1 | 1 | 1 | 0 | 0 | 1 |
2 | 0 | 1 | 0 | 1 | 0 |
3 | 0 | 1 | 1 | 0 | 0 |
4 | 1 | 1 | 0 | 1 | 0 |
5 | 1 | 0 | 1 | 0 | 0 |
6 | 0 | 1 | 1 | 0 | 0 |
7 | 1 | 0 | 1 | 0 | 0 |
8 | 1 | 1 | 1 | 0 | 1 |
9 | 1 | 1 | 1 | 0 | 0 |
10 | 1 | 0 | 1 | 0 | 1 |
??根據不同的support,我們會得到不同的頻繁項集挖掘結果。
support -> 0.1
step--[[0], [1], [2], [3], [4]]
step--[[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4]]
step--[[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 4], [1, 2, 4]]
step--[[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 4], [1, 2, 4], [0, 1, 2, 4]]
Frequent Items -> [[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 4], [1, 2, 4], [0, 1, 2, 4]]
--------------------------------
support -> 0.2
step--[[0], [1], [2], [3], [4]]
step--[[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 4], [1, 2], [1, 3], [1, 4], [2, 4]]
step--[[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 4], [1, 2], [1, 3], [1, 4], [2, 4], [0, 1, 2], [0, 1, 4], [0, 2, 4]]
Frequent Items -> [[0], [1], [2], [3], [4], [0, 1], [0, 2], [0, 4], [1, 2], [1, 3], [1, 4], [2, 4], [0, 1, 2], [0, 1, 4], [0, 2, 4]]
--------------------------------
support -> 0.30000000000000004
step--[[0], [1], [2]]
step--[[0], [1], [2], [0, 1], [0, 2]]
Frequent Items -> [[0], [1], [2], [0, 1], [0, 2]]
--------------------------------
support -> 0.4
step--[[0], [1], [2]]
step--[[0], [1], [2], [0, 1], [0, 2]]
Frequent Items -> [[0], [1], [2], [0, 1], [0, 2]]
--------------------------------
support -> 0.5
step--[[0], [1], [2]]
step--[[0], [1], [2], [0, 2]]
Frequent Items -> [[0], [1], [2], [0, 2]]
--------------------------------
support -> 0.6
step--[[0], [1], [2]]
Frequent Items -> [[0], [1], [2]]
--------------------------------
support -> 0.7
step--[[0], [2]]
Frequent Items -> [[0], [2]]
--------------------------------
support -> 0.7999999999999999
step--[]
Frequent Items -> []
--------------------------------
support -> 0.8999999999999999
step--[]
Frequent Items -> []
--------------------------------
FP-Growth
算法簡介:
??FP-Growth(Frequent Pattern Growth, 頻繁模式增長)算法是韓家煒等人在2000年提出的關聯分析算法,它采取如下分治策略:將提供頻繁項集的數據庫壓縮到一棵頻繁模式樹(FP-tree),但仍保留項集關聯信息。
??在算法中使用了一種稱為頻繁模式樹(Frequent Pattern Tree)的數據結構。FP-tree是一種特殊的前綴樹,由頻繁項頭表和項前綴樹構成。FP-Growth算法基于以上的結構加快整個挖掘過程。FP-Growth比Apriori算法效率更高,在整個算法執行過程中,只需要遍歷數據集2次,就可完成頻繁模式的發現。
提出背景:
??所周知,Apriori算法[1] 在產生頻繁模式完全集前需要對數據庫進行多次掃描,同時產生大量的候選頻繁集,這就使Apriori算法時間和空間復雜度較大。但是Apriori算法中有一個很重要的性質:頻繁項集的所有非空子集都必須也是頻繁的。但是Apriori算法在挖掘額長頻繁模式的時候性能往往低下,于是Jiawei Han提出了FP-Growth算法。
算法思想:
??FP-Growth算法的描述如下:
??1、對于每個頻繁項,構造它的條件投影數據庫和投影FP-tree。
??2、對每個新構建的FP-tree重復這個過程,直到構造的新FP-tree為空,或者只包含一條路徑。
??3、當構造的FP-tree為空時,其前綴即為頻繁模式;當只包含一條路徑時,通過枚舉所有可能組合并與此樹的前綴連接即可得到頻繁模式。
示例說明:
??假設我們有以下數據,假設現在支持度support=3:
TID | P1 | P2 | P3 | P4 | P5 |
---|---|---|---|---|---|
1 | 0 | 1 | 0 | 0 | 1 |
2 | 0 | 1 | 0 | 1 | 0 |
3 | 0 | 1 | 1 | 0 | 1 |
4 | 1 | 1 | 0 | 1 | 0 |
5 | 1 | 0 | 1 | 0 | 1 |
6 | 0 | 1 | 1 | 0 | 0 |
7 | 1 | 0 | 1 | 0 | 0 |
8 | 1 | 1 | 1 | 0 | 1 |
9 | 1 | 1 | 0 | 0 | 0 |
10 | 1 | 0 | 1 | 0 | 0 |
我們可以得到所有item的出現頻率:
item | p1 | p2 | p3 | p4 | p5 |
---|---|---|---|---|---|
frequency | 6 | 7 | 6 | 2 | 4 |
step 1. 生成Header Table
??去除不滿足support的item,并依據frequency按照從大到小進行排序。我們可以得到如下的table。
item | p2 | p1 | p3 | p5 |
---|---|---|---|---|
frequency | 0.7 | 0.6 | 0.6 | 0.4 |
step 2. 生成Header Tree
??再次掃描一次數據庫,依據Header Table中的各個屬性的順序,將每個示例加入到Header Tree中,過程如下圖所示:
??從Header Table出發,將每個相同的屬性串在同一條鏈表上,最終我們可以得到如下結構的FP Tree。
step 3. 挖掘頻繁項集
從FP樹中抽取頻繁項集的三個步驟:
??(1) 從FP樹中獲得條件模式基
??(2)利用每個頻繁項集的條件模式基,構建一顆條件FP樹
??迭代重復步驟(1),(2),直到樹包含一個元素項為止
條件模式基: 以所查找元素項為結尾的路徑集合,每一條路徑都是一條前綴路徑。 根據上圖挖掘的FP-Tree的結果,我們可以得到如下的條件模式基。
頻繁項 | 前綴路徑 |
---|---|
p2 | {}7 |
p1 | {p2}3, {}3 |
p3 | {p2}2, {p2,p1}1, {p1}3 |
p5 | {p2}1, {p2,p3}1, {p2,p1,p3}1 |
結果:
{p2},{p1},{p3},{p5}
{p2, p1},
{p2,p3},{p1,p3}
{p2,p5}
算法實現:
應用
??關聯規則挖掘技術已經被廣泛應用在西方金融行業企業中,它可以成功預測銀行客戶需求。一旦獲得了這些信息,銀行就可以改善自身營銷。銀行天天都在開發新的溝通客戶的方法。各銀行在自己的ATM機上就捆綁了顧客可能感興趣的本行產品信息,供使用本行ATM機的用戶了解。如果數據庫中顯示,某個高信用限額的客戶更換了地址,這個客戶很有可能新近購買了一棟更大的住宅,因此會有可能需要更高信用限額,更高端的新信用卡,或者需要一個住房改善貸款,這些產品都可以通過信用卡賬單郵寄給客戶。當客戶打電話咨詢的時候,數據庫可以有力地幫助電話銷售代表。銷售代表的電腦屏幕上可以顯示出客戶的特點,同時也可以顯示出顧客會對什么產品感興趣。
??再比如市場的數據,它不僅十分龐大、復雜,而且包含著許多有用信息。隨著數據挖掘技術的發展以及各種數據挖掘方法的應用,從大型超市數據庫中可以發現一些潛在的、有用的、有價值的信息來,從而應用于超級市場的經營。通過對所積累的銷售數據的分析,可以得出各種商品的銷售信息。從而更合理地制定各種商品的定貨情況,對各種商品的庫存進行合理地控制。另外根據各種商品銷售的相關情況,可分析商品的銷售關聯性,從而可以進行商品的貨籃分析和組合管理,以更加有利于商品銷售。
??同時,一些知名的電子商務站點也從強大的關聯規則挖掘中的受益。這些電子購物網站使用關聯規則中規則進行挖掘,然后設置用戶有意要一起購買的捆綁包。也有一些購物網站使用它們設置相應的交叉銷售,也就是購買某種商品的顧客會看到相關的另外一種商品的廣告。
??但是在我國,“數據海量,信息缺乏”是商業銀行在數據大集中之后普遍所面對的尷尬。金融業實施的大多數數據庫只能實現數據的錄入、查詢、統計等較低層次的功能,卻無法發現數據中存在的各種有用的信息,譬如對這些數據進行分析,發現其數據模式及特征,然后可能發現某個客戶、消費群體或組織的金融和商業興趣,并可觀察金融市場的變化趨勢。可以說,關聯規則挖掘的技術在我國的研究與應用并不是很廣泛深入。
參考:https://www.cnblogs.com/en-heng/p/5719101.html
https://baike.baidu.com/item/關聯分析/1198018?fr=aladdin
https://www.cnblogs.com/bigmonkey/p/7405555.html