【关联规则挖掘】基于Aprioir的蔬菜价格关联性分析

1. 什么是关联规则挖掘?

关联规则挖掘:用于发现数据库中属性之间的有趣联系。

如:用户在购买牛奶的时候,是否会同时购买面包?  购买面包的时候,是否会同时购买牛奶?

如图,可以看出4位顾客有3位同时购买了牛奶和面包,购买牛奶的顾客都购买了面包,购买面包的顾客都购买了牛奶,关联规则就是寻求此类关系。

2. 相关概念

事务:一次交易记录;

项:事务中的一个元素(商品);

项集:多件商品的组合;

k-项集:项集中包含个项(k件商品构成的项集);

频繁项集:对于一个项集,它出现在若干事务中;

支持度:表示同时包含项X和项Y的事务占所有事务的比例;

              {X,Y},support{X,Y} = P(X&Y) = 同时XY的事务/ 总事务数

最小支持度:人为设定的一个阈值,用来评判一个项集是否是频繁项集。若一个项集大于最小支持度即为频繁项集;

置信度:设X,Y位两个项集,则:

              confidence(X\RightarrowY) =  P(Y|X) = support(X,Y)/support(X)= 同时含XY的事务/  X的事务数

              eg:beer\Rightarrow Diaper的置信度=3/3=100%;

最小置信度:人为设定的一个阈值;

强关联规则:          support{X,Y} > 最小支持度

                               confidence(X\RightarrowY) > 最小置信度

3. Apriori算法实现过程

直接图解,文字表达可以看这:https://www.cnblogs.com/llhthinker/p/6719779.html

4. 实例

蔬菜价格相关性分析

蔬菜价格会受季节、天气等多方面因素的影响,但许多会出现同涨或同跌等现象,根据给出的蔬菜价格数据,采用关联规则发现算法,发现那些蔬菜之间具有同涨、同跌或者涨跌不同步的现象。

4.1 数据预处理

数据初始情况如下图,我们选择剔除空值,将数据重新组织,

import numpy as np
import pandas as pd

def DataArrage():
    data = pd.read_excel('蔬菜价格_原版.xls',sheet_name = '蔬菜价格3');
    data = data.drop_duplicates(['日期','蔬菜名'],'last');  #去除可能出现的一种蔬菜一天重复统计的可能
    data = data.drop_duplicates(['日期','肉食禽蛋'],'last');  # 去除可能出现的一种肉食禽蛋一天重复统计的可能
    data = data.dropna(); #去除含有缺失值的行

    #提取蔬菜类
    k1 = data['蔬菜名'] != '蔬菜类';
    vegetable = data[k1];
    #提取肉蛋类
    k2 = data['肉食禽蛋'] != '肉食禽蛋类';
    meat = data[k2];

    # 新数据
    dataveg = vegetable.pivot(index='日期', columns='蔬菜名', values='价格');
    datam =  meat.pivot(index='日期', columns='肉食禽蛋', values='批发价格');
    data = pd.merge(dataveg, datam, on="日期");

    #用Nan代替空格以便下面的dropna操作
    for i in data.columns:
        data[i] = data[i].apply(lambda x: np.NaN if str(x).isspace() else x)
        df_null = data[data[i].isnull()]
        df_not_null = data[data[i].notnull()]
    data = data.dropna(axis = 1)  # 去除没有价格的蔬菜/肉蛋(有空值的列)
                                  # 不选择填充是因为如果填充的数据不合适会影响涨跌关联分析

    # 存入excel表
    writer = pd.ExcelWriter('新蔬菜价格.xlsx')    #需要手动将excel日期列的数字分类设置为日期才能正常显示日期
    data.to_excel(writer)
    writer.save()

if __name__ == '__main__':
    DataArrage();

重新组织后的数据如下:

4.2 生成蔬菜价格涨跌情况

"""
pandas读出数据类型为DataFrame,为了方便操作数据,
使用xlwd和xlrd模块,读出的数据保存为元组,易于操作

1表示价格同比上一日增长
0 表示不变
-1表示跌

"""
import xlrd;
import xlwt;

def rise_fall():
    '''
    1表示价格同比上一日增长
    0 表示不变
    -1表示跌
    '''
    workbook = xlrd.open_workbook(r'新蔬菜价格.xlsx')
    workspace = xlwt.Workbook(encoding='ascii')
    booksheet = workbook.sheet_by_index(0)
    createsheet = workspace.add_sheet('蔬菜价格', cell_overwrite_ok=True)
    r_num = booksheet.nrows
    c_num = booksheet.ncols

    #生成横纵表头
    for r in range(0,r_num):
        createsheet.write(r, 0, booksheet.cell_value(r, 0))
        r = r + 1

    for c in range(0,c_num):
        createsheet.write(0, c, booksheet.cell_value(0, c))
        c = c + 1

    for c in range(1,c_num):  #日期最早的一天,没有对比
        createsheet.write(1,c,0)
    for r in range(2,r_num):
        for c in range(1,c_num):
            dis = booksheet.cell_value(r,c) - booksheet.cell_value(r-1,c)
            if dis > 0:
                createsheet.write(r,c,1)
            elif dis < 0:
                createsheet.write(r,c,-1)
            else:
                createsheet.write(r,c,0)
    workspace.save('蔬菜价格趋势.xls')

if __name__ == '__main__':
    rise_fall()

4.3 利用上面得到的数据进行Apriori分析

import xlrd

workbook = xlrd.open_workbook(r'蔬菜价格趋势.xls')
booksheet = workbook.sheet_by_index(0)
r_num = booksheet.nrows - 1;  # 第一行为标题
c_num = booksheet.ncols - 1;  # 第一列为时间
MAXI = 0  # 统计单日最多有多少个蔬菜存在涨跌
for i in range(1,r_num):
    tmp = 0
    for j in range(1,c_num):
        if booksheet.cell_value(i,j) != 0:
            tmp += 1
    if tmp > MAXI:
        MAXI = tmp

"""
将单日中出现的价格涨跌全部列入data
用up后缀表示涨的蔬菜名称,用down表示跌
"""
data = []
for i in range(1,r_num):
    t = set()
    for j in range(1,c_num):
        if booksheet.cell_value(i,j) == 1:
            t.add(booksheet.cell_value(0,j) + "_up")
        elif booksheet.cell_value(i,j) == -1:
            t.add(booksheet.cell_value(0,j) + "_down")
    data.append(t)

all_thing = []  # 所有事件
frequent_thing = []  # 用于储存所有的频繁项集

for j in range(1,c_num):
    l = []
    t = set()
    t.add(booksheet.cell_value(0,j) + "_up")
    all_thing.append(t)
    l.append(t)
    frequent_thing.append(l)
for j in range(1,c_num):
    l = []
    t = set()
    t.add(booksheet.cell_value(0,j) + "_down")
    all_thing.append(t)
    l.append(t)
    frequent_thing.append(l)


k = 0;
support = 30;  # 最小支持度
level = 2;
def frequent_1():  #生成频繁1项集
    for i in range(0,len(frequent_thing)):
        count = 0
        for j in range(0,len(data)):
            if frequent_thing[i][0].issubset(data[j]):   #ft[i][0]时否包含在data[j]中
                count += 1
        frequent_thing[i] += [count]
    i= 0
    while i < len(frequent_thing):
        if frequent_thing[i][1] < support:
            del frequent_thing[i];
        else:
            i = i + 1;

def isfrequentThing(fre_thing):  # 判断集合的子集是否都是频繁
    deset = set()
    relen = len(fre_thing)
    for i in range(0,len(all_thing)):
        tmp = 0
        deset = fre_thing.difference(all_thing[i])    #可以得到所有真子集
        if len(deset) == relen - 1:
            for j in range(0,len(frequent_thing)):
                if frequent_thing[j][0] == deset:
                    tmp = 1
                    break
            if tmp == 0:
                return 0
    return 1;

def fuction(listq, k):  # 循环主函数
    initlist = [];
    initset = set();
    g = 0
    for i in listq:
        if len(i[0]) == k - 1:  # 循环k-1项集
            for j in listq:
                if len(j[0]) == k - 1:
                    if i[0] != j[0]:
                        initset = i[0].union(j[0]);
                        if len(initset) == k:
                            if isfrequentThing(initset) == 1:
                                count = 0  # 统计出现次数
                                g = 0
                                for g in range(0,len(data)):
                                    if initset.issubset(data[g]) == 1:
                                        count = count + 1
                                tmp = 0
                                for kt in range(0,len(initlist)):
                                    if initlist[kt][0] == initset:
                                        tmp = 1
                                if tmp == 0:
                                    if count >= support:
                                        initlist = initlist + [[initset, count]]
    listq = listq + initlist  #不单独列出频繁 1 2 3...,直接组合时判断是否为k项集,跑得慢但是省空间且好写
    return listq

def showResult():
    for i in range(0, len(frequent_thing)):
        if len(frequent_thing[i][0]) >= 2:
            print(frequent_thing[i][0])

if __name__ == '__main__':
    frequent_1()
    frequent_thing = fuction(frequent_thing, level)
    frequent_thing = fuction(frequent_thing, level + 1)
    frequent_thing = fuction(frequent_thing, level + 2)
    showResult()

处理结果:

即得到的满足最低要求的频繁项集,可以认为同一项集内的蔬菜价格具有列出的相性。

猜你喜欢

转载自blog.csdn.net/weixin_42765557/article/details/114523570