【机器学习】采用关联规则算法分析蔬菜涨跌规律实验(Apriori算法)

实验目的:在掌握关联规则算法的原理的基础上,能够应用关联规则算法解决实际问题。
实验内容:根据实验数据,采用Apriori等关联规则发现算法,给出相关关联规则。
实验要求:给出数据预处理过程、关联规则发现算法及发现关联规则,并对关联规则结果进行分析说明。
实验题目:蔬菜价格相关性分析
蔬菜的价格会受季节、天气等多方面因素的影响,但许多会出现同涨或者同跌等现象,请根据给出的蔬菜价格数据,采用关联规则发现算法,发现哪些蔬菜之间具有同涨、同跌或者涨跌不同步的现象。
数据格式如下:
在这里插入图片描述

一、背景知识介绍

Apriori算法这里就不详细介绍了,毕竟这篇博客的重点是记录解决实验的过程。

象征性地推荐一些链接:
Apriori算法详解之【一、相关概念和核心步骤】

数据挖掘十大算法(四):Apriori(关联分析算法)

二、数据处理

做机器学习的实验,很大一部分或者说主要的工作,就是对数据的处理。

同时,对数据处理的方法也是多种多样的,路漫漫呀~~

这里采用了xlrd/xlwt 和 pandas 包进行数据的处理(这两种包单独使用当然可以处理好数据,这里主要是为了展现知识的丰富性),阅读下面代码前建议应先了解这两个包的基本用法。

1、数据处理第一部分:采用xlrd/xlwt将数据变得规整

思路点播博客链接:使用Apriori实现蔬菜价格涨跌关联分析
简单了解博客链接:python使用xlrd和xlwt读写Excel文件学习笔记(解决xlwt写入日期格式错误的问题)

知识点参考链接之一:

import xlrd; #导入读取excel表格数据包(第三方模块)
import xlwt; #导入写入excel表格数据包(第三方模块)

#一、准备部分
#1、Excel表格读的部分:
excel=xlrd.open_workbook('蔬菜价格3.xls') #读入本地名为‘蔬菜价格3.xls’的Excel表格
excelsheet=excel.sheets()[0]#获取excel表格中的第一个sheet工作表

#2、Excel表格写的部分:
newexcel = xlwt.Workbook(encoding = 'utf-8') #创建一个Excel对象,编码格式采用utf-8
#创建一个表名为“蔬菜价格关联分析”的工作表,cell_overwrite_ok=True表示可覆盖之前已操作的单元格
newsheet=newexcel.add_sheet('蔬菜价格关联分析',cell_overwrite_ok=True)

#3、设置表格样式,这里主要用于设置日期的格式
datastyle = xlwt.XFStyle() # 创建一个样式对象,初始化样式
datastyle.num_format_str = 'M/D/YY' #设置日期格式

#4、表格中有的空的位置不是真的为空,这里获取一个这样的值,待会用于特判
tmp=excelsheet.cell_value(5,2)

#二、正式开始处理数据,主要思路:从旧表中读出写入新表
ri=2 #用于读原始第ri行
rj=0#用于读原始第rj列
wi=0#用于写入表格的第wi行
wj=1#用于写入表格的第wj列
#创建新表的第一行,包括日期和各个菜的菜名
newsheet.write(0,0,excelsheet.cell_value(0,0))#将旧表的(0,0)位置的日期写到新表的(0,0)位置

#1、先读取蔬菜列和禽肉类的列名,暂时先不处理禽肉类的一个K位置为空值的特殊情况
while ri<42:
    rj=1#蔬菜在第1列,有40种,肉类在第3列,有23种
    newsheet.write(0,wj,excelsheet.cell_value(ri,rj))
    newsheet.write(0,wj+40,excelsheet.cell_value(ri,rj+2))
    ri=ri+1
    wj=wj+1

#2、先整理蔬菜的价格
ri=42 #遍历原始第ri行
rj=2#遍历原始第rj列
wi=0#写入表格的第wi行
wj=1#写入表格的第wj行
while ri<22320:
    if ri%47>=42:
        ri=ri+6
        wi=wi+1
        newsheet.write(wi,0,excelsheet.cell_value(ri,0),datastyle)
    else:
        if excelsheet.cell_value(ri,rj)!=tmp:
            newsheet.write(wi,wj,excelsheet.cell_value(ri,rj))              
        wj=wj%40+1
    ri=ri+1

#3、再整理禽肉类的价格
ri=26 #遍历原始第ri行
rj=4#遍历原始第4列
wi=0#写入表格的第wi行
wj=41#写入表格的第wj行,从第41列开始
while ri<22320:
    if ri%47>=26:
        ri=ri+22
        wi=wi+1
        wj=41
    else:
        if excelsheet.cell_value(ri,rj)!=tmp:      
            newsheet.write(wi,wj,excelsheet.cell_value(ri,rj))              
        wj=wj%(41+24)+1
    ri=ri+1
  
newexcel.save('蔬菜数据处理过渡版.xls')#生成处理后的表格

处理后数据示意图:
在这里插入图片描述
(小提示:要是看到部分日期显示为“XXXX”,把单元格拉宽就好了)

2、数据处理第二部分:采用pandas相关函数将表格变得更好操作:

import pandas as pd

#header = None用于不让蔬菜名那一行作为“表头”,便于之后步骤的数据获取
data=pd.read_excel('蔬菜数据处理过渡版.xls',header = None)

#删除第60列空值列,axis=1表示纵向,inplace=True表示删除操作对原数据生效
data.drop(labels=[60],axis=1,inplace=True)

ri=1
while ri<data.shape[0]-1:
    rj=1
    while rj<data.shape[1]:
        if (len(str(data.values[ri,rj]))==0 or len(str(data.values[ri+1,rj]))==0):
            data.iloc[ri,rj]=0
        else:
            d=float(data.values[ri,rj])-float(data.values[ri+1,rj]) #上-下 今天-昨天  正为涨,负为跌
            if d>0:
                data.iloc[ri,rj]='up'
            elif d<0:
                data.iloc[ri,rj]='down'
            else:
                data.iloc[ri,rj]=0
        rj=rj+1
    ri=ri+1

data.drop(labels=[data.shape[0]-1],axis=0,inplace=True)#删除最后一行
   
data.to_excel("蔬菜数据处理完成版.xlsx")

处理后数据示意图:
在这里插入图片描述

三、采用Apriori算法对蔬菜价格数据进行关联分析

1、完整代码
代码参考于:数据挖掘十大算法(四):Apriori(关联分析算法)

我自己简略分析代码的过程:【机器学习】Apriori(关联分析算法)经典代码理解与学习笔记(Python)

import pandas as pd

#获取数据
def vegetable():
    excel=pd.read_excel('蔬菜数据处理完成版.xlsx',header = None)
    data=[]
    ri=2 #结合表格,确定需要获取数据的位置
    while ri<excel.shape[0]:
        rj=1
        temp=[]
        while rj<excel.shape[1]:
            if excel.values[ri,rj]=='up':
                temp.append(excel.values[1,rj]+'up')
            elif excel.values[ri,rj]=='down':
                temp.append(excel.values[1,rj]+'down')
            rj=rj+1
        data.append(temp)
        ri=ri+1
    return excel.shape[0]-2,data
 
# 将所有元素转换为frozenset型字典,存放到列表中
def createC1(dataSet):
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])
    C1.sort()
    # 使用frozenset是为了后面可以将这些值作为字典的键
    return list(map(frozenset, C1))  # frozenset一种不可变的集合,set可变集合
 
# 过滤掉不符合支持度的集合
# 返回 频繁项集列表retList 所有元素的支持度字典
def scanD(D, Ck, minSupport):
    ssCnt = {}
    for tid in D:
        for can in Ck:
            if can.issubset(tid):   # 判断can是否是tid的《子集》 (这里使用子集的方式来判断两者的关系)
                if can not in ssCnt:    # 统计该值在整个记录中满足子集的次数(以字典的形式记录,frozenset为键)
                    ssCnt[can] = 1
                else:
                    ssCnt[can] += 1
    numItems = float(len(D))
    retList = []        # 重新记录满足条件的数据值(即支持度大于阈值的数据)
    supportData = {}    # 每个数据值的支持度
    for key in ssCnt:
        support = ssCnt[key] / numItems
        if support >= minSupport:
            retList.insert(0, key)
        supportData[key] = support 
    return retList, supportData # 排除不符合支持度元素后的元素 每个元素支持度
 
# 生成所有可以组合的集合
# 频繁项集列表Lk 项集元素个数k  [frozenset({2, 3}), frozenset({3, 5})] -> [frozenset({2, 3, 5})]
def aprioriGen(Lk, k):
    retList = []
    lenLk = len(Lk)
    for i in range(lenLk): # 两层循环比较Lk中的每个元素与其它元素
        for j in range(i+1, lenLk):
            L1 = list(Lk[i])[:k-2]  # 将集合转为list后取值
            L2 = list(Lk[j])[:k-2]
            L1.sort(); L2.sort()        # 这里说明一下:该函数每次比较两个list的前k-2个元素,如果相同则求并集得到k个元素的集合
            if L1==L2:
                retList.append(Lk[i] | Lk[j]) # 求并集
    return retList  # 返回频繁项集列表Ck
 
# 封装所有步骤的函数
# 返回 所有满足大于阈值的组合 集合支持度列表
def apriori(dataSet, minSupport = 0.5):
    D = list(map(set, dataSet)) # 转换列表记录为字典 
    C1 = createC1(dataSet)      # 将每个元素转会为frozenset字典    [frozenset({1}), frozenset({2}), frozenset({3}), frozenset({4}), frozenset({5})]
    L1, supportData = scanD(D, C1, minSupport)  # 过滤数据
    L = [L1]
    k = 2
    while (len(L[k-2]) > 0):    # 若仍有满足支持度的集合则继续做关联分析
        Ck = aprioriGen(L[k-2], k)  # Ck候选频繁项集
        Lk, supK = scanD(D, Ck, minSupport) # Lk频繁项集
        supportData.update(supK)    # 更新字典(把新出现的集合:支持度加入到supportData中)
        L.append(Lk)
        k += 1  # 每次新组合的元素都只增加了一个,所以k也+1(k表示元素个数)
    return L, supportData


# 获取关联规则的封装函数
def generateRules(L, supportData, minConf=0.7):  # supportData 是一个字典
    bigRuleList = []
    for i in range(1, len(L)):  # 从为2个元素的集合开始
        for freqSet in L[i]:
            # 只包含单个元素的集合列表
            H1 = [frozenset([item]) for item in freqSet]    # frozenset({2, 3}) 转换为 [frozenset({2}), frozenset({3})]
            # 如果集合元素大于2个,则需要处理才能获得规则
            if (i > 1):
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf) # 集合元素 集合拆分后的列表 。。。
            else:
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList
 
# 对规则进行评估 获得满足最小可信度的关联规则
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    prunedH = []  # 创建一个新的列表去返回
    for conseq in H:
        conf = supportData[freqSet]/supportData[freqSet-conseq]  # 计算置信度
        if conf >= minConf:
            #print(set(freqSet-conseq),'-->',set(conseq),'conf(置信度):',conf,',',set(freqSet),'的支持度supportData:',supportData[freqSet])
            print("{}——>{}的置信度为:{:.6f},支持度为:{:.6f}".format(set(freqSet-conseq),set(conseq),conf,supportData[freqSet]))
            brl.append((freqSet-conseq, conseq, conf))
            prunedH.append(conseq)
    return prunedH
 
# 生成候选规则集合
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
    m = len(H[0])
    if (len(freqSet) > (m + 1)): # 尝试进一步合并
        Hmp1 = aprioriGen(H, m+1) # 将单个集合元素两两合并
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        if (len(Hmp1) > 1):    #need at least two sets to merge
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
 
N,dataSet = vegetable()
#在这里控设置最小支持度
L,suppData = apriori(dataSet,minSupport=float(40)/float(N))
#在这里设置最小置信度
rules = generateRules(L,suppData,minConf=0.3)
#print("具有强关联规则的项集为:",rules)

2、结果稍微分析
①上面的代码输出结果:
在这里插入图片描述
②可通过设置支持度和置信度的不同,使显示结果数量不同!
例如,将要求都设置低一些:
在这里插入图片描述
这样就有机会看到“多推多”的情况了(部分显示结构截取):
在这里插入图片描述
在这里插入图片描述

③值得注意的是:“up”和“down”是可以互推的
在这里插入图片描述
(frozenset({‘茭瓜up’}), frozenset({‘韭菜down’}), 0.22988505747126436), (frozenset({‘韭菜down’}), frozenset({‘茭瓜up’}), 0.273972602739726)

后记

事情太多,随便改改,草草写完,莫得办法。
日后再补?

历史的经验告诉我,写完一篇博客之后再去完善补充一般是小概率事件,人生每个阶段都有每个阶段该去完成的任务,学会安排时间分配时间非常重要!

时光匆匆,且惜时光吧,少年ㄟ( ▔, ▔ )ㄏ

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值