数据挖掘与分析——关联规则模型

一、预处理

1.打开csv打分表,并指出数据存在的问题,给出相应的解决方案;

        打分表中,前五行数据为空白或不在统计范围内的数据,第六行数据为列名,因此需要从第七行开始进行存储。在列名中,存在许多同学所处分组、得分、提交标题和提交内容为空,还有一名同学专业和班级为空,因此在进行数据统计值分析前,需要把统计依照的列为空的项以及得分为空的项删除。

2.使用python从csv文件中提取数据,使用合适的数据结构进行保存,随后进行预处理;

# 将xlsx文件转化为csv文件
def xlsx_to_csv():
    workbook = xlrd.open_workbook('C:/Users/admin/Desktop/数据挖掘/实验一/数据挖掘与分析-你说对就.xlsx')
    table = workbook.sheet_by_index(0)
    with codecs.open('数据挖掘与分析-你说对就.csv', 'w', encoding='gbk') as f:
        write = csv.writer(f)
        for row_num in range(table.nrows):
            row_value = table.row_values(row_num)
            write.writerow(row_value)

def loadDataSet():
    xlsx_to_csv()
    fp = open('数据挖掘与分析-你说对就.csv', 'r')
    df = [line.strip().split(',') for line in fp.readlines()[6:]]
    dataset = []
    for d in df:
        if len(d) > 9:   # 不能出现过多空数据项
            if d[8] != '' and d[9] != '':
                dataset.append([d[8], d[9]])
    return dataset

       由于课程资料给的是xlsx文件,为了方便后续操作,编写xlsx_to_csv函数,将xlsx文件转为csv文件。随后编写loadDataset函数,通过判断分隔符“,”对数据进行分割,并从第七行数据开始获取,存放入dataframe,并创建事务集dataset,通过if语句判断每一行数据是否符合数据统计分析的要求,即相关数据是否为空,再将符合条件的数据append给dataset,最后得到的dataset就是参与数据统计的数据行的集合。

       在将xlsx文件转为csv文件时,一开始使用的encoding是utf-8,转为csv之后用写字板打开,发现出现了大量乱码,网上查阅资料后将encoding换成了gbk,随后就没有乱码了。而在对数据进行选择时,一开始混淆了dataframe和list,并且没有理解如何判断dataframe的某一列是否为空,导致频频出现报错,最后通过for循环找到了每一行,再依次判断数据的可用性,符合要求则存入dataset。

3.针对各列评分属性,进行数据统计值分析,并说明和解释分析结果。

if __name__=='__main__':
    dataSet = loadDataSet()
    df = pd.DataFrame(dataSet, columns=["所处分组", "得分"])
    df[['得分']] = df[['得分']].apply(pd.to_numeric)
    df = df.groupby('所处分组')['得分'].mean().sort_values()
    df.to_csv('你说对就队.csv')
    print(df)

       为了有效地进行数据统计值分析,通过调用loadDataset函数获取想要的列存入事务集dataSet,随后将dataSet存入dataframe便于之后的操作,同时根据loadDataset中获取的列为数据设置列名。然后,调用groupby函数来为数据根据设置的列名进行分组,再调用mean函数根据设置的列名对每一组取平均值。此处根据“所处分组”进行分组,求出每一组的得分均值。最后根据求出的均值进行排序,导出为csv文件并进行结果的打印。

       调用groupby函数与mean函数时,是根据列名来进行的,而先前导出的dataSet并没有设置列名,因此需要在转为dataframe时设置列名,方便数据统计值分析。在尝试运行后发现存在报错,和同学交流探讨后发现是“得分”的数据类型为文本而非数值,无法对其求平均值,因此补充了一条df[['得分']].apply(pd.to_numeric)语句,将“得分”的数据类型转为数值,再运行就成功求出平均值了。

       我分别根据所处分组、专业、院系这三项进行分组并求得分的平均值,并进行升序排列。从按“所处分组”进行分组的结果图中可以看出,第六组打分最高,为96分,第20组打分最低,为79分,分数跨度较大,但普遍打分在90分及以上。从按“专业”进行分组的结果图中可以看出,交通运输(汽车运用工程)(中美合作)这一专业的同学打分最高,为96分,而数据科学与大数据技术的同学打分最低,平均分约为90分,平均分都在90分以上。从按“院系”进行分组的结果图中可以看出,每个院系打分的平均分都在90分至94分之间。通过以上三组数据可以看出,大部分同学对该组的汇报认可度较高,仅有第20组和第3组同学较为不满意,可以询问该两组同学相关改善建议。

 

 

 

 二、关联规则算法

1.调试并理解Apriori算法的源代码实现apriori.py。

       apriori.py中,主程序中先调用了loadDataSet函数,将统计数据返回给dataSet,随后调用apriori函数。在apriori函数中,先调用createC1函数,从事务集中获取候选1项集,再事务集的每个元素转化为集合,然后调用scanD函数获取频繁1项集和对应的支持度并存入L。之后进入循环,调用aprioriGen函数找到k阶候选项集,再调用scanD函数获取对应的k阶频繁项集和支持度并append到L,更新supportData。这样就获得了所有的频繁项集和支持度。

       createC1函数通过循环嵌套定位到每个元素,随后判断该元素是否已存在于候选1项集C1中,若不存在则append至C1,查找完所有元素后排序。返回数据为候选1项集。

       scanD函数利用之前获得的k阶候选项集和事务集元素的集合,通过循环查找事务集元素的每个集合中,每个候选项集出现的次数,存入ssCnt。随后通过循环计算每个候选项集的支持度,再判断支持度是否大于预先设置的最小支持度,若大于,则为频繁项集,插入retList的首部,并将支持度存入supportData。返回数据为在Ck中找出的频繁项集以及各频繁项集的支持度。

       aprioriGen函数接收的第一个参数是多个数据组,将Lk中各组元素按照一定原则每两组之间合并成一组。 其中规则与k值相关,k=2时,如果第一组数据和第二组数据前两个数据排序后相同,则合并这两个数据组。最后返回数据为合并数据组后的retList。

 2.将数据集改为:

TID

商品列表

101

面包

可乐

麦片

102

牛奶

可乐

103

牛奶

面包

麦片

104

牛奶

可乐

105

面包

鸡蛋

麦片

106

牛奶

面包

可乐

107

牛奶

面包

鸡蛋

麦片

108

牛奶

面包

可乐

109

面包

可乐

def loadDataSet():
    return [['面包', '可乐', '麦片'], ['牛奶', '可乐'], ['牛奶', '面包', '麦片'], ['牛奶', '可乐'], ['面包', '鸡蛋', '麦片'], ['牛奶', '面包', '可乐'], ['牛奶', '面包', '鸡蛋', '麦片'], ['牛奶', '面包', '可乐'],['面包', '可乐']]

       数据集的导入仅与loadDataset的返回值有关,故将返回值改为商品列表的事务集即可。

3.改进代码,从频繁项集结果中,提取2阶频繁项集。

L, suppData = apriori(dataSet, minSupport=0.3)
print(L[1])

       k阶频繁项集由scanD函数得出后存放于L[k-1]中,因此提取2阶频繁项集只需要打印L[k-1]即可。但因最小支持度设置过高的话,则不存在2阶频繁项集,打印结果为空集合。故将最小支持度minSupport设置为0.3来进行实验,得出相应2阶频繁项集。

4.使用seaborn工具包,实现2阶频繁项集的热力图表示。

def draw_seaborn(suppData, n):
    list1 = []
    list2 = []
    for i in range(n):
        list3 = []
        list1.append(i + 1)     # 存放已查找的2阶项集
        for j in range(n):
            a = list(map(frozenset, [[i + 1, j + 1]]))[0]
            if a in suppData:
                list3.append(suppData[a])    # 存放当前元素的所有2阶项集的支持度
            else:
                list3.append(0.00)
        list2.append(list3)                  # 存放每一个元素对应的所有2阶项集的支持度
    data = {}
    for num in range(n):
        data[list1[num]] = list2[num]         # 将list2中存放的每一个元素对应的所有2阶项集的支持度存放到对应list1的data数据集中
    df = pd.DataFrame(data, index=list1, columns=list1)
    return sns.heatmap(df, linewidths=.5, annot=True, yticklabels=List_Dataset, xticklabels=List_Dataset)

       draw_seaborn函数先创建了list1和list2两个列表,list1用于存放当前正在获取2阶项集支持度的元素,list2用于存放每一个元素对应的所有2阶项集的支持度。

       然后进入循环,依次获取每个元素对应的所有的2阶项集支持度,同时创建list3,用于存放当前元素的所有2阶项集支持度。

       随后嵌套循环,依次获取当前元素的每一个2阶项集的支持度。实现方法是判断该2阶项集是否存在于之前apriori函数得出的支持度数据集suppData中。若不存在,则说明该2阶项集支持度为0;若存在,则为suppData中对应的值。将结果append给list3,结束内循环后,得到当前元素的完整的list3,再将list3依次append给list2,合成总支持度列表,结束外循环。

       下一步依旧运用循环,将list2中的数据,一一对应地赋给data列表,key为list1中对应的元素,从而将支持度与元素绑定,这才能在之后将data转为dataframe型,并为其index和column的命名直接赋list1的值。此处由于index与column名称相同,都为list1,所以不对二者进行区分,统一使用list1。若index与column名称不同,则需另外创建一个list2用于存放index的名称。

       最后返回的值调用了seaborn工具包中的heatmap函数,即热力图。df为热力图提供矩形数据集,其index和column信息分别对应到热力图的的columns和rows。linewidths定义了热力图中的每个方格之间的间隔大小。annot默认为False,当annot为True时,表明在热力图中的每个方格写入数据。xticklabels控制每列标签名的输出,yticklabels控制每行标签名的输出,此处应当设置为list1,但由于list1指代物品与List_Dataset一致,为了图像信息更加明确清晰,此处设置为List_Dataset。

5.改进代码,计算2阶频繁项集的置信度(Confidence)和提升度(Lift)。定义最小置信度阈值,并生成和输出2阶关联规则。

def data_deal(L, suppData):
    min = -1
    DataList = []
    for i in range(len(L[1])):
        for j in L[1][i]:
            if i > min:
                min = i
                List = []
                List.append(suppData[L[1][i]])
            List.append(j)
        DataList.append(List)
    Datanum = {}
    for m in range(len(L[0])):
        Datanum[len(L[0]) - m] = L[0][m]
    return DataList, Datanum

# 计算置信度
def confidence(DataList, Datanum, suppData):
    confidence_data = {}
    for num in range(len(DataList)):
        str1 = List_Dataset[DataList[num][1]-1] + '->' + List_Dataset[DataList[num][2]-1]
        confidence_data[str1] = DataList[num][0] / suppData[Datanum[DataList[num][1]]]
        str2 = List_Dataset[DataList[num][2]-1] + '->' + List_Dataset[DataList[num][1]-1]
        confidence_data[str2] = DataList[num][0] / suppData[Datanum[DataList[num][2]]]
    return confidence_data

# 计算提升度
def Lift(DataList, confidence_data, Datanum, suppData):
    Lift_data = {}
    for num in range(len(DataList)):
        str1 = List_Dataset[DataList[num][1]-1] + '->' + List_Dataset[DataList[num][2]-1]
        Lift_data[str1] = confidence_data[str1] / suppData[Datanum[DataList[num][2]]]
        str2 = List_Dataset[DataList[num][2]-1] + '->' + List_Dataset[DataList[num][1]-1]
        Lift_data[str2] = confidence_data[str2] / suppData[Datanum[DataList[num][1]]]
    return Lift_data

# 定义最小置信度阈值
def limit(confidence_data, Lift_data, minConfidence=0.5):
    limit_data = []
    for k in confidence_data.keys():
        data = []
        if confidence_data[k] >= minConfidence:
            data.append(k)
            data.append(confidence_data[k])
            data.append(Lift_data[k])
            limit_data.append(data)
    return limit_data

if __name__ == '__main__':
    dataSet = loadDataSet() 
    List_Dataset = ['面包', '可乐', '麦片', '牛奶', '鸡蛋']
    L, suppData = apriori(dataSet, minSupport=0.2)
    DataList, Datanum = data_deal(L, suppData)
    confidence_data = confidence(DataList, Datanum, suppData)
    Lift_data = Lift(DataList, confidence_data, Datanum, suppData)
    limit_data = limit(confidence_data, Lift_data, minConfidence=0.5)
    for i in limit_data:
        print(i)
    draw_seaborn(suppData, len(L[0]))
    plt.show()

       为了计算2阶频繁项集的置信度与提升度,需要先获取所有2阶频繁项集及其支持度的列表集合DataList和涉及到的1阶频繁项集的集合Datanum。data_deal函数满足了这一需求,函数体中利用循环嵌套的方式,内循环找到每一个1阶频繁项集对应2阶频繁项集的支持度,随后将支持度与2阶项集append至List,外循环将每个List逐一append给DataList。另外,data_deal又通过循环,将1阶频繁项集依次赋给Datanum。最后返回DataList和Datanum。

       confidence函数用于计算置信度。通过循环逐一将DataList中的每个List在List_Dataset中对应的物品名称以不同的顺序用“->”连接来表示关联规则,并存入str1与str2。随后计算相应的置信度,并以str1和str2作为confidence_data的key,将置信度存入confidence_data[str1]和confidence_data[str2]。最后返回的confidence_data中包含所有2阶频繁项集的置信度。 

       Lift函数用于计算提升度,函数体大致与confidence函数一致,仅在公式上有所区别。计算置信度时是2阶频繁项集的支持度除以1阶频繁项集的支持度,而计算提升度时是2阶频繁项集的置信度除以1阶频繁项集的支持度。

       limit函数定义最小置信度阈值,通过循环与判断选出大于等于最小置信度的confidence_data依次将相应的key、confidence_data、Lift_data逐一append给data,再将每一个data逐一append给limit_data,最后返回的limit_data就是所有符合最小置信度阈值的事务集,共有三个column,依次为关联规则、置信度、提升度,这就是最终输出的结果。

       为了便于编写代码,在输入数据时,物品名称用数字代替,这导致最终打印出的结果中,物品名称无法显示,不好理解。因此在confidence函数与lift函数中,对str1与str2进行了修改,用List_Dataset来进行编号与物品名称的转换。一开始没有注意到list下标与物品名称的标号相差了1,导致越界报错,在下标中又减去了1,就成功输出了结果。

       根据最终得出的结果进行分析,可以得知“鸡蛋->面包”、“鸡蛋->麦片”、“麦片->面包”的置信度都为1.0,这说明购买了前者的顾客有极大概率购买后者。“麦片->鸡蛋”、“鸡蛋->麦片”的提升度高达2.25,这说明顾客常常捆绑购买这两件商品,在计划促销活动时可以优先考虑鸡蛋和麦片一同销售。

三、实验心得 

       通过第一题,我学会了如何将xlsx文件转为csv文件,利用split()函数对csv文件进行元素分割后提取需要的数据至dataframe,存为数据矩阵。随后通过循环定位到dataframe中的每一个list,再如何通过if语句来判断是否是自己需要的元素,进行数据预处理。此外,我还学会了dataframe的apply.(pd.to_numeric)、groupby()、mean()等函数,以及将dataframe数据转为csv文件需要用到的dataframe的to_csv()函数的用法。

       第一题中学到的知识可以应用于一些基础的数据统计值分析,例如生活中的分析成绩、账单流水、人群喜好分析等,这些数据分析需要分组、求均值、排序这些功能,而这一题中学到的知识就和这些需求十分契合。至于将xlsx文件转为csv文件这一功能,先前用Navicate实现过,这次学会了用python来实验,拓宽了知识。

       通过第二题,我实际地了解到apriori算法可以发现频繁项集,而其具体实现用到了createC1、scanD、aprioriGen、apriori这四个函数,其中apriori函数内包含了对前三个函数的调用。三个函数各司其职,createC1负责获取候选1项集,scanD负责找出候选集中的频繁项集,aprioriGen通过频繁项集生成候选项集。

       在了解apriori算法的基础上,我还学会了用seaborn工具包中的heatmap函数实现了热力图的显示,以及将数据整理为所有2阶频繁项集及其支持度的列表集合DataList和相关的1阶频繁项集的集合Datanum,之后根据公式编写置信度计算函数confidence和提升度计算函数Lift,其中要注意的是,由于涉及list过多,因此key值需要多加思考,不然容易造成越界。最后编写limit函数来定义最小置信度阈值,k本身的意义就是2阶关联规则中频繁项集的关系,而confidence_data[k]和Lift_data[k]又分别代表了置信度与提升度,因此将这三个数据存入一个list作为一组数据,又接到limit_data的尾部,所以最后limit_data内存储的就是所有符合最小置信度阈值的关联规则。

       第二题中学到的知识可以用于挖掘数据关联规则,找出频繁项集,因此在实际生活中更运用于商业,例如分析顾客会更加倾向于购买哪一种商品组合,如何分配商品能够使其更加热销,什么种类的商品配以什么样的广告会更吸引顾客购买,这些问题都可以通过apriori算法来解决。

代码参考:https://www.ndmiao.cn/index.php/archives/93/

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

樱桃小叮当

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值