Day3.数据挖掘初探:亲和性分析-商品推荐

学了一段时间的数据可视化和NumPy库的使用,是不是很想伸手施展一下,但是用Python进行数据分析和挖掘还是要有一定的基础储备才可以的,其中涉及到算法、统计学、工程学、最优化理论和计算机等相关领域的知识和概念,越往深入,可能还需要一些地理,城市规划、经济金融等其他领域的知识和概念。要充分发挥数据挖掘的威力,还需要整合相关领域的知识。

不知道你有没有听过Walmart-沃尔玛依靠数据挖掘分析出啤酒和尿布陈列在一起可以提高销量的故事,亦或是我们在网上的浏览和搜索记录给广告商提供了信息,当我们打开程序时便会受到相关产品的推荐,这些都可以算作数据挖掘的应用。虽然数据挖掘对不同问题的实现细节可能千差万别,但是从抽象的来看,是大同小异。

数据挖掘第一步是创建数据集,它能描述真实世界的某一方面。数据集主要包括样本和特征,样本可以是一本书,一个人,一个动物或是其他物体;特征用来描述样本,特征可以是长度、数量、金额等。准备好了数据之后,就是调整算法,如果特征符合什么样的要求,就把它归为一类,比如统计得出成年男性的平均身高为170cm,一个男性的身高大于170算作“高”,小于170算作“矮”。从数据集中抽取特征是数据挖掘过程中的一个重要环节,特征的抽取往往需要对相关领域有深入的了解,或需要多次试错。

在这里我们开始第一个数据挖掘的案例,亲和性分析。亲和性(affinity)是根据样本个体之间的相似度,确定他们关系的亲疏。可以应用在向网站用户定向投放广告,推荐电影和产品,以及根据基因寻找有亲缘关系等方面。

亲和性有多种测量方法,比如统计两件商品一起出售的频率,或者统计顾客购买了商品1再购买商品2的比率,更复杂的有计算个体之间的相似度。亲和性分析最常见的应用是商品推荐服务,背后的思路是:人们过去经常购买的两件商品,以后也很有可能会购买。这个思路很容易转化成算法,根据商家的交易数据,看看同时购买了什么,然后把它推荐给客户,这样比随机推荐会更有效一点,从而提高销量。

我们的案例中,我们分析一个客人购买了X商品,很有可能购买Y商品这个规则,至于多件商品的推荐会更加复杂,先不介绍。下面我们一起来做一个顾客购买的两件商品的亲和性分析,也就是商品推荐。

NumPy加载数据集

首先,创建一个文件夹,将数据集"affinity_dataset.txt"保存在文件夹里,请在公号留言:亲和性分析 获取;然后在同一目录下新建一个Python3文档,加载数据完成后观察数据集。

import numpy as np

#加载数据集
dataset_filename = "affinity_dataset.txt"
X = np.loadtxt(dataset_filename)
n_samples, n_features = X.shape #shape属性的返回值是一个元祖,可以打印出来观察,结果是(100, 5)
print("该数据集含有 {0} 个样本并有 {1} 个特征".format(n_samples, n_features))
'''
该数据集含有 100 个样本并有 5 个特征
'''

# 观察前五行数据
print(X[:5])
'''
[[ 1.  0.  1.  0.  0.]
 [ 0.  0.  1.  1.  1.]
 [ 1.  1.  0.  0.  1.]
 [ 0.  1.  0.  1.  0.]
 [ 0.  1.  0.  0.  1.]]
 数据每行代表一个样本,即为一个交易记录。
 每一列代表的是一种商品,分别是苹果、香蕉、面包、牛奶、香肠。
 0代表没有购买,1代表购买为样本的特征,以第一行为例[ 1.  0.  1.  0.  0.],顾客购买了苹果和面包。
 在这里不考虑购买的数量,只考虑有没有购买。
'''
features = ["apples", "bananas", "bread", "milk", "sausages"] # 样本的特征名,以参考用

计算指定规则的支持度和置信度

如果我们要找出“如果一个客人购买了X商品,那么他们也可能愿意购买Y商品”这样的规则,简单的方法是,找出数据集中的所有同时购买的两件商品的样本,计算哪两个商品的组合出现次数多,并算其占同时购买两个商品的总数的百分比可以得出这两个商品一起购买的概率。

找到规则后,还需要判断其优劣,调好的规则用。衡量规则的优劣有多种方法,常用的是支持度(support)和置信度(confidence)。

支持度指数据集中规则应验的次数,统计起来很简单。有时候还需要对支持度进行规范化,即再除以规则有效前提下的总数量,案例中简单统计规则应验的次数。支持度是衡量给定规则应验的比例,而置信度则是衡量规则准确率如何。即符合给定条件的所有规则里,和当前规则结论一致的比例是多少,计算方法是首先统计当前规则的次数,再同它除以相同的规则数量。

在我们的案例中,规则是“如果一个客人购买了X商品,那么他们也可能愿意购买Y商品”,那我们指定规则“如果一个客人购买了面包,那么他们也可能愿意购买牛奶”,支持度需要统计指定规则的出现的次数,置信度计算客人买面包同时买牛奶这个指定规则,占所有同时购买两个物品的这一指定规则的比。

接下来我们来计算“如果顾客购买了面包,他们也会购买牛奶”这条规则的中购买面包的记录数,并计算其支持度和置信度。

# Sample表示一条交易信息也就是一行元素,判断交易数据中sample[2],第三列关于面包的交易信息是1还是0
num_bread_purchases = 0
for sample in X:
    if sample[2] == 1:  # 顾客购买面包
        num_bread_purchases += 1
print("{0} 位顾客购买了面包".format(num_bread_purchases))
'''
39 位顾客购买了面包
'''

# 下面的语句可以统计多少个样本符合规则
rule_valid = 0 # 规则应验
rule_invalid = 0 # 规则无效
for sample in X:
    if sample[2] == 1:  # 一个顾客购买了面包
        if sample[3] == 1:
            # 这位顾客既买了面包也买了牛奶
            rule_valid += 1
        else:
            # 这位顾客买了面包但没有买牛奶
            rule_invalid += 1
print("{0} 个交易记录符合规则".format(rule_valid))
print("{0} 个交易记录不符合规则".format(rule_invalid))
'''
27 个交易记录符合规则
16 个交易记录不符合规则
'''

# 得到了规则应验的样本个数,和规则无效的样本个数,可以来计算支持度和置信度
support = rule_valid  # 支持度是符合规则的样本的个数
confidence = rule_valid / num_bread_purchases # 置信度是规则准确率
print("支持度为:{0} 置信度为:{1:.3f}.".format(support, confidence))
# 置信度也可以用百分比表示
print("置信度为 {0:.1f}%.".format(100 * confidence))
'''
支持度为:27  置信度为:0.692.
置信度为 69.2%.
'''

计算所有规则的支持度和置信度

上面的的分析只是存在于一种给定规则下的分析,分析顾客买了面包会愿意买牛奶的情况,接下来我们计算所有可能发生的规则的支持度好置信度,X和Y可以两两组合。

我们计算支持度和置信度,需要统计数据集中的所有规则的相关数据。

首先是 *规则应验 *和 规则无效 这两种情况建字典,字典的键是由条件和结论组成的元祖,元祖元素为特征在特征列表中的索引值,不用实际特征名,比如“如果顾客购买了面包,他们也会买牛奶”,用(2,3)表示。如果某个个体的条件和结论均与给定规则相符,就表示给定规则对个体适用,否则无效。

接下来在案例中创建几个字典,用来存放计算结果。这里使用defaultdict(),它的好处是如果查找的键不存在,返回一个默认值,而不会报错。默认值依据字典中的数据类型而定,如:int数据类型不存在则返回0,list返回一个空的[]。需要统计的量有规则应验、规则无效、条件相同的规则数量。

from collections import defaultdict

# 定义三个字典存放统计量
valid_rules = defaultdict(int)
invalid_rules = defaultdict(int)
num_occurences = defaultdict(int)

# 计算过程中需要用到循环,依次对样本的每个个体以及个体的每个特征值进行处理;
# 也就是先寻找到购买X也购买Y的样本,再对样本中的个体(每一条交易记录)的特征值(购买情况)进行判断

for sample in X:
    for premise in range(n_features): # permise代表特征值的索引,使用双重for循环取出索引特征值(1 or 0)并判断是否购买了一件物品
        # 购买了一件物品是购买第二件物品的前提条件
        if sample[premise] == 0:
            continue # 如果没有购买,则继续下一个特征值的判断
        num_occurences[premise] += 1 # 如果购买了,则前提条件的出现次数加1
        for conclusion in range(n_features): # conclusion为第二件购买的商品Y的索引
            if premise == conclusion:  # 一行数据进行第二次判断时,重复的特征索引会被判断两次,这里continue跳过;
                # 即:如果顾客买了X,第二次继续买X;这个规则在这里没有多大的用处
                continue
            if sample[conclusion] == 1: # 如果买了X(permise),还购买了Y(conclusion)
                valid_rules[(premise, conclusion)] += 1 # valid_rules字典的键为条件permise和结论conclusion组成的元祖
            else:
                # 顾客买了X,但没有购买Y
                invalid_rules[(premise, conclusion)] += 1

## 得到所有所有统计量并计算支持度和置信度
support = valid_rules  # 支持度是规则应验的次数

confidence = defaultdict(float) # 置信度是规则应验次数占相同规则的条件发生次数的比
for premise, conclusion in valid_rules.keys(): # 计算置信度,遍历每条规则
    confidence[(premise, conclusion)] = valid_rules[(premise, conclusion)] / num_occurences[premise]

# 格式化输出每条规则
for premise, conclusion in confidence:
    premise_name = features[premise] # features = ["apples", "bananas", "bread", "milk", "sausages"]
    conclusion_name = features[conclusion] # 遍历输出前提条件:第一个购买的物品;结论:第二个购买的物品
    print("规则: 如果一个顾客购买了 {0}, 他们也会买 {1}".format(premise_name, conclusion_name))
    print(" - 置信度: {0:.3f}".format(confidence[(premise, conclusion)]))
    print(" - 支持度: {0}".format(support[(premise, conclusion)]))
    print("")

根据组合数C(5,1)*C(4,1)=20,有20个可能的规则。

运行结果:

规则: 如果一个顾客购买了 apples, 他们也会买 bananas
 - 置信度: 0.464
 - 支持度: 13

规则: 如果一个顾客购买了 bananas, 他们也会买 apples
 - 置信度: 0.250
 - 支持度: 13

规则: 如果一个顾客购买了 bread, 他们也会买 sausages
 - 置信度: 0.513
 - 支持度: 20

规则: 如果一个顾客购买了 sausages, 他们也会买 bread
 - 置信度: 0.351
 - 支持度: 20

规则: 如果一个顾客购买了 bread, 他们也会买 milk
 - 置信度: 0.564
 - 支持度: 22

规则: 如果一个顾客购买了 milk, 他们也会买 bread
 - 置信度: 0.512
 - 支持度: 22

规则: 如果一个顾客购买了 milk, 他们也会买 sausages
 - 置信度: 0.628
 - 支持度: 27

规则: 如果一个顾客购买了 sausages, 他们也会买 milk
 - 置信度: 0.474
 - 支持度: 27

规则: 如果一个顾客购买了 bananas, 他们也会买 milk
 - 置信度: 0.346
 - 支持度: 18
.......

排序找出最佳规则

得到所有规则的置信度和支持度,为了找出 最佳 规则,还需要根据支持度和置信度进行排序。

找出支持度最高的规则,首先对支持度进行排序。字典中的元素(一个键值对)默认为没有前后顺序。字典的items()函数返回包含字典所有元素的列表。itemgetter()可以对嵌套的列表进行排序。itemgetter(1)表示以字典各元素的值(这里为支持度)作为排序依据,参数reverse=True表示降序排列。

# item函数,返回包含字典所有元素的列表
from pprint import pprint
pprint(list(support.items())) # pprint 和 print都是打印,区别在于pprint可以以逗号分隔,对元素分行输出,结构更加清晰
'''
[((0, 1), 13),
 ((1, 0), 13),
 ((2, 4), 20),
 ((4, 2), 20),
 ((2, 3), 22),
 ((3, 2), 22),
 ((3, 4), 27),
 ((4, 3), 27),
 ((1, 3), 18),
 ((3, 1), 18),
 ((1, 4), 27),
 ((4, 1), 27),
 ((0, 2), 5),
 ((2, 0), 5),
 ((0, 4), 16),
 ((4, 0), 16),
 ((1, 2), 11),
 ((2, 1), 11),
 ((0, 3), 9),
 ((3, 0), 9)]
'''

# 列表中的元素含有嵌套结构,使用itemgetter(1)支持度做为键,这样可以对其进行排序
from operator import itemgetter 
sorted_support = sorted(support.items(), key=itemgetter(1), reverse=True)
'''
[((0, 1), 13),
 ((1, 0), 13),
 ((2, 4), 20),
 ((4, 2), 20),
 ((2, 3), 22),
 ((3, 2), 22),
 ((3, 4), 27),
 ((4, 3), 27),
 ((1, 3), 18),
 ((3, 1), 18),
 ((1, 4), 27),
 ((4, 1), 27),
 ((0, 2), 5),
 ((2, 0), 5),
 ((0, 4), 16),
 ((4, 0), 16),
 ((1, 2), 11),
 ((2, 1), 11),
 ((0, 3), 9),
 ((3, 0), 9)]

'''
# 将先前打印的置信度和支持度封装到函数print_rule()里
def print_rule(premise, conclusion, support, confidence, features):
    premise_name = features[premise] # features = ["apples", "bananas", "bread", "milk", "sausages"]
    conclusion_name = features[conclusion] # 遍历输出前提条件:第一个购买的物品;结论:第二个购买的物品
    print("规则: 如果一个顾客购买了 {0}, 他们也会买 {1}".format(premise_name, conclusion_name))
    print(" - 置信度: {0:.3f}".format(confidence[(premise, conclusion)]))
    print(" - 支持度: {0}".format(support[(premise, conclusion)]))
    print("")
    
# 输出支持度最高的前五条规则
for index in range(5):
    print("规则# {0}".format(index + 1))
    (premise, conclusion) = sorted_support[index][0]
    print_rule(premise, conclusion, support, confidence, features
'''
规则# 1
规则: 如果一个顾客购买了 milk, 他们也会买 sausages
 - 置信度: 0.628
 - 支持度: 27

规则# 2
规则: 如果一个顾客购买了 sausages, 他们也会买 milk
 - 置信度: 0.474
 - 支持度: 27

规则# 3
规则: 如果一个顾客购买了 bananas, 他们也会买 sausages
 - 置信度: 0.519
 - 支持度: 27

规则# 4
规则: 如果一个顾客购买了 sausages, 他们也会买 bananas
 - 置信度: 0.474
 - 支持度: 27

规则# 5
规则: 如果一个顾客购买了 bread, 他们也会买 milk
 - 置信度: 0.564
 - 支持度: 22

'''

同理,我们还可以输出置信度最高的规则。

# 根据置信度进行排序
sorted_confidence = sorted(confidence.items(), key=itemgetter(1), reverse=True)

for index in range(5):
    print("规则 #{0}".format(index + 1))
    (premise, conclusion) = sorted_confidence[index][0]
    print_rule(premise, conclusion, support, confidence, features)
'''
规则 #1
规则: 如果一个顾客购买了 milk, 他们也会买 sausages
 - 置信度: 0.628
 - 支持度: 27

规则 #2
规则: 如果一个顾客购买了 apples, 他们也会买 sausages
 - 置信度: 0.571
 - 支持度: 16

规则 #3
规则: 如果一个顾客购买了 bread, 他们也会买 milk
 - 置信度: 0.564
 - 支持度: 22

规则 #4
规则: 如果一个顾客购买了 bananas, 他们也会买 sausages
 - 置信度: 0.519
 - 支持度: 27

规则 #5
规则: 如果一个顾客购买了 bread, 他们也会买 sausages
 - 置信度: 0.513
 - 支持度: 20
'''

观察支持度和置信度的两个top5结果,我们可以发现”顾客买牛奶,也会买香肠“和”顾客买苹果,也会买香肠”这两条的规则的支持度和置信度都很高。超市可以根据这个发现来调整,牛奶、香肠和苹果的摆放位置。

在这里为了更好的观察结果,其实我们还可以进行数据可视化,但是内容较多,小编编辑起来内容过多。从这个例子我们可以看到数据挖掘的洞察力,来探索数据集中各变量的关系,得到新发现。

写在后面:

今天的数据挖掘初探-商品推荐是不是很有意思的~

跟随我们的脚步,继续学习~

记得打卡,在群里分享你的代码和笔记~

好文章,我在看❤
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值