本文代码均已在 MATLAB R2019b 测试通过,如有错误,欢迎指正。

@

(一)前言

(由于时间紧迫,就把本来打算写的决策树给鸽了)

我选择的内容是关联规则挖掘,这个之前在实验六是做过的,但是当时我并没有写数据预处理的部分,而是直接用的已经整理好的整型数据,同时有一个缺点是数据量不够大。

所以在这次综合实验部分,我继续改进Apriori算法的代码,并且找到了一个数据量大一些的数据集,在MATLAB R2019b版本上通过测试。总共写了200多行代码,全部都是我自己写的,不保证代码没有bug。

本文所有代码和原数据集可以在此下载:https://download.csdn.net/download/ljw_study_in_CSDN/13189443

(二)问题描述

以下给出了某超市的交易记录,以字符串形式给出商品名。
要求利用Apriori算法,从该交易记录中发掘关联规则。
要求自己编写好数据预处理的过程,将字符串类型的数据转换为matlab中的数字类型的数据,并且忽略原数据集中的缺失值。

经过查阅资料:Matlab 中实用数据结构之 containers.Map,我发现可以用matlab中的数据结构containers.Map来实现字符串->数字的映射关系。

原数据集(test_support_confidence.xlsx)部分数据,格式如下:
在这里插入图片描述

(三)MATLAB实现数据预处理和关联规则挖掘

(1)数据预处理,编写函数preprocess.m

function [X,map,reverseMap]=preprocess(data)
%% 传参:原数据集data
%% 返回:原数据集的每行转化为数字表示的向量X
%%      属性的映射关系存于map(将原来字符串型的属性映射到数字型),反序映射关系存于reverseMap(数字->字符)

[n,m]=size(data);
keyLength=0;
for i=1:n
    for j=1:m
        if ismissing(data{i,j}) % 这一行没有值了,忽略缺失值
            break;
        end
        if keyLength==0||~ismember(data{i,j},keySet) % 第一次出现
            keyLength=keyLength+1;
            keySet{keyLength}=data{i,j};
        end
    end
end
valueSet=1:keyLength;
map=containers.Map(keySet,valueSet); % 建立属性的映射关系
reverseMap=containers.Map(valueSet,keySet); % 建立属性的反序映射关系

for i=1:n
    pos=m; % 结束位置
    for j=1:m
        if ismissing(data{i,j}) % 这一行没有值了,忽略缺失值
            pos=j-1;
            break;
        end
        tmp(i,j)=map(data{i,j});
    end
    X{i}=tmp(i,1:pos);
end

end

(2)关联规则挖掘,编写函数apriori.m

function [C,C_sup,L,k,rule]=apriori(X,map,min_sup,min_con)
%% 传参:预处理后的数据集X,属性的映射关系map,最小支持度min_sup,最小置信度min_con
%% 返回:候选集C,候选集支持度计数C_sup,频繁集L,频繁集迭代的次数k(第k次结束),强关联规则rule

n=length(X); % 数据集行数
min_supcount=min_sup*n; % 最小支持度数
k=0;
while 1
    k=k+1;
    L{k}={};
    %% 生成候选集C{k}
    if k==1
        C{k}=(1:map.Count)'; % map.Count表示映射关系的数目,也就是属性的个数
    else
        [nL,mL]=size(L{k-1});
        cnt=0;
        for i=1:nL
            for j=i+1:nL
                tmp=union(L{k-1}(i,:),L{k-1}(j,:)); % 两集合并集
                if length(tmp)==k
                    cnt=cnt+1;
                    C{k}(cnt,1:k)=tmp;
                end
            end
        end
        C{k}=unique(C{k},'rows'); % 去掉重复的行
    end
    %% 求候选集的支持度C_sup{k}
    [nC,mC]=size(C{k}); % 候选集大小
    for i=1:nC
        cnt=0;
        for j=1:n
            if all(ismember(C{k}(i,:),X{j}),2)==1 % all函数判断向量是否全为1,参数2表示按行判断
                cnt=cnt+1;
            end
        end
        C_sup{k}(i,1)=cnt; % 每行存候选集对应的支持度
    end
    %% 求频繁项集L{k}
    L{k}=C{k}(C_sup{k}>=min_supcount,:);
    if isempty(L{k}) % 这次没有找出频繁项集
        break;
    end
    if size(L{k},1)==1 % 频繁项集行数为1,下一次无法生成候选集,直接结束
        k=k+1;
        C{k}={};
        L{k}={};
        break
    end
end

if k==1
    rule={};
    return;
end

[nL,mL]=size(L{k-1});
rule_count=0;
for p=1:nL % 第p个频繁集
    L_last=L{k-1}(p,:); % 之后将L_last分成左右两个部分,表示规则的前件和后件
    %% 求ab一起出现的次数cnt_ab
    cnt_ab=0;
    for i=1:n
        if all(ismember(L_last,X{i}),2)==1 % all函数判断向量是否全为1,参数2表示按行判断
            cnt_ab=cnt_ab+1;
        end
    end
    len=floor(length(L_last)/2);
    for i=1:len
        s=nchoosek(L_last,i); % 选i个数的所有组合
        [ns,ms]=size(s);
        for j=1:ns
            a=s(j,:);
            b=setdiff(L_last,a);
            [na,ma]=size(a);
            [nb,mb]=size(b);
            %% 关联规则a->b
            cnt_a=0;
            for i=1:na
                for j=1:n
                    if all(ismember(a,X{j}),2)==1 % all函数判断向量是否全为1,参数2表示按行判断
                        cnt_a=cnt_a+1;
                    end
                end
            end
            pab=cnt_ab/cnt_a;
            if pab>=min_con % 关联规则a->b的置信度大于等于最小置信度,是强关联规则
                rule_count=rule_count+1;
                rule(rule_count,1:ma)=double(a);
                rule(rule_count,ma+1:ma+mb)=double(b);
                rule(rule_count,ma+mb+1)=double(ma); % 倒数第二列记录分割位置(分成规则的前件、后件)
                rule(rule_count,ma+mb+2)=double(pab); % 倒数第一列记录置信度
            end
            %% 关联规则b->a
            cnt_b=0;
            for i=1:na
                for j=1:n
                    if all(ismember(b,X{j}),2)==1 % all函数判断向量是否全为1,参数2表示按行判断
                        cnt_b=cnt_b+1;
                    end
                end
            end
            pba=cnt_ab/cnt_b;
            if pba>=min_con % 关联规则b->a的置信度大于等于最小置信度,是强关联规则
                rule_count=rule_count+1;
                rule(rule_count,1:mb)=double(b);
                rule(rule_count,mb+1:mb+ma)=double(a);
                rule(rule_count,mb+ma+1)=double(mb); % 倒数第二列记录分割位置(分成规则的前件、后件)
                rule(rule_count,mb+ma+2)=double(pba); % 倒数第一列记录置信度
            end
        end
    end
end
if rule_count==0
    rule={};
end

end

(3)主函数main.m,调用数据预处理函数和关联规则挖掘函数,并且输出最后的结果。

clear;clc;

%% 装入数据集 这里写的是你自己放的文件的路径!
data = readcell("C:\Users\LJW\Desktop\test_support_confidence.xlsx");

%% 调用自己编写的preprocess函数,进行数据预处理
[X,map,reverseMap]=preprocess(data);

%% 输入参数
min_sup=input("请输入最小支持度,(0到1的小数,不包括0和1,示例:0.5)\n"); % 最小支持度(已除以n)
min_con=input("请输入最小置信度,(0到1的小数,不包括0和1,示例:0.5)\n"); % 最小置信度(已除以n)

%% 调用自己编写的apriori函数,求出候选集C、频繁集L、强关联规则rule
[C,C_sup,L,k,rule]=apriori(X,map,min_sup,min_con);

%% 输出候选集和频繁集
fprintf("\n");
for i=1:k
    fprintf("第%d轮的候选集为:",i); C{i}
    fprintf("第%d轮的频繁集为:",i); L{i}
end

if k==1 % 第1轮就结束了
    fprintf("当前设置的支持度阈值过大,无法生成强关联规则!\n");
else
    fprintf("程序在第%d轮结束迭代,最大频繁项集为:",k); L{k-1}
end 

%% 输出强关联规则
[nr,mr]=size(rule);
if(nr==0)
    fprintf("当前设置的置信度阈值过大,无法生成强关联规则!\n");
else 
    fprintf("当最小支持度为%.2f,最小置信度为%.2f时,生成的强关联规则:\n\n",min_sup,min_con);
    fprintf("强关联规则\t\t\t\t置信度\n");
    for i=1:nr
        pos=rule(i,mr-1); % 断开位置pos,1:pos为规则前件,pos+1:mr-2为规则后件
        for j=1:pos
            if j==pos
                fprintf("%s",reverseMap(rule(i,j)));
            else 
                fprintf("%s∧",reverseMap(rule(i,j)));
            end
        end
        fprintf(" => ");
        for j=pos+1:mr-2
            if j==mr-2
                fprintf("%s",reverseMap(rule(i,j)));
            else 
                fprintf("%s∧",reverseMap(rule(i,j)));
            end
        end
        fprintf("\t\t%f\n",rule(i,mr));
    end
end

输入最小支持度为0.2,最小置信度为0.2时,运行结果:

请输入最小支持度,(0到1的小数,不包括0和1,示例:0.5)
0.2
请输入最小置信度,(0到1的小数,不包括0和1,示例:0.5)
0.2

第1轮的候选集为:
ans =

  7×1 uint64 列向量

   1
   2
   3
   4
   5
   6
   7

第1轮的频繁集为:
ans =

  7×1 uint64 列向量

   1
   2
   3
   4
   5
   6
   7

第2轮的候选集为:
ans =

  21×2 uint64 矩阵

   1   2
   1   3
   1   4
   1   5
   1   6
   1   7
   2   3
   2   4
   2   5
   2   6
   2   7
   3   4
   3   5
   3   6
   3   7
   4   5
   4   6
   4   7
   5   6
   5   7
   6   7

第2轮的频繁集为:
ans =

  21×2 uint64 矩阵

   1   2
   1   3
   1   4
   1   5
   1   6
   1   7
   2   3
   2   4
   2   5
   2   6
   2   7
   3   4
   3   5
   3   6
   3   7
   4   5
   4   6
   4   7
   5   6
   5   7
   6   7

第3轮的候选集为:
ans =

  35×3 uint64 矩阵

   1   2   3
   1   2   4
   1   2   5
   1   2   6
   1   2   7
   1   3   4
   1   3   5
   1   3   6
   1   3   7
   1   4   5
   1   4   6
   1   4   7
   1   5   6
   1   5   7
   1   6   7
   2   3   4
   2   3   5
   2   3   6
   2   3   7
   2   4   5
   2   4   6
   2   4   7
   2   5   6
   2   5   7
   2   6   7
   3   4   5
   3   4   6
   3   4   7
   3   5   6
   3   5   7
   3   6   7
   4   5   6
   4   5   7
   4   6   7
   5   6   7

第3轮的频繁集为:
ans =

  2×3 uint64 矩阵

   1   3   4
   1   3   7

第4轮的候选集为:
ans =

  1×4 uint64 行向量

   1   3   4   7

第4轮的频繁集为:
ans =

  空的 0×4 uint64 矩阵

程序在第4轮结束迭代,最大频繁项集为:
ans =

  2×3 uint64 矩阵

   1   3   4
   1   3   7

当最小支持度为0.20,最小置信度为0.20时,生成的强关联规则:

强关联规则               置信度
啤酒 => 奶酪∧薯片     0.369369
奶酪∧薯片 => 啤酒     0.650794
奶酪 => 啤酒∧薯片     0.414141
啤酒∧薯片 => 奶酪     0.672131
薯片 => 啤酒∧奶酪     0.394231
啤酒∧奶酪 => 薯片     0.661290
啤酒 => 奶酪∧香蕉     0.378378
奶酪∧香蕉 => 啤酒     0.724138
奶酪 => 啤酒∧香蕉     0.424242
啤酒∧香蕉 => 奶酪     0.656250
香蕉 => 啤酒∧奶酪     0.411765
啤酒∧奶酪 => 香蕉     0.677419

输入最小支持度为0.9,最小置信度为0.9时,运行结果:

请输入最小支持度,(0到1的小数,不包括0和1,示例:0.5)
0.9
请输入最小置信度,(0到1的小数,不包括0和1,示例:0.5)
0.9

第1轮的候选集为:
ans =

  7×1 uint64 列向量

   1
   2
   3
   4
   5
   6
   7

第1轮的频繁集为:
ans =

  空的 0×1 uint64 列向量

当前设置的支持度阈值过大,无法生成强关联规则!
当前设置的置信度阈值过大,无法生成强关联规则!