目 录
一、实验目的... 1
二、实验环境... 1
三、实验内容... 1
3.1 connect_string()函数解析... 1
3.2 find_rule()函数解析+纠错... 2
3.3 关联规则挖掘... 4
四、心得体会... 7
一、实验目的
(1)理解Aprori关联挖掘算法的程序编写;
(2)能熟练地使用Aprori关联挖掘算法分析相应的数据。
二、实验环境
(1)操作系统:windows11;
(2)python版本:3.10;
(3)Python开发环境:Jupyter,pycharm。
三、实验内容
3.1 connect_string()函数解析
阅读给出的Aprori关联挖掘算法,并注释。
附件给出了一个代码文件,其中包含两个函数,connect_string(x, ms)函数代码如下:
# 自定义连接函数,用于实现L_{k-1}到C_k的连接
def connect_string(x, ms):
#分隔输入的频繁项集为二维数据结构
x = list(map(lambda i: sorted(i.split(ms)), x))
#获取频繁项集的项目数
l = len(x[0])
r = []
#遍历频繁项集,得到下一步的候选集
for i in range(len(x)):
for j in range(i, len(x)):
if x[i][:l - 1] == x[j][:l - 1] and x[i][l - 1] != x[j][l - 1]:
r.append(x[i][:l - 1] + sorted([x[j][l - 1], x[i][l - 1]]))
return r |
这个代码的作用为:输入n-1个项目的频繁项集,输出n个项目的候选集。详细说明如下:
首先,输入参数为x和ms,其中x是一个列表,表示现有的n-1个项目的频繁项集,例如:[“西红柿--鸡蛋”,“排骨--鸡蛋”,“西红柿--鞋子”......],是一个包含两个项目的频繁项集。ms是分隔符号,用于分隔两个项目,此处的代码使用的分隔符为“--”,在这个函数中存入这个参数的目的是通过这个参数将频繁项集分解为多个项目;
其次,原本的一维列表通过代码“x = list(map(lambda i: sorted(i.split(ms)), x))”分隔为了二维列表,形如:[[’’西红柿’’,’’鸡蛋’’],[’’排骨’’,’’鸡蛋’’],[’’西红柿’’,’’鞋子’’]......];
随后,代码“l = len(x[0])”获取了传入的频繁项集包含的项目数“n-1”,用于后续构建“n”个项目的候选集;
最后,通过两个循环遍历所有事务,找出所有的候选集。
注意:这里得到的是“候选集”并不是“频繁项集”,另外也不是“完全的候选集”,因为代码采用了一定的“先验知识”,即:频繁项集的子集一定是频繁项集。
3.2 find_rule()函数解析+纠错
附件代码中另一个函数为find_rule()函数,里面调用了上述的connect_string()函数,用于寻找关联规则,但是代码中存在错误,已经标红,原始代码如下:
# 寻找关联规则的函数
def find_rule(d, support, confidence, ms=u'--'):
result = pd.DataFrame(index=['support', 'confidence']) # 定义输出结果
support_series = 1.0 * d.sum() / len(d) # 支持度序列
column = list(support_series[support_series > support].index) # 初步根据支持度筛选
k = 0
while len(column) > 1:
k = k + 1
print(u'\n正在进行第%s次搜索...' % k)
column = connect_string(column, ms)
print(u'数目:%s...' % len(column))
sf = lambda i: d[i].prod(axis=1, numeric_only=True) # 新一批支持度的计算函数
# 创建连接数据,这一步耗时、耗内存最严重。当数据集较大时,可以考虑并行运算优化。
d_2 = pd.DataFrame(list(map(sf, column)), index=[ms.join(i) for i in column]).T
support_series_2 = 1.0 * d_2[[ms.join(i) for i in column]].sum() / len(d) # 计算连接后的支持度
column = list(support_series_2[support_series_2 > support].index) # 新一轮支持度筛选
support_series = support_series.append(support_series_2)
column2 = []
for i in column: # 遍历可能的推理,如{A,B,C}究竟是A+B-->C还是B+C-->A还是C+A-->B?
i = i.split(ms)
for j in range(len(i)):
column2.append(i[:j] + i[j + 1:] + i[j:j + 1])
cofidence_series = pd.Series(index=[ms.join(i) for i in column2]) # 定义置信度序列
for i in column2: # 计算置信度序列
cofidence_series[ms.join(i)] = support_series[ms.join(sorted(i))] / support_series[ms.join(i[:len(i) - 1])]
for i in cofidence_series[cofidence_series > confidence].index: # 置信度筛选
result[i] = 0.0
result[i]['confidence'] = cofidence_series[i]
result[i]['support'] = support_series[ms.join(sorted(i.split(ms)))]
result = result.T.sort_values(['confidence', 'support'], ascending=False) # 结果整理,输出
print(u'\n结果为:')
print(result)
return result |
该函数的用途为:将输入的多个事务通过apriori算法进行处理,得到关联规则,具体解析如下:
首先,传入一个bool矩阵d,包含多个事务与事务的全部项目情况,事务有该项目则置为“True”或“1”,无项目则置为“False”或“0”;另外传入自定义的最小支持度“support”和最小置信度“confidence”;同时默认的分隔符为“--”可以选择自定义;
其次,通过“support_series = 1.0 * d.sum() / len(d)”分别计算出单个项目的支持度,并通过“column = list(support_series[support_series > support].index)”筛除不符合最小支持度的项集;
随后,通过一个循环不断探索当前候选集下满足最小支持度和最小置信度的关联规则,每个循环结束使用connect_string()函数得到下一个候选集。每一次循环得到的关联规则追加到一个序列中,但是追加的代码出现了一个错误“support_series = support_series.app end(support_series_2)”,series结构的数据不能使用“append”方法进行追加,可以先转化为list进行追加或者使用“concat()”函数直接将两个series进行合并,因此修正后的代码为:
support_series = pd.concat([support_series,support_series_2]) |
最后,直到候选集不再能得到关联规则,推出循环,将得到的support_series转化为矩阵进行输出,具体输出效果见3.3。
3.3 关联规则挖掘
以超市购买记录数据为例,利用关联规则挖掘算法挖掘其关联规则。将其数据整理到一个Excel表格中(文件命名为tr.xlsx),其形式如下:
利用给出的Aprori关联挖掘算法,最小支持度为0.2,最小置信度为0.4,调用apriori函数挖掘其关联规则。
首先,由于附件代码仅仅给出了功能函数,主函数需要补充,内容如下:
df = pd.read_excel('tr.xlsx',header = None,index_col = 0)
df |
此处代码功能为:读取excel文件,并且确定第一列为索引无标题行,运行结果如下:
其次,需要提取出所有事务中涉及到的所有项目,代码如下:
#将每个事务读取为一个字符串,删除nan,用空格分隔并且删除相同元素
items = set()
for col in df.columns:
items.update(df[col].dropna().unique())
items = list(items)
items |
使用集合set可以提高效率,因为集合的查找操作平均时间复杂度是O(1),而列表的查找操作是O(n)。运行结果如下:
随后,将事务转化为bool矩阵,便于后续计算支持度与置信度,代码如下:
#创建bool矩阵d
d = pd.DataFrame(False, index = df.index, columns = items)
d |
运行结果如下,此时还未传入事务数据,全部置为False:
随后,填入bool矩阵,得到源数据的bool矩阵,代码如下:
#填充bool矩阵d
for idx, row in df.iterrows():
for item in row:
if pd.notnull(item):
d.at[idx, item] = True
d |
如果源事务中有项目,则置为True,运行结果如下:
最后,设定最小支持度为0.1,最小置信度为0.2,并将bool矩阵传入find_rule()函数,代码如下:
find_rule(d, 0.1, 0.2) |
运行结果如下,一共得到了77个关联规则:
四、心得体会
(1)在提取事务中所有项目的过程中:
#将每个事务读取为一个字符串,删除nan,用空格分隔并且删除相同元素
items = set()
for col in df.columns:
items.update(df[col].dropna().unique())
items = list(items)
items |
这段代码通过使用集合可以提高效率,因为集合的查找操作平均时间复杂度是O(1),而列表的查找操作是O(n)。因此,通常推荐使用集合来处理这类问题,然后再将其转换为列表,如果最终需要列表形式的话。
(2)在已有的关联规则中追加新的关联规则时:
series不能使用append追加,可以先转化为list进行追加或者使用“concat()”函数直接将两个series进行合并,因此修正后的代码为:
support_series = pd.concat([support_series,support_series_2]) |