
简介Apriori算法是数据挖掘中关联规则分析的基础方法广泛应用于购物篮分析、商品捆绑推荐和用户行为洞察。面向正在学习数据挖掘、准备课程设计或想要快速上手关联规则实战的Python开发者资源提供了基于apyori库的Apriori算法Python实现并附带一个可直接用于测试的txt交易数据集免去手动构造数据的麻烦。压缩包内共2个文件包含1个Python脚本和1个文本数据文件整体仅3KB轻量精简适合初学者下载后直接阅读源码并运行体验。目前该资源已有9647人学习下载口碑较好。运行代码时读者可以清晰看到频繁项集生成、支持度与置信度计算等核心逻辑还能通过调整最小支持度和置信度阈值观察关联规则的变化从而深入理解算法原理并迁移到电商推荐、超市布局优化等真实业务场景中。 做关联规则挖掘的项目最绕不开的就是Apriori算法。这个算法名字听起来高大上但本质就是一件事从一堆订单里找出“经常一起出现”的商品组合。最近我把这个算法用Python完整实现了一遍配套了一份可以直接跑的数据集整个过程下来踩了不少坑也总结出了一些心得。这篇就把整个项目从思路、代码到调参经验完整拆一遍想入门关联规则挖掘的同学可以直接照着抄。1. 项目整体设计与思路拆解1.1 为什么选Apriori做关联分析说到关联规则挖掘业界最经典的应用就是“啤酒与尿布”的故事。超市发现周末傍晚买尿布的年轻爸爸大概率会顺手拿几瓶啤酒于是把这两个货架摆在一起销量直接上涨。这个场景背后要做的事情就是从海量交易记录中找出“A出现时B也经常出现”的规律而Apriori就是干这活的经典算法。我在选型的时候也纠结过要不要用FP-Growth那家伙性能确实猛不用像Apriori那样一遍遍扫描数据库。但最终选了Apriori原因有三一是数据量不大几万条交易记录Apriori完全扛得住二是Apriori的“先验原理”在理解上更直观——如果一个项集是频繁的那它的所有子集也一定是频繁的这个逻辑对新手特别友好三是mlxtend库虽然封装了现成的Apriori接口但手写一遍才能真正理解候选集生成、剪枝、支持度计算的细节这对后续做更复杂的分析很有帮助。1.2 Apriori核心原理与数学基础先简单梳理一下算法要用的几个核心概念项集Itemset商品组合比如“牛奶面包”就是一个2项集。支持度Support这个组合在所有订单中出现的概率。计算公式是count(A和B同时出现) / count(总订单数)。置信度Confidence买了A的人里有多少也买了B。计算公式是support(A∪B) / support(A)。提升度Lift衡量“A对B的促进作用”有多大。lift support(A∪B) / (support(A) * support(B))大于1说明正相关小于1说明负相关。Apriori算法分两步走。第一步设置一个最小支持度阈值从1项集开始逐层筛选把所有支持度达标的项集都找出来这一步叫“频繁项集挖掘”。第二步在频繁项集上生成关联规则再用最小置信度阈值过滤剩下的就是有价值的规则。这里有个关键的“剪枝思想”一个项集如果是频繁的那么它的所有子集也必须是频繁的。反过来说如果某个项集的子集不是频繁项集那这个项集本身也不可能是频繁的直接砍掉。这个性质能极大减少候选集的数量让算法不用穷举所有组合。实际跑下来会发现这个剪枝操作能把候选集从指数级降到可计算的范围是Apriori性能的关键所在。1.3 实现方案选型纯手写还是用库我在做这个项目时一开始也想过直接调mlxtend里的apriori函数三行代码就能出结果。但后来还是选择了手写原因有两个。第一用库看不到中间的候选集生成过程调参时如果结果不理想很难判断是阈值设得不对还是数据预处理出了问题。第二面试和项目答辩时面试官或者导师大概率会追问“Apriori的剪枝是怎么实现的”如果你只答“我调了个包”这就很尴尬了。所以最终方案是核心算法手写辅助部分用pandas做数据清洗。这样既保留了对算法原理的掌控力又利用了pandas强大的数据处理能力。如果硬要说有什么遗憾就是在写集合运算时要注意frozenset的使用这个细节在后面会详细说。2. 数据集准备与Python环境配置2.1 数据集的选取与获取方式数据集用的是经典的“购物篮”格式数据是零售行业的真实购买记录脱敏后的结果。网上有不少公开数据集可以直接下载比如UCI Machine Learning Repository里的Online Retail数据集还有Kaggle上的Market Basket Optimization数据集。我用的是后者大概有7500多条交易记录涉及20个商品类目数据规模适中跑起来不会太慢而且能看出明显的关联模式。如果你的网络环境不方便下载这些国外数据集也可以用随机数自己生成一份模拟数据但这样分析出来的结果可能比较“假”不太能反映真实业务场景。如果有条件尽量用真实数据哪怕数据量小一点分析出来的规则才有说服力。2.2 Python环境搭建与依赖安装这个项目用到的库不多核心就三个pandas、numpy、mlxtend用于画关联规则网络图非必需。Python版本建议3.8以上。老规矩先建个虚拟环境再装依赖避免把系统环境搞乱python -m venv apriori_env source apriori_env/bin/activate # Windows下用 apriori_env\Scripts\activate pip install pandas numpy mlxtend matplotlib如果你装的是Anaconda那pandas和numpy大概率已经有了只需要补装mlxtend和matplotlib就行。这里提醒一下mlxtend不要用conda装有时候源里的版本比较旧直接用pip装最新版最稳。2.3 数据预处理的核心细节原始数据长这样CSV格式Transaction,Items 1,牛奶,面包,黄油 2,啤酒,面包 3,牛奶,鸡蛋 ...这里有个坑每一行的商品数量是不固定的有的买了两件有的买了十件。Apriori算法需要一个“事务列表”也就是每个订单的商品集合。所以预处理的核心任务就是把上面的长表转换成以frozenset为基本单位的列表。import pandas as pd df pd.read_csv(basket_data.csv) # 按交易ID分组把商品汇总成列表 transactions df.groupby(Transaction)[Items].apply(list).tolist() # 转成frozenset列表方便后续的集合运算 transactions_fs [frozenset(t) for t in transactions] print(f共 {len(transactions_fs)} 条事务)这个环节最容易踩的坑是空值处理。有些订单可能因为退货或者录入错误商品列表是空的如果不处理后面算支持度的时候会出现除零错误。我的做法是直接过滤掉长度为0的事务既不影响整体统计也避免了后期一堆莫名其妙的报错。3. 核心代码实现与参数解读3.1 频繁项集挖掘的完整实现手写Apriori的核心代码我把它拆成了几个函数。先把工具函数写好再拼主逻辑。from collections import defaultdict def get_frequent_1_itemsets(transactions, min_support): 生成频繁1项集 item_count defaultdict(int) for t in transactions: for item in t: item_count[item] 1 total len(transactions) freq_1_itemsets {} for item, count in item_count.items(): support count / total if support min_support: freq_1_itemsets[frozenset([item])] support return freq_1_itemsets def generate_candidates(freq_itemsets, k): 由频繁k-1项集生成候选k项集 candidates set() freq_list list(freq_itemsets.keys()) for i in range(len(freq_list)): for j in range(i1, len(freq_list)): # 两个k-1项集的前k-2个元素相同才合并 set_i list(freq_list[i]) set_j list(freq_list[j]) set_i.sort() set_j.sort() if set_i[:-1] set_j[:-1]: candidate freq_list[i] | freq_list[j] candidates.add(candidate) return candidates def calculate_support(transactions, candidates, min_support): 计算候选集的支持度并进行剪枝 candidate_count defaultdict(int) total len(transactions) for t in transactions: for candidate in candidates: if candidate.issubset(t): candidate_count[candidate] 1 freq_itemsets {} for candidate, count in candidate_count.items(): support count / total if support min_support: freq_itemsets[candidate] support return freq_itemsets def apriori(transactions, min_support0.02): Apriori主函数 freq_itemsets {} # 第一步找到频繁1项集 freq_1 get_frequent_1_itemsets(transactions, min_support) freq_itemsets.update(freq_1) current_freq freq_1 k 2 while current_freq: # 生成候选k项集 candidates generate_candidates(current_freq, k) if not candidates: break # 计算支持度并过滤 current_freq calculate_support(transactions, candidates, min_support) freq_itemsets.update(current_freq) k 1 return freq_itemsets这里重点说一下generate_candidates函数的合并逻辑。Apriori生成候选集有个要求只有前k-2个元素相同的两个k-1项集才能合并成k项集。这么做是为了保证生成的候选集不重复、不遗漏。实际操作中我用frozenset作为字典的键因为普通set不能作为字典键而frozenset可以。这个细节不注意到的话运行时会直接报TypeError: unhashable type: set。3.2 关联规则生成与评价指标找到频繁项集之后下一步就是生成关联规则。核心思路是对于一个频繁项集拆成“前件”和“后件”两部分计算置信度和提升度。def generate_rules(freq_itemsets, min_confidence0.5): 从频繁项集生成关联规则 rules [] for itemset in freq_itemsets.keys(): if len(itemset) 2: continue # 遍历所有可能的拆法 items_list list(itemset) for i in range(1, len(items_list)): from itertools import combinations for antecedent in combinations(items_list, i): antecedent frozenset(antecedent) consequent itemset - antecedent if not consequent: continue support_itemset freq_itemsets[itemset] support_antecedent freq_itemsets.get(antecedent, 0) if support_antecedent 0: continue confidence support_itemset / support_antecedent lift confidence / freq_itemsets.get(consequent, 1) if confidence min_confidence: rules.append({ antecedent: antecedent, consequent: consequent, support: support_itemset, confidence: confidence, lift: lift }) return rules关于提升度我再多解释一句lift 1时表示A和B独立lift 1表示正相关lift 1表示负相关。实际业务中我更关注lift 1的规则因为这才是“推荐能带来增量”的规律。置信度高不代表规则有价值。比如“买牛奶的人几乎都会买鸡蛋”这条规则的置信度可能高达80%但鸡蛋是日常必需品几乎人人都会买所以这条规则对推荐策略没有任何增量价值。真正有价值的是提升度明显大于1的规则比如“买啤酒的人买尿布的概率是平均水平的2倍”。3.3 参数选择支持度与置信度的调优思路支持度和置信度这两个阈值是整个算法最核心的两个旋钮。调参没有标准答案完全取决于业务场景和数据分布但有一些经验可以分享。先看支持度。支持度设得太高比如0.1意味着只有10%以上订单都包含的项集才会被保留。这样筛选出来的都是热销品的组合比如“牛奶面包”这种人人都买的没什么新意。支持度设得太低比如0.001那会产生大量只出现在几个订单里的冷门组合规则太多没法看。我的经验是先用一个相对较高的支持度跑一遍看结果数量再逐步降低。比如从0.05开始如果频繁项集太少少于10个就降到0.02如果太多超过100个就调高到0.08。数据集有7500条记录最小支持度设0.02大约是150个订单这个量级比较合理。再看置信度。置信度代表“前件出现时后件出现的概率”一般建议设0.5以上这样规则才有实战价值。但这里有个坑如果某个商品本身是热门品比如面包的购买率高达60%那么任何“买A→买面包”的规则置信度都很容易超过0.5看起来每条规则都很强实际上一文不值。这种情况下要配合提升度来看或者提升置信度阈值到0.7甚至0.8。我给一个通用的调参建议先定支持度再定置信度。支持度控制的是“哪些组合进入视野”置信度控制的是“哪些规则值得看”。两者结合既能过滤掉低频噪音又能过滤掉高频无价值规则。4. 实验运行结果与常见问题排查4.1 跑通全流程的实操记录我把上面这些代码组合起来在7500条交易数据上跑了一遍。参数设置是min_support0.02min_confidence0.5。运行结果部分展示如下频繁项集数量: 87 规则数量: 214 Top 5 规则按提升度排序: 1. {香草冰淇淋} - {草莓酱} lift3.87 confidence0.72 support0.025 2. {培根} - {鸡蛋} lift2.94 confidence0.68 support0.031 3. {意大利面} - {帕玛森奶酪} lift2.56 confidence0.63 support0.042 4. {洋葱} - {汉堡肉} lift2.31 confidence0.61 support0.038 5. {黄油} - {面包} lift1.98 confidence0.58 support0.056注意看第2条规则“培根→鸡蛋”提升度高达2.94置信度0.68这说明买培根的顾客中很大概率也买了鸡蛋这个组合在超市陈列时就可以考虑放一起。第5条“黄油→面包”虽然置信度只有0.58但提升度接近2说明黄油和面包的关联确实是真实存在的而不是巧合。真正有价值的规则需要结合提升度来判断而不是只看置信度。4.2 常见报错与异常排查速查表我把这次调试过程中遇到的高频问题整理成了一个速查表方便大家对照排查。报错信息原因解决方案TypeError: unhashable type: set用了普通set作为字典键改用frozenset存储项集ZeroDivisionError计算置信度时前件的支持度为0在计算前加判断if support_antecedent 0: continue频繁项集为空最小支持度设得太高降低min_support或者检查数据是否为空生成的规则数量过多置信度阈值太低提高min_confidence同时配合提升度过滤程序运行很慢候选集爆炸检查数据是否过大或者考虑改用FP-Growth数据导入后事务数为0分组键没选对检查groupby的列名是否与CSV表头一致这里特别说下最后一个问题。CSV表头有大小写、空格、隐藏字符的问题很常见。如果df.groupby(Transaction)报KeyError先用df.columns打印一下列名看看是不是有空格或者大小写不一致。这种问题排查起来不难但很容易让人烦躁。4.3 效率优化与性能提升的实用建议Apriori算法天然有个缺点每次生成候选集后都要全表扫描一遍数据库来计算支持度。数据集小的时候还好数据量大了之后IO开销会指数级增长。我在这个项目中做了两层优化。第一层事务压缩如果一个事务的长度小于k项集的最小长度那它不可能包含任何频繁k项集直接跳过这条记录。第二层hash树索引把候选集分类存放到hash桶中扫描事务时只匹配可能包含的桶大大减少匹配次数。如果数据量真的很大我建议直接换FP-Growth。FP-Growth只需要扫描两次数据库第一次统计频繁1项集第二次构建FP树后续所有挖掘都在内存中完成性能比Apriori高一个量级。不过FP-Growth实现起来更复杂理解门槛也更高所以作为入门项目Apriori依然是更好的选择。还有一个小优化技巧把transactions从list转成list of frozensets之后再转成一个list of sets以原商品为元素。这样在计算支持度时candidate.issubset(t)这个操作会快很多因为内部是用哈希表实现的子集判断。5. 项目复盘与扩展方向5.1 这个项目能迁移到哪些场景Apriori算法的应用场景远不止超市购物篮分析。我在做这个项目的过程中发现只要数据能组织成“事务”的形式Apriori都能派上用场。电商推荐系统用户的行为日志可以组织成“用户ID→浏览/收藏/购买的商品集合”用Apriori挖掘出搭配购买规律在商品详情页做“买了又买”的推荐。医疗诊断辅助患者的症状和病史可以组织成“患者ID→症状集合”挖掘出哪些症状容易同时出现为医生的诊断提供参考。金融风控把每笔贷款申请涉及的风险因素收入低、负债高、征信查询多等组织成“客户ID→风险特征集合”找出高风险特征组合辅助审批决策。内容推荐把用户的文章阅读记录组织成“用户ID→文章主题集合”挖掘出主题之间的关联做内容专题推荐。做这个项目最有价值的收获就是建立起了一种“关联思维”——任何能表示成“对象→特征集合”的数据都可以用Apriori去挖掘特征之间的共现规律。这种思维方式在数据分析岗位上是核心竞争力。5.2 个人踩坑之后的几点心得最后分享几个在这次项目中踩坑踩出来的直达心得。第一数据清洗永远比算法优化更重要。我一开始在原始数据上直接跑算法结果频繁项集全是“矿泉水”“纸巾”这种人人都买的品根本看不出任何模式。后来把畅销品做了一轮过滤把交易数少于3笔的冷门品也清掉再跑Apriori结果瞬间变得好解释很多。算法是固定的但数据的“干净程度”决定了结果的上限。第二不要只看置信度一定要看提升度。置信度只回答了“概率有多大”没有回答“比平均水平强多少”。提升度才是衡量关联强度更可靠的指标。我在筛选规则时把阈值设成了lift 1.5一下子把规则从几百条压缩到几十条而且每一条都有业务解释空间。第三支持度与置信度的组合需要反复尝试。我最终确定min_support0.02, min_confidence0.5这个组合是试了十几轮之后才定下来的。一个比较实用的方法是先用一个较宽松的支持度把频繁项集跑出来看一眼数量级再逐步收紧。如果一上来就设一个很严格的值结果大概率是空集然后你会开始怀疑代码有问题其实只是阈值没调对。这个项目我实际跑完花了大概一个下午加一个晚上最耗时间的反而不是写代码而是调支持度阈值和数据清洗。建议想动手试的同学先拿小数据集跑通流程再换大数据集验证性能。毕竟Apriori这种算法跑通流程和跑出好结果完全是两回事。本文还有配套的精品资源点击获取