概述

模型概念

购物篮分析(Market Basket Analysis)是关联规则挖掘的应用场景,通过研究用户在一次购买行为中放入购物篮中不同商品之间的关联,研究顾客的购买行为,从而辅助零售企业制定营销策略的一种关联分析方法。

购物篮分析使用分析商品关联性可以用以下指标进行衡量:

指标定义概率描述举例说明
支持度支持度是指 A 商品和 B 商品同时被购买的概率,或者说某个商品组合的购买次数占总商品购买次数的比例。支持度说明了这条规则在所有事务中有多大的代表性,显然支持度越大,关联规则越重要。物品集 A 对物品集 B 的支持度 P(A ∩ B)今天共有 10 笔订单,其中同时购买牛奶和面包的次数是 6 次,那么 牛奶+面包 组合的支持度就是 6/10=60%
置信度置信度是指购买 A 之后又购买 B 的条件概率,简单说就是因为购买了 A 所以购买了 B 的概率物品集 B 对物品集 A 的置信度 confidence P(B|A) = P(A ∩ B) / P(A)今天共有 10 笔订单,其中购买 A 的次数是 8,同时购买 A 和 B 的次数是 6,则其置信度是 6/8=75%
提升度先购买 A 对购买 B 的提升作用,用来判断商品组合方式是否具有实际价值,是看组合商品被购买的次数是否高于单独商品的购买次数,大于 1 说明该组合方式有效,小于 1 则说明无效。L = P(A ∩ B) / [ P(A)*P(B)]今天共有 10 笔订单,购买 A 的次数是 8,购买 B 的次数是 6,购买 A+B 的次数是 6,那么提升度是 0.6/(0.8*0.6)>1,因此 A+B 的组合方式是有效的。

应用意义

购物篮分析目的是从大规模订单数据集中寻找商品之间的关联,线下零售商可藉由此分析改变货架上的商品排列或是设计吸引客户的组合促销套餐等,线上电商则可以做商品推荐,最著名的案例是啤酒和尿片的故事。

实战

基础数据

这里选择面包店销售数据集进行分析,数据字段为:

  • TransactionNo:每笔交易的唯一标识符
  • Items:购买的物品
  • DateTime:交易的日期和时间戳
  • Daypart:进行交易的一天中的一部分(早上、下午、傍晚、晚上)
  • DayType:分类交易是在周末还是工作日进行

部分数据如下:

TransactionNoItemsDateTimeDaypartDayType
1Bread2016-10-30 09:58:11MorningWeekend
2Scandinavian2016-10-30 10:05:34MorningWeekend
2Scandinavian2016-10-30 10:05:34MorningWeekend
3Hot chocolate2016-10-30 10:07:57MorningWeekend
3Jam2016-10-30 10:07:57MorningWeekend
3Cookies2016-10-30 10:07:57MorningWeekend

数据下载地址:Bakery Sales Dataset

数据处理

  1. 因为物品种类较多,这里只筛选 day_part = 'Evening'day_type = 'Weekend',过滤只购买单一物品的销售数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    WITH t1 AS (
    SELECT DISTINCT
    "transaction_no",
    "items"
    FROM
    "bakery" tx
    WHERE
    "day_part" = 'Evening'
    AND "day_type" = 'Weekend'
    AND EXISTS (
    SELECT
    "transaction_no"
    FROM
    "bakery" ty
    WHERE
    "day_part" = 'Evening'
    AND "day_type" = 'Weekend'
    AND tx."transaction_no" = ty."transaction_no"
    GROUP BY
    "transaction_no"
    HAVING
    COUNT ( DISTINCT "items" ) > 1 -- 购买两个以上不同物品
    )
    )

  2. 关联同一订单的其他商品,关联购买物品 A 的订单数量,关联购买物品 B 的订单数量,关联订单数量;过滤物品 A 和 物品 B 是同一物品,否则分析没有意义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    SELECT DISTINCT
    t1."items" AS "items_a",
    t2."items" AS "items_b",
    t3."a_num",
    t4."b_num",
    SUM ( 1 ) OVER ( PARTITION BY t1."items", t2."items" ) AS "a_b_num", -- 同时购买物品 A 和物品 B 的订单数
    t5."total_num"
    FROM
    t1
    LEFT JOIN t1 AS t2 ON t1."transaction_no" = t2."transaction_no" -- 关联同一订单的其他商品
    LEFT JOIN ( SELECT "items", COUNT ( DISTINCT "transaction_no" ) AS "a_num" FROM t1 GROUP BY "items" ) t3 ON t1."items" = t3."items" -- 关联购买物品 A 的订单数量
    LEFT JOIN ( SELECT "items", COUNT ( DISTINCT "transaction_no" ) AS "b_num" FROM t1 GROUP BY "items" ) t4 ON t2."items" = t4."items" -- 关联购买物品 B 的订单数量
    LEFT JOIN ( SELECT COUNT ( DISTINCT "transaction_no" ) AS "total_num" FROM t1 ) t5 ON 1 = 1 -- 关联订单数量
    WHERE
    t1."items" != t2."items" -- 过滤物品 A 和 物品 B 是同一物品

  3. 计算支持度、置信度和提升度。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SELECT
    *,
    ROUND( "a_b_num" :: NUMERIC / "total_num" * 100, 4 ) AS "support", -- 支持度
    ROUND( "a_b_num" :: NUMERIC / "a_num" * 100, 4 ) AS "confidence", -- 置信度
    ROUND( ( "a_b_num" :: NUMERIC / "total_num" ) / ( ( "a_num" :: NUMERIC / "total_num" ) * ( "b_num" :: NUMERIC / "total_num" ) ) * 100, 4 ) "promotion" -- 提升度
    FROM
    (...) t6
    ORDER BY
    "items_a",
    "items_b"

完整 SQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
WITH t1 AS (
SELECT DISTINCT
"transaction_no",
"items"
FROM
"bakery" tx
WHERE
"day_part" = 'Evening'
AND "day_type" = 'Weekend'
AND EXISTS (
SELECT
"transaction_no"
FROM
"bakery" ty
WHERE
"day_part" = 'Evening'
AND "day_type" = 'Weekend'
AND tx."transaction_no" = ty."transaction_no"
GROUP BY
"transaction_no"
HAVING
COUNT ( DISTINCT "items" ) > 1 -- 购买两个以上不同物品
)
)

SELECT
*,
ROUND( "a_b_num" :: NUMERIC / "total_num" * 100, 4 ) AS "support", -- 支持度
ROUND( "a_b_num" :: NUMERIC / "a_num" * 100, 4 ) AS "confidence", -- 置信度
ROUND( ( "a_b_num" :: NUMERIC / "total_num" ) / ( ( "a_num" :: NUMERIC / "total_num" ) * ( "b_num" :: NUMERIC / "total_num" ) ) * 100, 4 ) "promotion" -- 提升度
FROM
(SELECT DISTINCT
t1."items" AS "items_a",
t2."items" AS "items_b",
t3."a_num",
t4."b_num",
SUM ( 1 ) OVER ( PARTITION BY t1."items", t2."items" ) AS "a_b_num", -- 同时购买物品 A 和物品 B 的订单数
t5."total_num"
FROM
t1
LEFT JOIN t1 AS t2 ON t1."transaction_no" = t2."transaction_no" -- 关联同一订单的其他商品
LEFT JOIN ( SELECT "items", COUNT ( DISTINCT "transaction_no" ) AS "a_num" FROM t1 GROUP BY "items" ) t3 ON t1."items" = t3."items" -- 关联购买物品 A 的订单数量
LEFT JOIN ( SELECT "items", COUNT ( DISTINCT "transaction_no" ) AS "b_num" FROM t1 GROUP BY "items" ) t4 ON t2."items" = t4."items" -- 关联购买物品 B 的订单数量
LEFT JOIN ( SELECT COUNT ( DISTINCT "transaction_no" ) AS "total_num" FROM t1 ) t5 ON 1 = 1 -- 关联订单数量
WHERE
t1."items" != t2."items" -- 过滤物品 A 和 物品 B 是同一物品
) t6
ORDER BY
"items_a",
"items_b"

输出数据

items_aitems_ba_numb_numa_b_numtotal_numsupportconfidencepromotion
AlfajoresBread171372.7027100.0000528.5714
AlfajoresCake171372.7027100.0000528.5714
AlfajoresCoffee1121372.7027100.0000308.3333
Art TrayTartine111372.7027100.00003700.0000
BreadAlfajores711372.702714.2857528.5714
BreadCake772375.405428.5714151.0204

数据可视化

  1. 绘制气泡图,其中 X 轴为支持度,Y 轴为置信度,气泡大小表示提升度。 购物篮分析 - 气泡图

  2. 绘制物品 A 对物品 B 支持度的矩阵图,表示同时购买 A 和 B 的概率。 购物篮分析 - 支持度矩阵图

  3. 绘制物品 A 对物品 B 置信度的矩阵图,表示因为购买了 A 所以购买了 B 的概率。 购物篮分析 - 置信度矩阵图

  4. 绘制物品 A 对物品 B 提升度的矩阵图,表示组合商品被购买的次数是否高于单独商品的购买次数,大于 100% 表示有效。 购物篮分析 - 提升度矩阵图