python入门与实践(四)--亲和性分析(Apriori算法)

关联分析步骤

1、发现频繁项集,即计算所有可能组合数的支持度,找出不少于人为设定的最小支持度的集合。

2、发现关联规则,即计算不小于人为设定的最小支持度的集合的置信度,找到不小于认为设定的最小置信度规则。

关联分析的两种关系:简单关联关系和序列关联关系

简单关联关系:

简单关联关系可以从经典的购物中进行分析,购买面包的顾客80%都会购买牛奶,由于面包和牛奶是早餐搭配的必需品,二者搭配构成了早餐的组成部分,这就是一种简单的关联关系。

序列关联关系:

当购买一款新手机后,就会考虑去购买手机膜等手机配件,这就是一种序列关系,不会先买手机膜再买手机的,先后关系是非常明显的,这种关系是一种顺序性的关系,也就是一种序列关联关系。

关联规则:规则就是一种衡量事物的标准,也就是一个算法。
如何设定合理的支持度和置信度?

对于某条规则:(A=a)−>(B=b)(A=a)−>(B=b)(support=30%,confident=60%);其中support=30%表示在所有的数据记录中,同时出现A=a和B=b的概率为30%;confident=60%表示在所有的数据记录中,在出现A=a的情况下出现B=b的概率为60%,也就是条件概率。支持度揭示了A=a和B=b同时出现的概率,置信度揭示了当A=a出现时,B=b是否会一定出现的概率。

(1)如果支持度和置信度闭值设置的过高,虽然可以减少挖掘时间,但是容易造成一些隐含在数据中非频繁特征项被忽略掉,难以发现足够有用的规则;

(2)如果支持度和置信度闭值设置的过低,又有可能产生过多的规则,甚至产生大量冗余和无效的规则,同时由于算法存在的固有问题,会导致高负荷的计算量,大大增加挖掘时间。
Apriori算法
研究人员发现一种所谓的Apriori原理,可以帮助我们减少计算量。Apriori原理是说如果某个项集是频繁的,那么它的所有子集也是频繁的。

例如一个频繁项集包含3个项A、B、C,则这三个项组成的子集{A},{B},{C},{A、B},{A、C}、{B、C}一定是频繁项集。

不过更常用的是它的逆否命题,即如果一个项集是非频繁的,那么它的所有超集也是非频繁的。

已知阴影项集{2,3}是非频繁的。利用这个知识,我们就知道项集{0,2,3},{1,2,3}以及{0,1,2,3}也是非频繁的。也就是说,一旦计算出了{2,3}的支持度,知道它是非频繁的后,就可以紧接着排除{0,2,3}、{1,2,3}和{0,1,2,3}。

Apriori算法是发现频繁项集的一种方法。并不会找出关联规则,关联规则需要在找到频繁项集以后我们再来统计。

Apriori算法的两个输入参数分别是最小支持度和数据集。该算法首先会生成所有单个元素的项集列表。接着扫描数据集来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然后,对剩下来的集合进行组合以生成包含两个元素的项集。接下来,再重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有项集都被去掉。

该算法需要不断寻找候选集,然后剪枝即去掉非频繁子集的候选集,时间复杂度由暴力枚举所有子集的指数级别降为多项式级别,多项式具体系数视底层实现情况而定的。

先用Apriori算法生成频繁项集,接着,通过检测频繁项集中前提和结论的组合,生成关联规则。
第一个阶段,为Apriori算法指定最小值尺度
第二个阶段,根据置信度选取关联规则。

all_ratings = pd.read_csv('D:/python_work/Data Mining with python/ch4/ml-100k/u.data',delimiter='\t',header=None,names=['UserID','MovieID','Rating','Datetime'])
all_ratings['Datetime']=pd.to_datetime(all_ratings['Datetime'],unit='s')

读取数据,首先该数据集的数据之间用制表符“\t”而不是逗号分隔,参数delimiter可以指定。header=None,表示不要把第一行作为表头,names=[],给数据集添加名称。返回的是dataframe格式的数据
第二行的代码用来解析时间戳数据,参数unit=‘s’指示精确到秒,默认为ns,精确到微秒;参数format=’%Y%m%d’ / ‘%Y%m%d %H%m%s’ 表明转换的时间的格式。
dataframe的方法:
sort_values(by=[’’,’’]):排序,参数ascending=False降序排列
sum():求和,默认对每一列进行求和且排除NaN值,参数axis=1时为对每一行求和,参数skipna=False为不排除NaN值。
mean()、min()、max()、median()、std()、var()、diff()
idxmax()、idxmin():返回每一列中最大值、最小值的索引
cumsum():对每一列进行累加
mad():根据平均值计算平均绝对离差 df.mad
corr():计算列与列之间的相关性,不计算包括NA / null值的列。参数:
method:pearson:皮尔逊相关系数、kendall:肯德尔等级相关系数、spearman:斯皮尔曼等级相关系数
min_periods:为获取有效结构,每对列所需的最小观察数据量
返回:关于原始DataFrame列与列之间相关性的DataFrame对象。

Apriori算法的实现:
把发现的频繁项集保存到以项集长度为键的字典中,便于根据长度查找频繁项集。
随着项集长度的增加,项集数随着可用规则的增加而增长一段时间后才开始变少,减少是因为项集达不到最低支持度要求。项集的减少是Apriori算法的优点之一。
频繁项集是一组达到最小支持度的项目,而关联规则由前提和结论组成。

import pandas as pd
#1000名用户和1700部电影
all_ratings = pd.read_csv('u.data',delimiter='\t',header=None,names = ['UserID','MovieID','Rating','Datetime'])
all_ratings['Datetime'] = pd.to_datetime(all_ratings['Datetime'],unit='s')
#all_ratings.head()
#对某部电影评分大于3,判断于喜欢该电影
all_ratings['Favorable'] = all_ratings['Rating']>3
#all_ratings[10:15]
#训练集
ratings = all_ratings[all_ratings['UserID'].isin(range(200))]
 
#用户喜欢某部电影的数据集
favorable_ratings = ratings[ratings['Favorable']]
#每个用户喜欢那些电影 
favorable_reviews_by_users = dict((k , frozenset(v.values)) for k ,v in favorable_ratings.groupby('UserID')['MovieID'])
#每部电影的影迷数量
num_favorable_by_movie = ratings[['MovieID','Favorable']].groupby('MovieID').sum()
#最受欢迎的五部电影
num_favorable_by_movie.sort_values(by = 'Favorable',ascending=False)[:5]
 
#初始化频繁项集
frequent_itemsets ={}
#最小支持度
min_support = 50
 
frequent_itemsets[1] = dict((frozenset((movie_id ,)) , row['Favorable']) for movie_id , row in num_favorable_by_movie.iterrows() if row['Favorable']> min_support)
 
 
from collections import defaultdict
from os import sys
def find_frequent_itemsets(favorable_reviews_by_users ,k_1_itemsets,minsupport):
    counts = defaultdict(int)
    for user , reviews in favorable_reviews_by_users.items():
        for itemset in k_1_itemsets:
            if itemset.issubset(reviews):
                for other_reviewed_movie in reviews - itemset :
                    current_superset = itemset | frozenset((other_reviewed_movie,))
                    counts[current_superset] += 1
    return dict([(itemset , frequency) for itemset , frequency in counts.items() if frequency >= min_support])
for k in range(2,20):
    cur_frequent_itemsets = find_frequent_itemsets(favorable_reviews_by_users ,frequent_itemsets[k-1],min_support)
    frequent_itemsets[k] = cur_frequent_itemsets
    if len(cur_frequent_itemsets) == 0:
        print('Did not find any frequent itemsets of length {0}'.format(k))
        sys.stdout.flush()#把缓存区中的内容输出到终端
        break
    else:
        print('I find {0} frequent itemsets of length {1}'.format(len(cur_frequent_itemsets),k))
        sys.stdout.flush() 
del frequent_itemsets[1]
    
 
candidate_rules = []
for itemset_length , itemset_counts in frequent_itemsets.items():
    for itemset in itemset_counts.keys():
        for conclusion in itemset:
            premise = itemset - set((conclusion ,))
            candidate_rules.append((premise,conclusion))
print(candidate_rules[:5])
 
correct_counts = defaultdict(int)
incorrect_counts = defaultdict(int)
for user , reviews in favorable_reviews_by_users.items():
    for candidate_rule in candidate_rules:
        premise , conclusion = candidate_rule
        if premise.issubset(reviews):
            if conclusion in reviews:
                correct_counts[candidate_rule] += 1
            else:
                incorrect_counts[candidate_rule] += 1
rule_confidence = {candidate_rule : correct_counts[candidate_rule] / float(correct_counts[candidate_rule]+incorrect_counts[candidate_rule]) for candidate_rule in candidate_rules}
 
from operator import itemgetter
sorted_confidence = sorted(rule_confidence.items() ,key = itemgetter(1) ,reverse =True)
for index in range(5):
    print('Rule #{0}'.format(index +1 ))
    (premise , conclusion) = sorted_confidence[index][0]
    print('RuleL if a person recommends {0} they will also recommend {1}'.format(premise , conclusion))
    print(' - Confidence: {0:.3f}'.format(rule_confidence[(premise , conclusion)]))
    print('')            
        
movie_name_data = pd.read_csv('u.item',delimiter='|',header=None , encoding='mac_roman')
movie_name_data.columns = ['MovieID', 'Title', 'Release Date','Video Release', 'IMDB', '', 'Action', 'Adventure','Animation', "Children's", 'Comedy', 'Crime', 'Documentary','Drama', 'Fantasy',
                           'Film-Noir','Horror', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller','War', 'Western']
def get_movie_name(movie_id):
    title_object = movie_name_data[movie_name_data['MovieID'] == movie_id]['Title']
    title= title_object.values[0]
    return title
for index in range(5):
    print('Rule #{0}'.format(index +1 ))
    (premise , conclusion) = sorted_confidence[index][0]
    premise_names = ', '.join(get_movie_name(idx) for idx in premise)
    conclusion_name = get_movie_name(conclusion)
    print('Rule: if a person recommends {0} they will also recommend {1}'.format(premise_names , conclusion_name))
    print(' - Confidence: {0:.3f}'.format(rule_confidence[(premise , conclusion)]))
    print('')  
test_dataset = all_ratings[~all_ratings['UserID'].isin(range(200))]
test_favorable = test_dataset[test_dataset['Favorable']]
test_favorable_by_users = dict((k,frozenset(v.values)) for k ,v in test_favorable.groupby('UserID')['MovieID'] )
 
correct_counts = defaultdict(int)
incorrect_counts = defaultdict(int)
for user ,reviews in test_favorable_by_users.items():
    for candidate_rule in candidate_rules:
        premise , conclusion = candidate_rule
        if premise.issubset(reviews):
            if conclusion in reviews:
                correct_counts[candidate_rule] +=1
            else:
                incorrect_counts[candidate_rule] +=1
test_confidence = {candidate_rule :correct_counts[candidate_rule] / float(correct_counts[candidate_rule] + incorrect_counts[candidate_rule] ) for candidate_rule in candidate_rules}
for index in range(5):
    print('Rule #{0}'.format(index +1 ))
    (premise , conclusion) = sorted_confidence[index][0]
    premise_names = ', '.join(get_movie_name(idx) for idx in premise)
    conclusion_name = get_movie_name(conclusion)
    print('Rule: if a person recommends {0} they will also recommend {1}'.format(premise_names , conclusion_name))
    print(' - Train Confidence: {0:.3f}'.format(rule_confidence[(premise , conclusion)]))
    print(' - Test Confidence: {0:.3f}'.format(test_confidence[(premise , conclusion)]))
    print('')  

你可能感兴趣的:(关联分析,python数据挖掘)