『数据分析』关于亲和性分析的简单案例讲解

2021-08-05 15:23:23 浏览数 (1)

我们的第84篇原创

作者:才哥


大家好,我是才哥。

今天我们简单介绍一个关于亲和性分析的案例,作为后续关联规则分析的启蒙。

以下案例来自《Python数据挖掘入门与实践(第二版)》第一章1.3亲和性分析的简单示例。

我们先通过本案例了解下关于亲和性的一些概念和工作流程,然后再讨论一些相对更优化的处理方案。

注:该书明确说明为了便于理解,有时更加关注代码和工作流程是否清晰易懂,而不是所采用的方法效率是否最优。

1. 什么是亲和性分析

亲和性分析是一种用于计算样本相似度的数据挖掘方法,这个相似度可以出现在以下几种场景:

  • 网站的用户,拓展服务项目或者定向投放广告;
  • 销售的商品,推荐电影或其他商品(猜你喜欢)。

关于亲和性分析,大家估计都会看到以下这个经典的例子:

在美国的零售业有着这样一个传奇故事,沃尔玛百货将他们的纸尿裤和啤酒并排摆在一起销售,结果纸尿裤和啤酒的销量双双增长!

在今天的案例中,为了简化代码方便理解,我们仅考虑同时购买2件商品的情况,比如以下简单的规则:

用户如果购买了商品X,那么也倾向于购买商品Y

这里我们给出2个用来评价规则的数据指标概念:支持度置信度

支持度 是规则在数据集中出现的次数,即匹配规则的样本数,比如同时购买商品X和Y的交易数;

置信度 是衡量匹配规则的准确度的,比如在购买商品X的交易中同时购买商品Y的比例。

2. 案例详解

本节为书中案例介绍,其使用的工具库如下:

  • numpy
  • collections

2.1. 加载数据集

数据集后台回复 955 领取,有兴趣的同学也可以使用numpy自己构建随机数据组。

代码语言:javascript复制
import numpy as np

dataset_filename = "affinity_dataset.txt"
X = np.loadtxt(dataset_filename)
n_samples, n_features = X.shape
print(f"这份数据集 有 {n_samples} 行 和 {n_features} 列")
print(X[:5])
代码语言:javascript复制
这份数据集 有 100 行 和 5 列
[[0. 1. 0. 0. 0.]
 [1. 1. 0. 0. 0.]
 [0. 0. 1. 0. 1.]
 [1. 1. 0. 0. 0.]
 [0. 0. 1. 1. 1.]]

这份数据集比较小,一共100行5列数据。这里我们将每行数据看做是一次交易行为每列代表一种商品数字1代表有购买0代表没有购买

对于这5列,分别代表 面包、牛奶、奶酪、苹果和香蕉。比如第一行[0. 1. 0. 0. 0.]显示首次交易中的商品,消费者只购买了牛奶,而没有购买其它商品。

代码语言:javascript复制
features = ["面包", "牛奶", "奶酪", "苹果", "香蕉"]

接下来,我们开始研究支持度和置信度的计算。

2.2. 求苹果->香蕉的亲和性

根据概念,置信度需要知道支持度中某商品交易数,这里我们拿香蕉举例,看看其交易数。

计算规则:遍历苹果列数据,值为1则计数 1

代码语言:javascript复制
num_apple_purchases = 0
for sample in X:
    if sample[3] == 1:  # 第4列
        num_apple_purchases  = 1
print(f"含苹果的交易数为:{num_apple_purchases}")
代码语言:javascript复制
含苹果的交易数为:43

接着,我们计算苹果-香蕉的支持度。

计算规则:如果苹果列为1且香蕉列为1,则计数 1

代码语言:javascript复制
rule_valid = 0
for sample in X:
    if sample[3] == 1:  # 购买了苹果
        if sample[4] == 1:
            # 同时也购买了香蕉
            rule_valid  = 1
        else:
            continue
print(f"同时购买苹果和香蕉的交易数为:{rule_valid}")
代码语言:javascript复制
同时购买苹果和香蕉的交易数为:27

以上两个数值都计算出来后,我们可以很方便的获取苹果—>香蕉置信度

计算规则:用苹果-香蕉的支持度/含苹果的交易数

代码语言:javascript复制
support = rule_valid  # 支持度
confidence = rule_valid / num_apple_purchases # 置信度
print(f"苹果-香蕉的支持度为 {support}n苹果—>香蕉的置信度为 {confidence:.3f}")
# Confidence can be thought of as a percentage using the following:
print(f"苹果—>香蕉的置信度百分比为 {confidence*100:.1f}%.")
代码语言:javascript复制
苹果-香蕉的支持度为 27
苹果—>香蕉的置信度为 0.628
苹果—>香蕉的置信度百分比为 62.8%

至此,我们便计算出了苹果-香蕉支持度为27,也就是同时购买苹果和香蕉的交易数为27;而购买苹果的用户中也购买了香蕉的比例为62.8%,这就是置信度。

接着,我们就可以计算全部组合之间的支持度与置信度数据,然后进行对比分析,接着安排相关策略了。

2.3. 亲和性分析

现在我们需要从数据集中计算所有规则(注意:这里的规则我们还是以2个商品为例,不拓展更多组合)。因此需要创建1个字典用于存储匹配的规则,字典的key是X—>Y,值则是支持度;另外一个字典用于存储对应的X—>Y中X出现的次数。

代码语言:javascript复制
from collections import defaultdict

# 初始化2个字典
valid_rules = defaultdict(int)
num_occurences = defaultdict(int)

# 计算支持度及X->Y中X出现次数
for sample in X:
    for premise in range(n_features):
        if sample[premise] == 0: 
            continue   
        # 记录X—>Y中X出现的次数
        num_occurences[premise]  = 1
        for conclusion in range(n_features):
            if premise == conclusion:  # X—>X是无意义的,跳过
                continue
            if sample[conclusion] == 1:
                # X和Y同时出现,则匹配规则的次数 1
                valid_rules[(premise, conclusion)]  = 1
            else:
                continue
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]
    conclusion_name = features[conclusion]
    print(f"规则: {premise_name}—>{conclusion_name}")
    print(f" - 置信度: {confidence[(premise, conclusion)]*100:.2f}%")
    print(f" - 支持度: {support[(premise, conclusion)]}")
    print('-'*20)

输出结果如下:

代码语言:javascript复制
规则: 面包—>牛奶
 - 置信度: 46.43%
 - 支持度: 13
--------------------
规则: 牛奶—>面包
 - 置信度: 25.00%
 - 支持度: 13
--------------------
规则: 奶酪—>香蕉
 - 置信度: 51.28%
 - 支持度: 20
--------------------
规则: 香蕉—>奶酪
 - 置信度: 35.09%
 - 支持度: 20
--------------------
规则: 奶酪—>苹果
 - 置信度: 56.41%
 - 支持度: 22
--------------------
规则: 苹果—>奶酪
 - 置信度: 51.16%
 - 支持度: 22
--------------------
规则: 苹果—>香蕉
 - 置信度: 62.79%
 - 支持度: 27
--------------------
规则: 香蕉—>苹果
 - 置信度: 47.37%
 - 支持度: 27
--------------------
规则: 牛奶—>苹果
 - 置信度: 34.62%
 - 支持度: 18
--------------------
规则: 苹果—>牛奶
 - 置信度: 41.86%
 - 支持度: 18
--------------------
规则: 牛奶—>香蕉
 - 置信度: 51.92%
 - 支持度: 27
--------------------
规则: 香蕉—>牛奶
 - 置信度: 47.37%
 - 支持度: 27
--------------------
规则: 面包—>奶酪
 - 置信度: 17.86%
 - 支持度: 5
--------------------
规则: 奶酪—>面包
 - 置信度: 12.82%
 - 支持度: 5
--------------------
规则: 面包—>香蕉
 - 置信度: 57.14%
 - 支持度: 16
--------------------
规则: 香蕉—>面包
 - 置信度: 28.07%
 - 支持度: 16
--------------------
规则: 牛奶—>奶酪
 - 置信度: 21.15%
 - 支持度: 11
--------------------
规则: 奶酪—>牛奶
 - 置信度: 28.21%
 - 支持度: 11
--------------------
规则: 面包—>苹果
 - 置信度: 32.14%
 - 支持度: 9
--------------------
规则: 苹果—>面包
 - 置信度: 20.93%
 - 支持度: 9
--------------------

我们可以输出置信度前5的规则如下:(大家也可以试着输出支持度前5的规则)

代码语言:javascript复制
def print_rule(premise, conclusion, support, confidence, features):
    premise_name = features[premise]
    conclusion_name = features[conclusion]
    print(f"规则: {premise_name}—>{conclusion_name}")
    print(f" - 置信度: {confidence[(premise, conclusion)]*100:.2f}%")
    print(f" - 支持度: {support[(premise, conclusion)]}")
    print('-'*20)
    
from operator import itemgetter

sorted_support = sorted(support.items(), key=itemgetter(1), reverse=True)
sorted_confidence = sorted(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_rule(premise, conclusion, support, confidence, features)

输出结果如下:

代码语言:javascript复制
Rule #1
规则: 苹果—>香蕉
 - 置信度: 62.79%
 - 支持度: 27
--------------------
Rule #2
规则: 面包—>香蕉
 - 置信度: 57.14%
 - 支持度: 16
--------------------
Rule #3
规则: 奶酪—>苹果
 - 置信度: 56.41%
 - 支持度: 22
--------------------
Rule #4
规则: 牛奶—>香蕉
 - 置信度: 51.92%
 - 支持度: 27
--------------------
Rule #5
规则: 奶酪—>香蕉
 - 置信度: 51.28%
 - 支持度: 20
--------------------

当我们得出以上数据后,就可以进行下一步的销售策略调整了,简单的比如 购买苹果可以获得购买香蕉折扣之类的。具体这里不深入讨论,我们放在后续 关联规则分析中做深入介绍。

3. 算法优化

关于亲和性分析,是有一些数据挖掘算法如Apriori算法来处理的,这里也不展开。那么,这里的算法优化其实是指对案例中非常直接的计算方式进行优化,这里用到的是pandas工具以及我们之前介绍过的itertools《itertools拼装迭代器与生成器》。

主要是计算支持度的逻辑优化,实现的方式有很多种,大家具体查看代码注释吧。

代码语言:javascript复制
import pandas as pd

# 将数据集转化为Dataframe类型
df = pd.DataFrame(X, columns=features)

# 先引入该内置标准库
import itertools

# 求组合(顺序不同组合不同)
it = itertools.permutations(features,2)
rules = list(it)

for rule in rules:
    num_occurence = df[rule[0]].sum() # X—>Y中X在整个数据集中出现次数
    num_rule = df.query(f"{rule[0]} {rule[1]}==2").shape[0] # 同时购买X和Y的交易次数(支持度),这里用两数相加=2来验证
#     df.query(f"{rule[0]}*{rule[1]}==1").shape[0] # 用两数相乘=1 验证
#     df[(df[rule[0]]==1) & (df[rule[1]]==1)].shape[0] # 直接用对应值都是1来验证
#     df[df[rule[0]]==1][rule[1]].sum() # 用X—>Y中,X购买情况下Y列求和(毕竟购买为1,不购买为0)
#     df[list(rule)].all(axis = 'columns').sum() # 由于数据为0和1,适用于all()方法进行bool判断,然后sum求和
    confidence = num_rule / num_occurence # 置信度
    
    print(f'规则:{rule[0]}—>{rule[1]}n- 置信度:{confidence*100:.2f}%n- 支持度:{num_rule}n')

输出结果如下:(我们只展示部分)

代码语言:javascript复制
规则:面包—>牛奶
- 置信度:46.43%
- 支持度:13

规则:面包—>奶酪
- 置信度:17.86%
- 支持度:5

规则:面包—>苹果
- 置信度:32.14%
- 支持度:9

规则:面包—>香蕉
- 置信度:57.14%
- 支持度:16

规则:牛奶—>面包
- 置信度:25.00%
- 支持度:13

为了更方便做后续的操作,我们可以将以上数据转化为DataFrame类型,操作如下:

代码语言:javascript复制
columns=['规则','置信度','支持度']
data = pd.DataFrame(columns=columns)
for rule in rules:
    num_occurence = df[rule[0]].sum() # X—>Y中X在整个数据集中出现次数
    num_rule = df[df[rule[0]]==1][rule[1]].sum() # 用X—>Y中,X购买情况下Y列求和(毕竟购买为1,不购买为0)
    confidence = num_rule / num_occurence # 置信度
    data = data.append(pd.DataFrame([[f'{rule[0]}—>{rule[1]}',confidence,num_rule]],columns=columns),ignore_index=True)
    
data.nlargest(n=5, columns= '置信度', keep='first')

置信度前5

以上就是本文关于亲和性分析的基础介绍,案例来源《Python数据挖掘入门与实践(第二版)》第一章1.3亲和性分析的简单示例,并没有做太多的展开介绍。

我们后续将会拿实际生活中的具体场景再一次进行更详细的介绍,敬请期待哈。

如果你喜欢本篇介绍,还请点赞、在看给个支持哈!(在看超过10个,咱们分享案例数据集和ipynb笔记

0 人点赞