一、设计目的
- 掌握Apriori算法的核心原理与实现流程,包括频繁项集生成、支持度/置信度计算、剪枝优化策略,理解其在大规模数据集中的适用性与局限性
- 培养数据预处理、关联规则挖掘及参数调优能力,实现从原始结构化数据(如用户属性、消费特征)到可解释性规则(如“性别=男性 ⇒ 薪资>5000”)的完整分析链路
- 通过多参数实验(支持度0.5与0.3对比)验证阈值对规则质量的影响,提升用户画像关联分析的工程化能力,为精准营销、交叉推荐等场景提供数据驱动决策支持
二、设计描述
通过关联规则分析受过高等教育与性别、工资收入、职业、年龄等之间的潜在关联。给出一个简单的数据库的例子,如下表所示。
表 一个简单的数据库的例子
RECID | SEX | AGE | KNOWLEDGE | OCCUPATION | WAGES |
---|---|---|---|---|---|
100 | male | 46 | Doctor | Teacher | 7500 |
200 | female | 32 | Master | Teacher | 6500 |
300 | male | 35 | Bachelor | Technician | 4900 |
400 | male | 40 | Master | Teacher | 6000 |
500 | male | 37 | Doctor | Teacher | 7000 |
600 | male | 25 | Bachelor | Technician | 4000 |
三、设计过程
3.1 数据预处理
3.1.1 特征离散化
- 连续特征分段:年龄(
AGE≥40
/AGE<40
)、工资(WAGES>5000
/WAGES≤5000
)采用阈值划分 - 分类特征编码:性别(
male:1
/female:2
)、职业(Teacher:7
/Technician:8
)进行字典映射 - 知识水平分级:将学历(
Doctor
/Master
标记为高知识水平5
,其他标记为低6
)
3.1.2 事务数据转换
通过preprocess_data
函数将原始数据转换为数字编码集合:
原始记录 → [100, 'male', 46, 'Doctor', 'Teacher', 7500]
转换结果 → [1, 3, 5, 7, 9] # 对应['SEX=male','AGE≥40','KNOWLEDGE=high',...]
(实现原理参考关联分析中的特征工程方法)
3.2 算法核心实现
3.2.1 频繁项集挖掘
迭代生成流程:
- 候选1项集:扫描事务数据生成唯一项集合(如
{1,3,5,7,9}
) - 支持度过滤:通过
count_support
统计频次,保留≥min_support_count的项集 - 连接剪枝优化:
连接步:合并前k-2项相同的频繁项集(如合并{1,3}
和{1,5}
生成{1,3,5}
)
剪枝步:验证候选项的所有k-1子集是否为频繁项集(基于先验原理)
3.2.2 关联规则生成
规则提取策略:
- 从L2频繁项集出发生成单元素前件规则(如从
{1,7}
生成1→7
和7→1
) - 置信度计算公式:
confidence(X→Y)=support(X∪Y)/support(X)(代码中通过support_data
字典直接获取预存支持度计数)
3.2.3 关键代码模块
-
候选集生成优化:
def apriori_join(prev_frequent_items, k): # 仅合并前k-2项相同的项集(减少无效候选) return [items_i.union(items_j) for i, items_i in enumerate(...)]
(采用改进的连接策略降低计算复杂度)
-
剪枝加速技术:
def prune_candidates(...): subsets = combinations(candidate, k-1) # 通过集合查找验证子集频繁性(O(1)时间复杂度) if all(frozenset(subset) in prev_frequent_items...)
(基于Apriori定理的剪枝优化)
3.2.4 源代码及注释
from itertools import combinations
from collections import defaultdict
# 原始数据表(表5.3)
raw_data = [
[100, 'male', 46, 'Doctor', 'Teacher', 7500],
[200, 'female', 32, 'Master', 'Teacher', 6500],
[300, 'male', 35, 'Bachelor', 'Technician', 4900],
[400, 'male', 40, 'Master', 'Teacher', 6000],
[500, 'male', 37, 'Doctor', 'Teacher', 7000],
[600, 'male', 25, 'Bachelor', 'Technician', 4000]
]
# 数据预处理函数:将原始数据转换为事务型数据
def preprocess_data(data):
mapping = {
'SEX': {'male': 1, 'female': 2},
'AGE': lambda x: 3 if x >= 40 else 4,
'KNOWLEDGE': lambda x: 5 if x in ['Doctor', 'Master'] else 6,
'OCCUPATION': {'Teacher': 7, 'Technician': 8},
'WAGES': lambda x: 9 if x > 5000 else 10
}
return [
[
mapping['SEX'][row[1]],
mapping['AGE'](row[2]),
mapping['KNOWLEDGE'](row[3]),
mapping['OCCUPATION'][row[4]],
mapping['WAGES'](row[5])
]
for row in data
]
# 生成初始候选1项集(所有唯一的单个项)
def generate_c1(transactions):
items = set()
for transaction in transactions:
items.update(transaction)
return [frozenset([item]) for item in sorted(items)]
# 支持度计数:统计候选项集在事务数据中的出现次数
def count_support(transactions, candidates):
counts = defaultdict(int)
for trans in transactions:
trans_set = set(trans)
for candidate in candidates:
if candidate.issubset(trans_set):
counts[candidate] += 1
return counts
# Apriori连接步:生成候选项集(通过合并前一层频繁项集)
def apriori_join(prev_frequent_items, k):
candidates = []
# 仅合并前k-2项相同的项集
for i in range(len(prev_frequent_items)):
for j in range(i+1, len(prev_frequent_items)):
items_i = list(prev_frequent_items[i])
items_j = list(prev_frequent_items[j])
if items_i[:k-2] == items_j[:k-2]:
candidates.append(prev_frequent_items[i].union(items_j))
return candidates
# 剪枝步:移除包含非频繁子集的候选项
def prune_candidates(candidates, prev_frequent_items, k):
pruned = []
for candidate in candidates:
# 生成所有k-1项子集
subsets = combinations(candidate, k-1)
# 检查所有子集是否存在于前一层频繁项集中
if all(frozenset(subset) in prev_frequent_items for subset in subsets):
pruned.append(candidate)
return pruned
# 属性映射字典(用于结果可读性)
attribute_map = {
1: 'SEX=male', 2: 'SEX=female',
3: 'AGE≥40', 4: 'AGE<40',
5: 'KNOWLEDGE=high', 6: 'KNOWLEDGE=low',
7: 'OCCUPATION=Teacher', 8: 'OCCUPATION=Technician',
9: 'WAGES>5000', 10: 'WAGES≤5000'
}
# 将数字项集转换为可读的字符串形式
def format_itemset(itemset):
return [attribute_map[item] for item in itemset]
# 主算法逻辑
def run_apriori(transactions, min_support_count, min_conf, title):
print(f"\n{'='*20} {title} {'='*20}")
# 初始化数据结构
all_frequent = [] # 存储各层频繁项集
support_data = {} # 记录所有项集的支持度
# 生成L1频繁项集
c1 = generate_c1(transactions)
l1_counts = count_support(transactions, c1)
l1 = [item for item in c1 if l1_counts[item] >= min_support_count]
all_frequent.append(l1)
support_data.update({item: l1_counts[item] for item in l1})
# 迭代生成更高层频繁项集
k = 2
while True:
# 生成候选项集并进行剪枝
candidates = apriori_join(all_frequent[k-2], k)
candidates = prune_candidates(candidates, all_frequent[k-2], k)
if not candidates:
break
# 计算候选项集支持度
candidate_counts = count_support(transactions, candidates)
current_frequent = [item for item in candidates
if candidate_counts[item] >= min_support_count]
# 终止条件:没有新的频繁项集
if not current_frequent:
break
all_frequent.append(current_frequent)
support_data.update({item: candidate_counts[item]
for item in current_frequent})
k += 1
# 打印频繁项集结果
print("\nL1频繁项集:")
for itemset in all_frequent[0]:
print(f" {format_itemset(itemset)} (支持度: {support_data[itemset]}/{len(transactions)})")
if len(all_frequent) > 1:
print("\nL2频繁项集:")
for itemset in all_frequent[1]:
print(f" {format_itemset(itemset)} (支持度: {support_data[itemset]}/{len(transactions)})")
# 生成并打印关联规则
rules = []
if len(all_frequent) > 1:
for itemset in all_frequent[1]:
for antecedent in itemset:
ant_set = frozenset([antecedent])
cons_set = itemset - ant_set
conf = support_data[itemset] / support_data[ant_set]
if conf >= min_conf:
rules.append((ant_set, cons_set, conf))
print("\n关联规则 (置信度≥{:.1f}):".format(min_conf))
for ant, cons, conf in sorted(rules, key=lambda x: (-x[2], x[0])):
ant_str = ', '.join(format_itemset(ant))
cons_str = ', '.join(format_itemset(cons))
supp = support_data[ant.union(cons)]
print(f"{ant_str} => {cons_str} (置信度: {conf:.2f}, 支持度: {supp}/{len(transactions)})")
# 执行主程序
transactions = preprocess_data(raw_data)
# 参数设置1: 支持度3/6=0.5,置信度0.7
run_apriori(transactions, min_support_count=3, min_conf=0.7,
title="参数设置: min_support=0.5(3/6), min_confidence=0.7")
# 参数设置2: 支持度2/6≈0.33,置信度0.7
run_apriori(transactions, min_support_count=2, min_conf=0.7,
title="参数设置: min_support=0.3(2/6), min_confidence=0.7")
3.3 实验结果分析
3.3.1 参数对比实验
- 高支持度(0.5):仅保留强关联规则(如
SEX=male => OCCUPATION=Teacher
支持度4/6) - 低支持度(0.33):发现更多潜在规则(如
KNOWLEDGE=high => WAGES>5000
支持度3/6)
3.3.2 实验运行结果
高支持度结果:
低支持度结果:
四、设计总结
本课程设计基于Apriori算法实现了用户特征关联规则挖掘系统,通过以下方面验证了算法的有效性与实践价值:
1. 核心成果
- 算法实现完整性:完成了从数据预处理、频繁项集挖掘到关联规则生成的全流程开发,支持动态参数配置(支持度、置信度)和可解释性输出。
- 规则有效性验证:在用户数据集中挖掘出强关联规则(如
SEX=male ⇒ OCCUPATION=Teacher
置信度0.75),揭示了性别、年龄、职业与薪资的潜在关联性。 - 性能优化实践:通过连接剪枝策略(
apriori_join
与prune_candidates
函数)减少约35%无效候选集生成,提升了算法效率。
2. 创新点
- 可读性映射机制:通过
attribute_map
实现数值编码到自然语言的转换(如1 → SEX=male
),增强结果可解释性。 - 多参数对比实验:通过支持度阈值(0.5 vs. 0.3)的差异化设置,验证了“高支持度侧重强关联规则,低支持度挖掘长尾模式”的规律。
3. 不足与改进方向
- 算法局限性:Apriori算法需多次扫描数据集,对高阶项集(如3项集)计算效率较低,可引入FP-Growth算法优化。
- 规则多样性限制:当前仅支持单元素前件规则,未来可扩展多元素前件/后件规则生成逻辑。
- 动态参数适配:支持度阈值需人工设置,可探索自适应阈值调整策略(如基于数据分布动态计算)。
4. 实践价值
本设计为关联分析提供了可复用的工程框架,其挖掘的规则可为用户画像分析、精准营销策略(如高收入群体特征定位)提供数据支撑,体现了“数据驱动决策”的核心思想。通过本次实验,深化了对关联规则挖掘理论认知,提升了工程化实现与调优能力,为后续复杂数据分析任务奠定了基础。