基于最大参与率挖掘稀有特征的co-location模式
理论
回顾之前传统的co-location模式挖掘,都是基于一个团,去挖掘出参与度大于我们给定的阈值min_prev,即PI© >= min_prev,则我们说模式c是一个频繁的co-location模式。
在传统的基于团的挖掘算法中,参与率和参与度会随着co-location模式阶的增大而单调递减,根据单调性可以对co-location模式挖掘进行有效的剪枝。然而,如果在稀有空间特征存在的数据集合中挖掘co-location模式,用传统的方法将导致一些有趣的co-location模式被排除了。
比如,国家一级保护动物大熊猫全球只有1000多只,然而竹子广泛分布于东亚、东南亚和印度洋及太平洋岛屿等2200万公顷的区域内,如果按照传统的挖掘算法,对于1000:220000000的数据,毫无疑问{熊猫,竹子}这中模式会被剪枝掉,但是我们又想发现这种有趣的模式,就需要引入稀有对象这个概念。在空间co-location模式{大熊猫,竹子生长地区}中,大熊猫是稀有的。
挖掘带有稀有空间特征的co-location模式的意义在于当一个模式中存在稀有特征时,仍能够准确的挖掘频繁co-location模式。
最大参与率
不同于之前的参与率定义PI=min( PR(f1,c), PR(f2,c),…,PR(fk,c) ),最大参与率定义为:对于co-location模式 c = {f1, f2, … , fk},模式c的参与度为模式中所有特征的参与率中的最大值,即 maxPR=max( PR(f1,c), PR(f2,c),…,PR(fk,c) )
最大参与率的概念的引入解决了稀有co-location模式的发现问题,但是最大参与率不具有上面所说的单调性。
如图中,空间特征集 {A,B} ∈ {A,B,C}。然而 maxPR({A,B}) = max{0.6, 0.5} = 0.6,
maxPR({A,B,C}) = max{0.4, 0.5, 0.75} = 0.75。maxPR({A,B}) < maxPR({A,B,C}),所以最大参与率不是单调的。
针对这个问题,后面会提出基于最小加权参与率来解决这个问题,那是后面的事情了,本篇文章主要基于最大参与率来挖掘co-location模式。
方法一:基于join—based方法的扩展 Min—Max方法
因为最大参与率不具有单调性,因此我们不能直接用类似Apriori中的剪枝方法,即利用超集和子集的关系进行剪枝。
设置两个阈值min_prev和min_maxPR,挖掘满足条件:
(1)PI(P)> min_prev
(2)maxPR(P)> min_maxPR
的模式P。
Min—Max方法举例:
如上图的空间关系,假设min_prev = 0且min_maxPR = 0.75,给出从2阶模式生成3阶模式的迭代步骤:
- 首先,我们有P
~2~ = { {A,B}, {A,C}, {B,C}} 且 P~2~ = {{A,C}, {B,C}}。算法从P
2生成了候选3阶模式C3 = {{A,B,C}}。候选模式{A,B,C}的表实例由两个2阶模式的表实例连接得到。 - 当参与度阈值为0时,算法会找出所有模式。如果min_prev 大于0,一些maxPR 很高但参与度很低的模式将会被丢弃。例如上述例子中,如果min_prev 设置为0.55,而 PI({A,B}) = 0.5,那么{A,B} 不属于P`2。模式{A,C} 和 {B,C} 不会连接生成候选模式{A,B,C},尽管maxPR({A,B,C}) = 0.75 >= min_maxPR。
Min—Max算法的一个优点时用户可以根据min_prev 值指定感兴趣的频繁模式。缺点是如果用户想挖掘完整的模式,算法会产生大量候选并对他们进行测试,即使阈值min_maxPR设的很高。
代码
import itertools
import copy
import math
import numpy as np
#数据可视化
def drawData(E,R):
values=[x[2] for x in E]
x = np.array(values)[:,0]
y = np.array(values)[:,1]
n = [x[0]+str(x[1]) for x in E]
fig,ax=plt.subplots(figsize=(12, 7))
ax.scatter(x,y)
for i,txt in enumerate(n):
ax.annotate(txt,(x[i],y[i]))
for i in range(len(values)-1):
for j in range(i+1,len(values)):
if math.sqrt((values[j][0]-values[i][0])**2 + (values[j][1]-values[i][1])**2) < R:
ax.plot([values[i][0],values[j][0]],[values[i][1],values[j][1]])
#定义行实例类,用于保存行实例
class Row_instance:
def __init__(self, colocation, t_list):
self.colocation = colocation
self.t_list = t_list
def appende_row(self, row):
self.t_list.extend(row)
#获取每个特征的实例个数
def get_count_ET(E,ET):
count = {}
for i in ET:
count[i] = 0
for j in E:
if j[0] == i:
count[i] += 1
return count
#计算欧式距离
def distance(point1,point2):
distance = ((point1[0]-point2[0])**2 + (point1[1]-point2[1])**2)**0.5
#判断两个实例之间是否相邻
def isNiber(instance1,instance2,R):
if distance(instance1[2],instance2[2]) <=R :
return 1
else:
return 0
#根据几何关系生成2阶表实例
def createT2(E,R):
T2 = []
table = {}
for i in range(len(E)-1):
for j in range(i,len(E)):
row = []
if E[i][0]!=E[j][0] and isNiber(E[i],E[j],R):
instance = []
instance.append(E[i][0]+str(E[i][1]))
instance.append(E[j][0]+str(E[j][1]))
T2.append(instance)
T_class = Table_2_Tclass(T2)
return T_class
#从频繁k项生成k+1候选模式
def Pk_2_Cplus(P_prev,k):
Ct = set()
Ck = []
P_prev_size = len(P_prev)
# 若lk_size<=1则不需要再组合
if P_prev_size > 1:
for c in itertools.combinations((P_prev), 2):
c = list(c)
t = set(sum(c, []))
# 两两组合后项集长度是k+1,否则不要
if len(t) == k:
f = frozenset(t)
if f not in Ct:
Ct.add(f)
list_t = list(t)
list_t.sort()
Ck.append(list_t)
return list(Ck)
#获取参与度PI
def get_canyulv(p,count_ET):
canyulv = 1
for key in p.keys():
canyudu = p[key]/count_ET[key]
if canyudu < canyulv:
canyulv = canyudu
return canyulv
#获取最大参与率
def get_maxPR(p,count_ET):
maxPR= 0
for key in p.keys():
canyudu = p[key]/count_ET[key]
if canyudu > maxPR:
maxPR= canyudu
return maxPR
#从k阶表实例中生成频繁k项集
def Tk_2_Pk(T_cu,C,count_ET,min_prev,min_maxPR):
P = []
for co in C:
if co in [co_list.colocation for co_list in T_cu]:
P.append(co)
T = T_cu.copy()
for row in T_cu:
p = {}
for i in range(len(row.colocation)):
p[row.colocation[i]] = len(set([wai[i] for wai in row.t_list]))
canyulv = get_canyulv(p,count_ET)
maxPR = get_maxPR(p,count_ET)
if canyulv <= min_prev and maxPR >= min_maxPR:
T.remove(row)
P.remove(row.colocation)
return T,P
#从k阶候选项集中生成k阶表实例
def Ck_2_Tk(C,T_prev):
T_list = []
for t_list in T_prev:
for instance in t_list.t_list:
row = []
for i in range(len(t_list.colocation)):
row.append(t_list.colocation[i]+str(instance[i]))
T_list.append(row)
T = []
T_tem = []
for c in itertools.combinations((T_list), 2):
new_list = list(set(c[0]).union(set(c[1])))
new_list.sort()
if new_list in T_tem:
continue
T_tem.append(new_list)
if len(new_list) == len(c[0])+1:
p = 1
for chek in itertools.combinations((new_list), len(new_list)-1):
if list(chek) not in T_list:
p = 0
if p == 1:
T.append(new_list)
T_class = Table_2_Tclass(T)
return T_class
def Table_2_Tclass(T):
table = {}
for l in T:
co = ""
row_instance = []
for i in range(len(l)):
co += str(l[i])[0]
row_instance.append(str(l[i])[1])
if str(co) not in table.keys():
table[str(co)] = [row_instance]
else:
table[str(co)].append(row_instance)
colocations = [list(key) for key in table.keys()]
row_lists = [value for value in table.values()]
new_T = []
for i in range(len(colocations)):
new_T.append(Row_instance(colocations[i],row_lists[i]))
return new_T
if __init__ == 'main':
min_prev = 0.3
min_maxPR = 0.75
ET = ["A","B","C","D"]
E = [[]] #E是空间数据集合,输入自己的空间数据集合,按下面的格式
#E = [["A",1,[24,14]]
# ,["A",2,[64,44]]
# ,["A",3,[46,24]]
# ,["A",4,[63,7]]
# ,["B",1,[13,3]]
# ,["B",2,[19,45]]
# ,["B",3,[47.5,10.5]]
# ,["B",4,[62,52]]
# ,["B",5,[13,50]]
# ,["C",1,[39,11]]
# ,["C",2,[52,50]]
# ,["C",3,[40,36]]
# ,["D",1,[40,20]]
# ,["D",2,[6,10]]]
R = 16 #设最小邻近关系的距离为16
drawData(E,R)
count_ET = get_count_ET(E,ET)
P = [[i] for i in ET]
k=2
T_cu = createT2(E,R)
C = Pk_2_Cplus(P,k)
print("C2")
print(C)
T,P = Tk_2_Pk(T_cu,C,count_ET,min_prev,min_maxPR)
print("T2")
for i in T:
print(i.colocation,i.t_list)
print("P2")
print(P)
while(1):
k+=1
C = Pk_2_Cplus(P,k)
if len(C)==0:
break
print("C{}".format(k))
print(C)
Tcu = Ck_2_Tk(C,T)
T,P = Tk_2_Pk(Tcu,C,count_ET,min_prev)
print("T{}".format(k))
for i in T:
print(i.colocation,i.t_list)
print("P{}".format(k))
print(P)
方法二:基于maxPR的弱单调性 maxPrune方法
最大参与率的弱单调性: 假设P是一个k阶的co-location模式,那么至多存在一个 (k-1)阶的子模式P` ∈ P 使得maxPR(P`) < maxPR(P)
基于上述的弱单调性理论,如果一个k阶模式的maxPR大于阈值min_maxPR,那么在它的k个(k-1)阶子模式中至少有 k-1 个子模式的maxPR大于min_maxPR。因此,我们可以修改产生候选模式的过程,只有那些至多拥有一个(k-1)阶子模式的maxPR值小于min_maxPR的k阶模式才能成为候选模式。
设P 是maxPR值大于等于阈值min_maxPR的一个k阶模式(k>=3),那么存在两个(k-1)模式P1 和 P2,使得:
(1) P1 ∈ P, P2∈ P
(2) P1 和 P2 的前k-3个特征相同
(3) P1 和 P2 中包含 P 中的第k个特征、第k-1个特征、第k-2个特征这三者之一,但不同时包含这三者之二
(4) P1 和 P2的maxPR值都小于min_maxPR
maxPrune算法举例:
图中展示了一个包含特征集F={A,B,C,D}的空间数据集合,满足空间邻近关系R的实例用直线相连。假设min_maxPR = 0.5。首先,所有1阶co-location模式都是频繁的,用几何方法生成候选2阶模式以及他们的表实例,如下表
基于表实例,计算得出他们的maxPR,可以得到2阶频繁co-location模式P2 = {{A,B}, {A,C}, {A,D}, {B,C}, {B,D}, {C,D}}。接着生成候选3阶模式,{A,B}连接{A,C},{A,B}连接{A,D},{A,C}连接{A,D},以及{B,C}连接{B.D},生成候选3阶模式 C3 = {{A,B,C}, {A,B,D}, {A,C,D}, {B,C,D}}。
重复上述过程,得到4阶候选模式 C4 ={{A,B,C,D}}。
与Min-Max算法相比,maxPrune算法不需要参与度阈值就能挖掘出所有maxPR大于等于阈值min_maxPR的co-location模式完全集。相比min_prev = 0时的Min-Max算法而言,在挖掘过程中,maxPrune算法生成的候选集更少,大大降低了表实例生成和检测的计算开销。
代码
导入包
import itertools
import copy
import math
import numpy as np
import matplotlib.pyplot as plt
将数据可视化
def drawData(E,R):
values=[x[2] for x in E]
x = np.array(values)[:,0]
y = np.array(values)[:,1]
n = [x[0]+str(x[1]) for x in E]
fig,ax=plt.subplots(figsize=(12, 7))
ax.scatter(x,y)
for i,txt in enumerate(n):
ax.annotate(txt,(x[i],y[i]))
for i in range(len(values)-1):
for j in range(i+1,len(values)):
if math.sqrt((values[j][0]-values[i][0])**2 + (values[j][1]-values[i][1])**2) < R:
ax.plot([values[i][0],values[j][0]],[values[i][1],values[j][1]])
看下数据长啥样
ET = ["A","B","C","D"]
E = [["A",1,[13,50]]
,["A",2,[15,42]]
,["A",3,[31,50]]
,["A",4,[30,39]]
,["A",5,[60,50]]
,["A",6,[58,42]]
,["A",7,[24,20]]
,["A",8,[50,31]]
,["B",1,[27,44]]
,["B",2,[38,39]]
,["B",3,[16,20]]
,["C",1,[26,52]]
,["C",2,[65,45]]
,["C",3,[20,25]]
,["C",4,[21,15]]
,["C",5,[48,21]]
,["C",6,[60,22]]
,["D",1,[20,47]]
,["D",2,[53,26]]]
R = 11 #设最小邻近关系的距离为16
drawData(E,R)
结果为
定义行实例类,用于保存行实例
class Row_instance:
def __init__(self, colocation, t_list):
self.colocation = colocation
self.t_list = t_list
def appende_row(self, row):
self.t_list.extend(row)
获取每个特征的实例个数
def get_count_ET(E,ET):
count = {}
for i in ET:
count[i] = 0
for j in E:
if j[0] == i:
count[i] += 1
return count
计算欧式距离
def distance(point1,point2):
distance = ((point1[0]-point2[0])**2 + (point1[1]-point2[1])**2)**0.5
return distance
判断两个实例之间是否相邻
def isNiber(instance1,instance2,R):
if distance(instance1[2],instance2[2]) <=R :
return 1
else:
return 0
根据几何关系生成二阶表实例
def createT2(E,R):
T2 = []
table = {}
for i in range(len(E)-1):
for j in range(i,len(E)):
row = []
if E[i][0]!=E[j][0] and isNiber(E[i],E[j],R):
instance = []
instance.append(E[i][0]+str(E[i][1]))
instance.append(E[j][0]+str(E[j][1]))
T2.append(instance)
T_class = Table_2_Tclass(T2)
return T_class
获取最大参与率
def get_maxPR(p,count_ET):
maxPR = 0
for key in p.keys():
canyudu = p[key]/count_ET[key]
if canyudu > maxPR:
maxPR = canyudu
return maxPR
从k-1阶频繁项到k阶候选项集
def Pk_2_Cplus(P_prev,P2_dic,k,min_maxPR):
Ct = set()
Ck = []
P_prev_size = len(P_prev)
# 若lk_size<=1则不需要再组合
if P_prev_size > 1:
if k < 3:
for c in itertools.combinations(P_prev, 2):
c = list(c)
t = set(sum(c, []))
# 两两组合后项集长度是k,否则不要
if len(t) == k:
f = frozenset(t)
if f not in Ct:
Ct.add(f)
list_t = list(t)
list_t.sort()
Ck.append(list_t)
else:
sublist = list(itertools.combinations(P_prev, 2))
lenth = len(sublist)
for i in range(lenth):
sub_2 = list(sublist[i])
if sub_2[0][:k-3] == sub_2[1][:k-3]:
t = set(sum(sub_2, []))
if len(t) == k:
maxPR_1 = P2_dic[str(sub_2[0])]
maxPR_2 = P2_dic[str(sub_2[1])]
if maxPR_1 >= min_maxPR and maxPR_2 >= min_maxPR:
f = frozenset(t)
if f not in Ct:
Ct.add(f)
list_t = list(t)
list_t.sort()
Ck.append(list_t)
return list(Ck)
从k阶表实例生成k阶频繁模式
def Tk_2_Pk(T_cu,C,count_ET,min_maxPR):
P = []
P_dic = {}
for co in C:
if co in [co_list.colocation for co_list in T_cu]:
P.append(co)
T = T_cu.copy()
for row in T_cu:
p = {}
for i in range(len(row.colocation)):
p[row.colocation[i]] = len(set([wai[i] for wai in row.t_list]))
maxPR = get_maxPR(p,count_ET)
if maxPR < min_maxPR:
T.remove(row)
P.remove(row.colocation)
else:
P_dic[str(row.colocation)] = maxPR
return T,P,P_dic
根据候选集生成表实例集合
def Ck_2_Tk(C,T_prev):
T_list = []
for t_list in T_prev:
for instance in t_list.t_list:
row = []
for i in range(len(t_list.colocation)):
row.append(t_list.colocation[i]+str(instance[i]))
T_list.append(row)
T = []
T_tem = []
for c in itertools.combinations((T_list), 2):
new_list = list(set(c[0]).union(set(c[1])))
new_list.sort()
if new_list in T_tem:
continue
T_tem.append(new_list)
if len(new_list) == len(c[0])+1:
p = 1
for chek in itertools.combinations((new_list), len(new_list)-1):
if list(chek) not in T_list:
p = 0
if p == 1:
T.append(new_list)
T_class = Table_2_Tclass(T)
return T_class
工具方法,解决list不能做键,而转为对象
def Table_2_Tclass(T):
table = {}
for l in T:
co = ""
row_instance = []
for i in range(len(l)):
co += str(l[i])[0]
row_instance.append(str(l[i])[1])
if str(co) not in table.keys():
table[str(co)] = [row_instance]
else:
table[str(co)].append(row_instance)
colocations = [list(key) for key in table.keys()]
row_lists = [value for value in table.values()]
new_T = []
for i in range(len(colocations)):
new_T.append(Row_instance(colocations[i],row_lists[i]))
return new_T
主函数
if __init__ = 'main':
count_ET = get_count_ET(E,ET)
P = [[i] for i in ET]
P_dic = {}
k=2
T_cu = createT2(E,R)
C = Pk_2_Cplus(P,P_dic,k,min_maxPR)
print(C)
T,P,P_dic = Tk_2_Pk(T_cu,C,count_ET,min_maxPR)
print("T2")
for i in T:
print(i.colocation,i.t_list)
print("P2")
print(P)
print("P2_dic")
for i in P2_dic.keys():
print(i,P2_dic[i])
while(1):
k+=1
C = Pk_2_Cplus(P,P_dic,k,min_maxPR)
if len(C)==0:
break
print("C{}".format(k))
print(C)
Tcu = Ck_2_Tk(C,T)
T,P,P_dic = Tk_2_Pk(Tcu,C,count_ET,min_maxPR)
if bool(T):
print("T{}".format(k))
for i in T:
print(i.colocation,i.t_list)
if bool(P):
print("P{}".format(k))
print(P)
if bool(P_dic):
print("P{}_dic".format(k))
for i in P_dic.keys():
print(i,P_dic[i])
运行结果为: