什麼是序列模式
Apriori處理的數據沒有考慮每個客戶在超市多次購物的情況。
序列模式:一個用戶在不同時間點的交易記錄就構成了一個購買序列,
N個用戶的購買序列就組成一個規模爲N的序列數據集.。
Apriori目的:挖掘出頻繁集,找到其中的關聯規則
對於Apriori處理的數據集設置支持度閾值爲:2
則(麪包機、麪包)爲頻繁集
設置可信度爲:0.7
則關聯規則:麪包機 麪包
這條關聯規則的意義:在一次交易中買了麪包機,就很可能買麪包
序列模式目的:挖掘滿足最小支持度的頻繁序列
對於序列模式處理的數據集設置支持度閾值爲:2
則<麪包機 麪包> 爲頻繁序列
這條頻繁序列的意義:如果一個顧客買了麪包機,那麼他以後就回來買麪包
如果我來經營一家超市,通過Apriori算法,我需要將麪包機與麪包放在一起,通過序列模式,我知道如果一段時間內麪包機賣了很多,我將多進貨麪包
序列模式三個算法GSP SPADE PrifixSpan
GSP
GSP算法由Srikant&Agrawal於1996年提出
舉例:
設置支持度閾值爲:3
(1)掃描序列數據庫,對每一項進行支持度統計,得到長度爲1的頻繁序列模式
(2)根據長度爲1的頻繁序列,通過連接操作生成長度爲2的候選序列模式,然後掃描序列數據庫,計算每個候選序列的支持度,通過刪除操作產生長度爲2的頻繁序列模式
(3)根據長度爲2的頻繁序列,通過連接操作生成長度爲3的候選序列模式,然後掃描序列數據庫,計算每個候選序列的支持度,通過刪除操作產生長度爲3的頻繁序列模式
這裏沒有統計<A,AB>,因爲<A,A>是非頻繁序列(根據公理:如果一個序列是頻繁序列,那麼它的子序列也是頻繁序列;如果一個序列是非頻繁序列,那麼包含它的序列也是非頻繁序列)
沒有統計<B,AB>也是同樣的道理。
SPADE
SPADE算法是Zaki在2001年發表的《An efficient algorithm for mining frequent sequences》提出的。
SPADE的算法過程和GSP類似,只是在掃描的時候不是掃描整個數據庫,而是掃描ID_LIST.
舉例:
設置支持度閾值爲:3
1序列的ID_list
紅色框代表非頻繁序列
2序列的ID_list
注意:<A,A> 是由1頻繁序列A的ID_list生成的;<A,B> 是由1頻繁序列A的ID_list和1頻繁序列B的ID_list生成的;<(A,B)> 也是由1頻繁序列A的ID_list和1頻繁序列B的ID_list生成的
3序列的ID_list
注意:<A,(AB)> 沒有生成ID_list,因爲<A,A> 是非頻繁的; <B,(AB)> 沒有生成ID_list,因爲 <B,A> 是非頻繁的;
SPADE:通過便利一次數據庫得到的經驗知識來降低多次對數據庫的掃描。
SPADE不僅通過減少對數據庫的掃描降低I/O成本通過搜索ID_list降低計算成本。
PrefixSpan
舉例:
設置支持度閾值爲:3
(1)對數據庫中所有的項進行支持度統計,將支持度低於閾值的項從數據庫中刪除,得到長度爲1的前綴
(2)對於每個長度爲1滿足支持度要求的前綴進行遞歸挖掘:
注意:
B和_B不一樣,前者是前綴A與B在不同的項集,後者是和前綴A在相同的項集中。
a,b,c 分別對應PrefixSpan算法步驟第二步中的a,b,c
(3)重複第二步
GSP實驗
數據集採用的是Zaki在2001年發表的《An efficient algorithm for mining frequent sequences》中的一個例子。
#coding=utf-8
import itertools
import sys
import datetime
#-----------------
class GSP(object):
def __init__(self):
self.queue = []
#----------------------------------------------------------#
# 計算freq1 #
#----------------------------------------------------------#
def freq1(self, data, frequent_num):
appear = ''
freq1 = []
appear_ele = []
appear_ele2 = []
for i in range(len(data)):
appear = ''
for j in range(len(data[i])):
appear += data[i][j]
appear_ele += list(set(appear))
# print(appear_ele)
appear_ele2 = list(set(appear_ele))
# print(appear_ele2)
for item in appear_ele2:
itmes = appear_ele.count(item)
if itmes >= frequent_num:
freq1.append(item)
print('頻繁1項集爲:%s' %freq1)
return freq1
#----------------------------------------------------------#
# 計算freq_more #
#----------------------------------------------------------#
def freq_more(self, data, freq1):
queue = []#所有的備選序列放在這裏面
queue_new = []#最終結果在這裏面
top = 0 #這個是queue_new的隊尾標號
times = 3
while True:
if (queue_new == []): #爲空則代表這是第一次遍歷,python中的&&是and,||是or
for i in range(len(freq1)):
for j in range(i+1, len(freq1)):
item = freq1[i] + freq1[j]
queue.append(item)
for i in range(len(freq1)):
for j in range(len(freq1)):
if j != i:
item = freq1[i] +'->'+ freq1[j]
queue.append(item)#第一次遍歷後全部可能出現的情況
for i in range(len(queue)):
freq_item = self.isFreq(queue[i], data)
if freq_item != 0:
queue_new.append(freq_item)
queue = []#清空queue(備選序列)
if (queue_new != []): #後幾次遍歷時要把所有的情況寫入空的queue中
if top == len(queue_new) - 1: #表示沒有新加入元素,那麼終止 while 循環
print('頻繁多項集爲:%s' %queue_new)
break
else:
demo_list = []#專門放'AB','BF','AF'這樣的頻繁序列,後面將他們合成爲更多成員的備選頻繁序列
for i in range(top, len(queue_new)):
if '->' not in queue_new[i]:
demo_list.append(queue_new[i])
demo_string = self.List_to_String(demo_list) #將列表中的元素拼接成字符串,諸如拼成'ABBFAF'
demo_ele = "".join(set(demo_string)) #刪除串中的重複元素,輸出'ABF'
if len(demo_ele) >= times:
if len(demo_ele) == times :#那麼demo_ele是唯一的備選成員
queue.append(demo_ele)
times += 1
else: #否則對備選字母進行排列組合,比如'ABCDE',一共能排列出10鍾情況,並把它們推入queue(待判斷成員隊列)
combin = self.Combinations(demo_ele, times)
for i in range(len(combin)):
queue.append(combin[i])
times += 1
###-----####至此已經把備選頻繁尋列推入 queue ####-----###
queue = self.Make_time_queue(top, freq1, queue, queue_new)
###-----#### 至此已經把 queue 放滿了備選成員 ####-----###
top = len(queue_new)# 更新隊尾指針 top 的位置
###-----#### 檢測 queue 中的備選序列是否頻繁 ####-----###
for i in range(len(queue)):
freq_item = self.isFreq(queue[i], data) #---->> isFreq
if freq_item != 0: #如果這個成員是頻繁的
queue_new.append(freq_item)
queue = []
#將列表中的字母合併成字符串
def List_to_String(self, list):
demo_string = ''
for i in range(len(list)):
demo_string = demo_string + list[i]
return demo_string
#demo_ele是待排列的字符串, times是將它們排列成幾個元素
def Combinations(self, item, times):
demo_list = []
combin = []
element = ''
for i in range(1, len(item) +1):
iter = itertools.combinations(item, i)
demo_list.append(list(iter))
demo_combin = demo_list[times -1]
for i in range(len(demo_combin)):
for j in range(len(demo_combin[0])):
element += demo_combin[i][j]
combin.append(element)
element = ''
return combin
#判斷item是不是頻繁的
def isFreq(self, item, data):
num = 0
if '->' not in item: #類似如'ABF'
for i in range(len(data)):
for j in range(len(data[i])):
if self.isIn_Item(item, data, i, j) != 0:
num += 1
if num >= 2:
return item
else:
return 0
else: #類似如‘D->B->A’
item0 = item.split('->')
for i in range(len(data)):
array = 0
j = 0
while True:
if array == len(item0) or j == len(data[i]):
break
if len(item0[array]) >= 2: #如果類似 'BA' 形式
if self.isIn_Item(item0[array], data, i, j) == 1:
array += 1
j += 1
else:
j += 1
else:
if item0[array] in data[i][j]:
array += 1
j += 1
else:
j += 1
if array == len(item0):
num += 1
if num >= 2:
return item
else:
return 0
#判斷 item 是否在 data[i][j]中
def isIn_Item(self, item, data, i, j):
demo_num = 0
for k in range(len(item)):
if item[k] in data[i][j]:
demo_num += 1
if demo_num == len(item):
return 1
else:
return 0
#
def isIn_Time(self, item0, data, i, j):
num = 0
item0_lenth = len(item0)
if item0_lenth == 2:
for m in range(j+1, len(data[i])):
if item0[1] in data[i][m]:
num += 1
else:
if item0[item0_lenth -2] in data[i][j]:
for m in range(j+1, len(data[i])):
if item0[item0_lenth -1] in data[i][m]:
num += 1
break
return num
#創造新的備選時間序列
def Make_time_queue(self, top, freq1, queue, queue_new):
for i in range(top, len(queue_new)):
# for j in range(len(freq1)):
if '->' not in queue_new[i]:
difference = self.Difference(queue_new[i], freq1)
for j in range(len(difference)):
queue.append(difference[j] + '->' +queue_new[i]) #諸如 'D->AB'
queue.append(queue_new[i] + '->' +difference[j]) #諸如 'AB->D'
else:
difference = self.Difference(queue_new[i], freq1)
for j in range(len(difference)):
queue.append(queue_new[i] + '->' + difference[j]) #諸如'B->A' 擴展成 'B->A->D'
return queue
#尋找兩個字符串中的不同字母,並提取出來
def Difference(self, item, freq1):
demo_list = []
if '->' not in item:
for i in range(len(freq1)):
if freq1[i] not in item:
demo_list.append(freq1[i])
else:
demo_item = item.split('->') #將諸如'A->B'拆分成 'A','B'
demo_item_string = self.List_to_String(demo_item) #合併成'AB'
for i in range(len(freq1)):
if freq1[i] not in demo_item_string:
demo_list.append(freq1[i])
return demo_list
# main #
#----------------------------------------------------------#
data = {0:['CD','ABC','ABF','ACDF'],
1:['ABF','E'],
2:['ABF'],
3:['DGH','BF','AGH']}
starttime = datetime.datetime.now()
g= GSP()
freq1 = g.freq1(data,2)
g.freq_more(data, freq1)
endtime = datetime.datetime.now()
print(endtime - starttime)
實驗截圖
問題:
序列模式挖掘出頻繁序列假設爲 <A,B> ,這表明A發生後隔一段時間可能發生B,但隔一段時間到底是隔多長時間呢?