基于最大参与率挖掘稀有特征的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}}。算法从P2生成了候选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])

运行结果为:
在这里插入图片描述

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值