实验内容与完成情况:
- 相关知识点
- 频繁项集挖掘:从事务数据中发现经常一起出现的项集,常用于市场篮分析和推荐系统。
- Apriori算法:通过生成候选项集并计算支持度来找频繁项集,效率较低,适用于小规模数据集。
- FP-Growth算法:通过构建FP树,避免生成候选项集,能高效挖掘频繁项集,适用于大数据集。
- FP树:一种压缩的树结构,用于存储事务数据,通过头指针表优化频繁项集挖掘。
- 实验分析
- 数据预处理:实验首先从CSV文件加载商品购买事务数据,并将其转换为适合FP-Growth算法的数据格式。每一行数据表示一个事务,包含多个商品项,采用0和1表示是否购买该商品。
- FP树构建:通过统计每个项的出现频率,移除低频项,构建头指针表,并根据频率对项进行排序。随后,遍历事务数据,按照排序后的频率依次将每个事务插入到FP树中,树的每个节点表示一个商品项,并记录该项出现的次数。
- 频繁项集挖掘:在FP树构建完成后,利用递归的方式从FP树中挖掘频繁项集。通过前缀路径的遍历,合并路径并递归生成新的候选频繁项集。每次递归时,根据当前前缀的支持度筛选出符合最小支持度条件的频繁项集。
- 输出结果:最终,输出频繁项集及其支持度,展示哪些商品项组合经常一起购买。支持度反映了某个商品组合在整个事务数据中出现的频率。
- 算法性能:与Apriori算法相比,FP-Growth避免了频繁生成候选项集,减少了计算量。在实验中,当数据集较大时,FP-Growth表现出更好的性能,尤其在内存和计算时间的消耗上较为高效。
- 实验代码
import pandas as pd
from collections import defaultdict
# 定义FP树节点类
class TreeNode:
def __init__(self, name, count, parent):
self.name = name
self.count = count
self.parent = parent
self.children = {}
self.nodeLink = None
# 创建FP树
def createTree(dataSet, minSupport):
# 统计每个项的出现次数
headerTable = defaultdict(int)
for trans in dataSet:
for item in trans:
headerTable[item] += 1
# 移除低于最小支持度的项
headerTable = {item: count for item, count in headerTable.items() if count >= minSupport}
# 如果没有频繁项,返回None
if not headerTable:
return None, None
# 初始化头指针表
headerTable = {item: [count, None] for item, count in headerTable.items()}
# 创建根节点
retTree = TreeNode('Null Set', 1, None)
# 构建FP树
for tranSet in dataSet:
localD = {item: headerTable[item][0] for item in tranSet if item in headerTable}
if localD:
orderedItems = [item for item, _ in sorted(localD.items(), key=lambda p: p[1], reverse=True)]
updateTree(orderedItems, retTree, headerTable, 1)
return retTree, headerTable
# 更新FP树
def updateTree(items, inTree, headerTable, count):
if items[0] in inTree.children:
inTree.children[items[0]].count += count
else:
inTree.children[items[0]] = TreeNode(items[0], count, inTree)
if headerTable[items[0]][1] is None:
headerTable[items[0]][1] = inTree.children[items[0]]
else:
updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
if len(items) > 1:
updateTree(items[1:], inTree.children[items[0]], headerTable, count)
# 更新头指针表
def updateHeader(nodeToTest, targetNode):
while nodeToTest.nodeLink:
nodeToTest = nodeToTest.nodeLink
nodeToTest.nodeLink = targetNode
# 向上遍历树节点
def ascendTree(leafNode, prefixPath):
if leafNode.parent:
prefixPath.append(leafNode.name)
ascendTree(leafNode.parent, prefixPath)
# 寻找项的前缀路径
def findPrefixPath(basePat, treeNode):
condPats = {}
while treeNode:
prefixPath = []
ascendTree(treeNode, prefixPath)
if len(prefixPath) > 1:
condPats[frozenset(prefixPath[1:])] = treeNode.count
treeNode = treeNode.nodeLink
return condPats
# 递归查找频繁项集
def frequentItems(item, conditionalFPtree, freqItems):
for child in conditionalFPtree.children.values():
count = child.count
freqItems.append((item + [child.name], count))
frequentItems(item + [child.name], child, freqItems)
# 查找频繁项集
def findFrequentItems(headerTable, prefix, minSup, freqItems):
sortedItems = sorted(headerTable.items(), key=lambda p: p[1][0])
for item, _ in sortedItems:
newPrefix = prefix.copy()
newPrefix.add(item)
support = headerTable[item][0]
freqItems.append((list(newPrefix), support))
conditionalFPtree, _ = createTree(findPrefixPath(item, headerTable[item][1]), minSup)
if conditionalFPtree:
frequentItems([item], conditionalFPtree, freqItems)
# 从CSV文件加载数据并预处理
data = pd.read_csv('ShopCT.csv')
dataset = {frozenset([col for col in data.columns if row[col] == 1]): 1 for _, row in data.iterrows()}
# 设置最小支持度
minSupport = 2
# 创建FP树并挖掘频繁项集
myFPtree, myHeaderTab = createTree(dataset, minSupport)
myFreqItems = []
findFrequentItems(myHeaderTab, set(), minSupport, myFreqItems)
#显示频繁项集及其支持度
print("频繁项集及支持度:")
for item, support in myFreqItems:
print(f"{item} : {support}")
- 运行截图
- 实验总结
实验过程中,成功构建了FP树并有效地挖掘出频繁项集。FP-Growth比Apriori算法更为高效,尤其适用于大数据集。通过简化输出结果,我们能清楚地看到每个频繁项集及其对应的支持度。总体来看,FP-Growth算法具备了很好的实用性,特别是在频繁项集挖掘中具有明显的优势。
出现的问题:在最开始运行代码程序后不管怎样设置支持度的大小生成的都是空集。
解决方案:通过查阅资料,在电脑上搜寻资料,发现是因为自己数据格式转化错误以及findFrequentItems中的代码逻辑有问题,改正错误后,代码可以运行了