地标 数据关联_数据分析之关联分析

一,关联分析定义

关联分析,就是从大规模数据中,发现对象之间隐含关系与规律的过程,也称为关联规则学习。例如:购物篮分析,最早是为了发现超市销售数据库中不同的商品之间的关联关系。

用于寻找数据集中各项之间的关联关系。根据所挖掘的关联关系,可以从一个属性的信息来推断另一个属性的信息。当置信度达到某一阈值时,可以认为规则成立。

常用的关联分析算法

二,关联规则概念

1.项与项集

项,指我们分析数据中的一个对象;项集,就是若干项的项构成的集合,如集合{牛奶、麦片、糖}是一个3项集

2.支持度

某项集在数据集中出现的概率。即项集在记录中出现的次数,除以数据集中所有记录的数量。

支持度体现的是某项集的频繁程度,只有某项集的支持度达到一定程度,我们才有研究该项集的必要。

support(A)=count(A)/count(dataset)=P(A)

3.置信度

项集A发生,则项集B发生的概率。关联规则{A->B}中,A与B同时出现的次数,除以A出现的次数。

置信度体现的是关联规则的可靠程度,如果关联规则{A->B}的置信度较高,则说明当A发生时,B有很大概率也会发生,这样就可能会带来研究价值。

4.提升度

关联规则{A->B}中,提升度是指{A->B}的置信度,除以B的支持度

提升度体现的是组合(应用关联规则)相对不组合(不应用关联规则)的比值,如果提升度大于1,则说明应用该关联规则是有价值的。如果提升度小于1,说明应用该关联规则起到了负面影响。因此,我们应该尽可能让关联规则的提升度大于1,提升度越大,则应用关联规则的效果越好。(注:如果两个事件相互独立,P(AB)=p(A)*P(B),提升度为1).

5.频繁项集

如果项集I的支持度满足预定义的最小支持度阈值,则I是频繁项集。

通常情况下,我们只会对频繁出现的项集进行研究。如果一个频繁项集含有K个元素,我们称之为频繁K项集。

6.最小支持度

用户或专家定义的衡量支持度的一个阈值,表示项集在统计意义上的最低重要性。

7.最小置信度

用户或专家定义的衡量置信度的一个阈值,表示关联规则的最低可靠性。同时满足最小支持度阈值和最小置信度阈值的规则成为强规则。

目前,设置最小支持度和最小置信度,大部分根据业务经验设置初始值,然后经过多次调整,获取与业务相符的关联规则结果。

三,关联分析过程

关联分析包含2个过程:从数据集中寻找频繁项集,从频繁项集中生成关联规则。

1.寻找频繁项集

首先,我们需要找到所有的频繁项集,即经常出现在一起的对象集合,实际上,找到频繁项集并不复杂,我们只需要按照如下的步骤来进行操作即可:

a.遍历对象之间所有可能的组合(包含单个对象的组合),每种组合构成一个项集。

b.针对每一个项集A,计算A的支持度(A出现的次数除以记录总数)

c.返回所有支持度大于阈值的项集。

以上的理论是没有问题,但是,却很难再实际应用中使用,因为,对象之间任意组合构成的项集,数量可能非常大。例如,在上图中,4个不同的对象,就可以构成15种组合。而对于含有N个对象的数据集,总共可以构成2ⁿ-1种组合,这是一个非常大的数字。

因此,为了降低计算量,我们使用Apriori算法原理进行优化。Apriori算法原理可以解释如下:1)如果X是频繁项集,则其所有子集也是频繁项集。 p(AB)=p(A)*P(B)

2)如果X是非频繁项集,则其所有父集也是非频繁项集。

Apriori算法会从k=1开始,使用两个K项集进行组合,从而产生K+1项集。结合之前介绍的算法原理,我们可知,频繁K+1项集是由两个K项集合组合而成,而对于K+1项集来说,其所有的K项集子集必然都是频繁项集,这就意味着,频繁K+1项集只可能从两个频繁K项集组合产生,因此,当我们在组合的过程中,一旦发现某个K项集不是频繁项集,就可以将其移除,无需再参与后续生成K+1项集的组合。这样一来,就可以大大减少计算量。

例如在图中,假设{2,3}是非频繁项集,则其所有父集也是非频繁项集,故{0,2,3},{1,2,3}与{0,1,2,3}也是非频繁项集。因此,我们就无需使用{2,3}与其他2项集进行组合,去生成3项集了。

Apriori算法流程如下:

2.从频繁项集中生成关联规则

当生成频繁项集后,生成关联规则会相对简单。我们只需要将每个频繁项集拆分成两个非空子集,就可以构成关联规则。当然,一个频繁项集拆分成两个非空子集可能有很多种方式,我们要考虑每一种不同的可能。例如:频繁项集{1,2,3}可以拆分成:{1->2,3},{2->1,3},{3->1,2},{1,2->3}{1,3->2},{2,3->1}

然后,我们针对每一个关联规则,分别计算其置信度,仅保留符合最小置信度的关联规则。

四,程序实现

1.加载数据

def load_data(path):

result=[]

with open(path) as f:

for line in f:

line=line.strip('\n')

#split处理之后返回list,相当于二维数组的形式

result.append(line.split(","))

return result

dataset=load_data("data.csv")

print(len(dataset))

print(dataset[:10])

for i in range(10):

print(i+1,dataset[i],sep="->")7501

[['shrimp', 'almonds', 'avocado', 'vegetables mix', 'green grapes', 'whole weat flour', 'yams', 'cottage cheese', 'energy drink', 'tomato juice', 'low fat yogurt', 'green tea', 'honey', 'salad', 'mineral water', 'salmon', 'antioxydant juice', 'frozen smoothie', 'spinach', 'olive oil'], ['burgers', 'meatballs', 'eggs'], ['chutney'], ['turkey', 'avocado'], ['mineral water', 'milk', 'energy bar', 'whole wheat rice', 'green tea'], ['low fat yogurt'], ['whole wheat pasta', 'french fries'], ['soup', 'light cream', 'shallot'], ['frozen vegetables', 'spaghetti', 'green tea'], ['french fries']]

1->['shrimp', 'almonds', 'avocado', 'vegetables mix', 'green grapes', 'whole weat flour', 'yams', 'cottage cheese', 'energy drink', 'tomato juice', 'low fat yogurt', 'green tea', 'honey', 'salad', 'mineral water', 'salmon', 'antioxydant juice', 'frozen smoothie', 'spinach', 'olive oil']

2->['burgers', 'meatballs', 'eggs']

3->['chutney']

4->['turkey', 'avocado']

5->['mineral water', 'milk', 'energy bar', 'whole wheat rice', 'green tea']

6->['low fat yogurt']

7->['whole wheat pasta', 'french fries']

8->['soup', 'light cream', 'shallot']

9->['frozen vegetables', 'spaghetti', 'green tea']

10->['french fries']

2.编码转换

数据集中含有的都是文本数据,虽然也能操作,但是会影响性能,也会带来一定的繁琐,为了方便起见,我们对文本进行编码,将其转为数值类型。

import itertools

items=set(itertools.chain(*dataset))

#用来保存字符串到编号的映射

str_to_index={}

#用来保存编号到字符串的映射

index_to_str={}

for index,item in enumerate(items):

str_to_index[item]=index

index_to_str[index]=item

#输出结果

print("字符串到编号:",list(str_to_index.items())[:5])

print("编号到字符串:",list(index_to_str.items())[:5])字符串到编号: [('milk', 0), ('soda', 1), ('bacon', 2), ('almonds', 3), ('spaghetti', 4)]

编号到字符串: [(0, 'milk'), (1, 'soda'), (2, 'bacon'), (3, 'almonds'), (4, 'spaghetti')]

#将原始数据进行转换,由字符串映射为数值索引

for i in range(len(dataset)):

for j in range(len(dataset[i])):

dataset[i][j]=str_to_index[dataset[i][j]]

for i in range(10):

print(i+1,dataset[i],sep="->")1->[111, 3, 53, 64, 109, 37, 94, 95, 29, 70, 44, 48, 56, 104, 35, 22, 8, 108, 83, 20]

2->[10, 102, 42]

3->[69]

4->[80, 53]

5->[35, 0, 39, 105, 48]

6->[44]

7->[17, 81]

8->[28, 47, 57]

9->[15, 4, 48]

10->[81]

3.生成1项候选集

生成1项集,即找出不重复的项,为了方便后面的操作,我们将每个对象放入frozenset中

#生成候选1项集

def buildC1(dataset):

item1=set(itertools.chain(*dataset))

return [frozenset([i]) for i in item1]

c1=buildC1(dataset)

c1[frozenset({0}),

frozenset({1}),

frozenset({2}),

.......

4.根据1项候选集生成频繁1项集

当生成候选1项集列表后,我们就可以根据候选1项集列表,生成频繁1项集字典,字典中的key为frozenset类型的对象,该key对象是我们要分析的项集,字典的value为每个项集对应的支持度

#根据候选k项集和最小支持度,生成频繁k项集

def ck_to_lk(dataset,ck,min_support):

support={}#定义项集-频数字典,用来存储每个项集key对应的频数value

for row in dataset:

for item in ck:

#判断项集是否在记录中出现

if item.issubset(row):

support[item]=support.get(item,0)+1

total=len(dataset)

return {k:v/total for k,v in support.items() if v/total>=min_support}

L1=ck_to_lk(dataset,c1,0.05)

L1frozenset({3}): 0.13211571790427942,

frozenset({4}): 0.07145713904812692,

frozenset({11}): 0.0658578856152513,

frozenset({43}): 0.23836821757099053,

frozenset({44}): 0.06332489001466471,

frozenset({75}): 0.07652313024930009,

frozenset({20}): 0.0871883748833489,

frozenset({104}): 0.17970937208372217,

frozenset({52}): 0.06252499666711105,

frozenset({71}): 0.12958272230369283,

frozenset({101}): 0.058525529929342755,

frozenset({46}): 0.1709105452606319,

frozenset({93}): 0.05052659645380616,

frozenset({17}): 0.17411011865084655,

frozenset({41}): 0.09532062391681109,

frozenset({38}): 0.08038928142914278,

frozenset({16}): 0.0510598586855086,

frozenset({67}): 0.1638448206905746,

frozenset({72}): 0.05999200106652446,

frozenset({64}): 0.06839088121583789,

frozenset({25}): 0.09505399280095987,

frozenset({35}): 0.0523930142647647,

frozenset({32}): 0.09825356619117451,

frozenset({112}): 0.0793227569657379,

frozenset({108}): 0.08105585921877083}

5.根据频繁K项集组合生成候选K+1项集

#频繁K项集组合生成候选K+1项集

def lk_to_ck(lk_list):

#保存所有组合之后的候选k+1项集

ck=set()

lk_size=len(lk_list)

if lk_size>1:

k=len(lk_list[0])

for i,j in itertools.combinations(range(lk_size),2):

t=lk_list[i]|lk_list[j]

if len(t)==k+1:

ck.add(t)

return ck

c2=lk_to_ck(list(L1.keys()))

c2

L2=ck_to_lk(dataset,c2,0.05)

L2{frozenset({43, 104}): 0.05092654312758299,

frozenset({17, 43}): 0.05972536995067324,

frozenset({43, 67}): 0.05265964538061592}

6.生成所有频繁项集

#生成所有频繁项集,从原始数据生成频繁项集

def get_L_all(dataset,min_support):

c1=buildC1(dataset)

L1=ck_to_lk(dataset,c1,min_support)

L_all=L1

Lk=L1

while len(Lk)>1:

lk_key_list=list(Lk.keys())

ck=lk_to_ck(lk_key_list)

Lk=ck_to_lk(dataset,ck,min_support)

if len(Lk)>0:

L_all.update(Lk)

else:

break

return L_all

L_all=get_L_all(dataset,0.05)

L_all{frozenset({3}): 0.13211571790427942,

frozenset({4}): 0.07145713904812692,

frozenset({11}): 0.0658578856152513,

frozenset({43}): 0.23836821757099053,

frozenset({44}): 0.06332489001466471,

frozenset({75}): 0.07652313024930009,

frozenset({20}): 0.0871883748833489,

frozenset({104}): 0.17970937208372217,

frozenset({52}): 0.06252499666711105,

frozenset({71}): 0.12958272230369283,

frozenset({101}): 0.058525529929342755,

frozenset({46}): 0.1709105452606319,

frozenset({93}): 0.05052659645380616,

frozenset({17}): 0.17411011865084655,

frozenset({41}): 0.09532062391681109,

frozenset({38}): 0.08038928142914278,

frozenset({16}): 0.0510598586855086,

frozenset({67}): 0.1638448206905746,

frozenset({72}): 0.05999200106652446,

frozenset({64}): 0.06839088121583789,

frozenset({25}): 0.09505399280095987,

frozenset({35}): 0.0523930142647647,

frozenset({32}): 0.09825356619117451,

frozenset({112}): 0.0793227569657379,

frozenset({108}): 0.08105585921877083,

frozenset({43, 104}): 0.05092654312758299,

frozenset({17, 43}): 0.05972536995067324,

frozenset({43, 67}): 0.05265964538061592}

7.生成关联规则

#从频繁项集生成关联规则

def rules_from_item(item):

#定义规则左侧的列表

left=[]

for i in range(1,len(item)):

left.extend(itertools.combinations(item,i))

return [(frozenset(le),frozenset(item.difference(le))) for le in left]

rules_from_item(frozenset({1,2,3}))[(frozenset({1}), frozenset({2, 3})),

(frozenset({2}), frozenset({1, 3})),

(frozenset({3}), frozenset({1, 2})),

(frozenset({1, 2}), frozenset({3})),

(frozenset({1, 3}), frozenset({2})),

(frozenset({2, 3}), frozenset({1}))]

#根据关联规则,计算置信度,保留符合最小置信度的关联规则

def rules_from_L_all(L_all,min_confidence):

#保存所有候选的关联规则

rules=[]

for lk in L_all:

if len(lk)>1:

rules.extend(rules_from_item(lk))

result=[]

for left,right in rules:

support=L_all[left|right]

confidence=support/L_all[left]

lift=confidence/L_all[right]

if confidence>=min_confidence:

result.append({"左侧":left,"右侧":right,"支持度":support,"置信度":confidence,"提升度":lift})

return result

rules_from_L_all(L_all,0.3)[{'左侧': frozenset({17}),

'右侧': frozenset({43}),

'支持度': 0.05972536995067324,

'置信度': 0.3430321592649311,

'提升度': 1.4390851379453289},

{'左侧': frozenset({67}),

'右侧': frozenset({43}),

'支持度': 0.05265964538061592,

'置信度': 0.3213995117982099,

'提升度': 1.3483320682317521}]

8.最终程序

#最终程序,从原始数据生成关联规则

def apriori(dataset,min_support,min_confidence):

L_all=get_L_all(dataset,min_support)

rules=rules_from_L_all(L_all,min_confidence)

return rules

rules=apriori(dataset,0.05,0.3)

rules[{'左侧': frozenset({17}),

'右侧': frozenset({43}),

'支持度': 0.05972536995067324,

'置信度': 0.3430321592649311,

'提升度': 1.4390851379453289},

{'左侧': frozenset({67}),

'右侧': frozenset({43}),

'支持度': 0.05265964538061592,

'置信度': 0.3213995117982099,

'提升度': 1.3483320682317521}]

#最后我们把代码转换成真实的对象名称,同时为了能清晰呈现数据,用dataframe对象进行展示

import pandas as pd

def change(item):

li=list(item)

for i in range(len(li)):

li[i]=index_to_str[li[i]]

return li

df=pd.DataFrame(rules)

df=df.reindex(["左侧","右侧","支持度","置信度","提升度"],axis=1)

df["左侧"]=df["左侧"].apply(change)

df["右侧"]=df["右侧"].apply(change)

df

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值