数据挖掘——几个算法的python实现

这个学期开了“数据挖掘”课程,学习了几个相关的算法,本着记录和验证一下学习成果的目的写了这篇文章~

所谓是自己懂不算懂,能讲清楚让别人也懂才算懂~

本文一共有四个算法的基本概念和实现,分别是KNN、ID3、C4.5、Apriori


目录

1. KNN算法

1.1 算法介绍

1.2 算法实现步骤

1.3 算法的关键

1.4 Python具体实现(电影分类)

2. ID3算法

2.1 算法介绍

2.2 算法实现步骤

2.3 Python具体实现(打垒球活动)

3. C4.5算法

3.1 算法介绍

3.2 算法实现步骤

3.3 Python具体实现

4. Apriori算法

4.1 算法介绍

4.2 算法实现步骤

4.3 Python具体实现


1. KNN算法

1.1 算法介绍

KNN是一种监督学习算法,算法思想是通过计算新数据与训练数据特征值之间的距离,然后选取K个距离最近的邻居进行分类判别。若K=1,新数据会被简单分配给其近邻的类。

 

1.2 算法实现步骤

① 选择一种距离计算方法,计算新数据的某个特征值与已知类别数据集中的数据点的相对应特征值的距离;

② 按照距离大小进行升序排序,选取与新数据距离最小的K个点;

③ 对于离散分类,返回K个点中出现频率最多的类作为预测分类。

 

1.3 算法的关键

① 数据的所有特征值都要是可比较的量化值。

② 样本特征要做归一化处理。

③ 需要一个距离函数以计算两个样本之间的距离。

④ 确定K的值:K太大引起欠拟合、太小容易过拟合。交叉验证以确定K的值。

 

1.4 Python具体实现(电影分类)

完整代码和对代码的解释如下:

import numpy as np
import operator


# KNN算法分类爱情片和动作片
# 通过打斗镜头次数和接吻镜头次数区分


# 模拟数据 打斗次数 接吻次数
def init_data():
    data_X = np.array([[1,100],[2,96],[3,93],[6,90],[80,3],[93,2],[86,5]])
    data_Y = ['爱情片','爱情片','爱情片','爱情片','动作片','动作片','动作片']
    return data_X,data_Y


# data: 测试数据
# testData: 数据集合
# output:测试数据输出
# k:取最接近数据的前几个
def kNN(data, testData, output, k):
    # 获取测试数据数量
    dataInputRow = testData.shape[0]
    # np.tile 数组沿各个方向复制
    # 这里将输入数据copy和测试数据数量保持一致,用来计算和测试数据的欧式距离
    # 数组排序效果如下所示,设所要分类的为[2,93],训练数据为[3,93],[6,90],[80,3],[93,2]
    # [2,93][3,93]
    # [2,93][6,90]
    # [2,93][80,3]
    # [2,93][93,2]
    #计算与训练数据之间的欧氏距离
    reduceData = np.tile(data, (dataInputRow,1)) - testData
    squareData = reduceData ** 2
    squareDataSum = squareData.sum(axis = 1)
    distance = squareDataSum ** .5
    # argsort()为排序函数,得到的数组为排序完成之后的索引数组
    # 如要排序的数组为[-2,3,1]
    # argsort()排序之后得到的索引数组为[0,2,1]
    sortDistance = distance.argsort()
    dataCount = {}
    # 统计排名靠前k数据的爱情片和动作片次数,取次数最高的做为输出
    for i in range(k):
        output_ = output[sortDistance[i]]
        dataCount[output_] = dataCount.get(output_,0) + 1
        # operator.itemgetteer(1)按值从小到大排序
        sortDataCount = sorted(dataCount.items(), key = operator.itemgetter(1), reverse = True)
    return sortDataCount[0][0]


if __name__ == '__main__':
    data_X,data_Y = init_data()
    print(kNN([2,93], data_X, data_Y, 3))

#By MIC_H

参考博客见:https://blog.csdn.net/u010479989/article/details/80606055


2. ID3算法

2.1 算法介绍

◆ ID3算法的结果是生成一个决策树,决策树中的每一个非叶结点对应着一个非类别属性,树枝代表这个属性的值。一个叶结点代表从树根到叶结点之间的路径对应的记录所属的类别属性值。

◆ 每一个非叶结点都将与属性中具有最大信息量的非类别属性相关联。

◆ 采用信息增益来选择出能够最好地将样本分类的属性。

下面的一些解释会用到这个表格,各属性值的不同会如何影响活动的进行与否作为例子

天气 

温度 

湿度 

风速 

活动 

炎热

取消

炎热

取消

炎热

进行

适中

进行

寒冷

正常

进行

寒冷

正常

取消

寒冷

正常

进行

适中

取消

寒冷

正常

进行

适中

正常

进行

适中

正常

进行

适中

进行

炎热

正常

进行

适中

取消

相关名词

① 熵

通常熵表示事物的混乱程度,熵越大表示混乱程度越大,越小表示混乱程度越小。对于随机事件S,如果我们知道它有N种取值情况,每种情况发生的概论为,那么这件事的熵就定义为:

熵是对事件对应的属性的不确定性的度量。一个属性的熵越大,它蕴含的不确定信息越大,越有利于数据的分类。

例如对于打垒球的例子,要求活动的熵H(活动)。在活动一栏属性中发现活动的取值有两种:取消(5个)和进行(9个),它们所占的比例分别为5/14,9/14。那么H(活动)的取值为:,算出的结果约为0.94。

② 信息增益

信息增益就是两个熵的差,当差值越大说明按照此划分对于事件的混乱程度减少越有帮助。

用上面的例子,则有

在天气为晴时有5种情况,发现活动取消有3种,进行有2种,计算现在的条件熵

=0.971

同理天气为阴时有4种情况,活动进行的有4种,则条件熵为:

=0

同理天气为雨时有5种情况,活动取消的有2种,进行的有3种,则条件熵为:

=0.971

由于按照天气属性不同取值划分时,天气为晴占整个情况的5/14,天气为阴占整个情况的4/14,天气为雨占整个情况的5/14,

则按照天气属性不同取值划分时的带权平均值熵为:算出的结果约为0.693.

则此时的信息增益:

Gain(活动,天气)= H(活动) - H(活动|天气) = 0.94- 0.693 = 0.246

同理我们可以计算出按照温度属性不同取值划分后的信息增益:

Gain(活动,温度)= H(活动) - H(活动|温度) = 0.94- 0.911 = 0.029

按照湿度属性不同取值划分后的信息增益:

Gain(活动,湿度)= H(活动) - H(活动|湿度) = 0.94- 0.789 = 0.151

按照风速属性不同取值划分后的信息增益:

Gain(活动,风速)= H(活动) - H(活动|风速) = 0.94- 0.892 = 0.048

 

2.2 算法实现步骤

① 计算各个属性的熵;

② 计算信息增益;

③ 根据信息增益来构建决策树。

用上面这个例子来走一下这个算法:

决策树的构造就是要选择当前信息增益最大的属性作为当前决策树的节点,对于本例子来说,根据上面的计算结果,也就是天气这个属性,因此我们用天气属性作为决策树的根节点。

天气属性有3中取值:晴、阴、雨。

当天气为阴时,活动全为进行,因此这个节点的结果是确定的;而当天气为晴或雨时,活动中有进行的也有取消的,结果是不确定的,所以还要用剩余的其他属性来划分,也就是再次计算熵和信息增益,选择最大信息增益的属性来作为下一个节点,直到结果是唯一的为止。可见,这是一个递归建树的过程。

最后的树就是下面这样的:

  

2.3 Python具体实现(打垒球活动)

完整代码和对代码的解释如下:

from math import log
from operator import *


def storeTree(inputTree, filename):
    import pickle
    fw = open(filename, 'wb')  # pickle默认方式是二进制,需要制定'wb'
    pickle.dump(inputTree, fw)
    fw.close()


def grabTree(filename):
    import pickle
    fr = open(filename, 'rb')  # 需要制定'rb',以byte形式读取
    return pickle.load(fr)


def createDataSet():
    '''
    dataSet=[[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]
    labels = ['no surfacing','flippers']
    '''
    dataSet = [['sunny', 'hot', 'high', 'weak', 'no'],
               ['sunny', 'hot', 'high', 'strong', 'no'],
               ['overcast', 'hot', 'high', 'weak', 'yes'],
               ['rain', 'mild', 'high', 'weak', 'yes'],
               ['rain', 'cool', 'normal', 'weak', 'yes'],
               ['rain', 'cool', 'normal', 'strong', 'no'],
               ['overcast', 'cool', 'normal', 'strong', 'yes'],
               ['sunny', 'mild', 'high', 'weak', 'no'],
               ['sunny', 'cool', 'normal', 'weak', 'yes'],
               ['rain', 'mild', 'normal', 'weak', 'yes'],
               ['sunny', 'mild', 'normal', 'strong', 'yes'],
               ['overcast', 'mild', 'high', 'strong', 'yes'],
               ['overcast', 'hot', 'normal', 'weak', 'yes'],
               ['rain', 'mild', 'high', 'strong', 'no']]
    labels = ['outlook', 'temperature', 'humidity', 'wind']
    return dataSet, labels


def calcShannonEnt(dataSet):  # 计算香农熵
    numEntries = len(dataSet)

    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1]  # 取得最后一列数据,该属性取值情况有多少个
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1 #计算yes的次数和no的次数

    # 计算熵
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key]) / numEntries
        shannonEnt -= prob * log(prob, 2)

    return shannonEnt


# 定义按照某个特征进行划分的函数splitDataSet
# 输入三个变量(待划分的数据集,特征,分类值)
# axis特征值中0代表no surfacing,1代表flippers
# value分类值中0代表否,1代表是
def splitDataSet(dataSet, axis, value):
    retDataSet = []
    for featVec in dataSet:  # 取大列表中的每个小列表
        if featVec[axis] == value:
            reduceFeatVec = featVec[:axis]   #去掉axis特征
            reduceFeatVec.extend(featVec[axis + 1:])
            retDataSet.append(reduceFeatVec)

    return retDataSet  # 返回不含划分特征的子集


def chooseBestFeatureToSplit(dataSet):
    numFeature = len(dataSet[0]) - 1
    baseEntropy = calcShannonEnt(dataSet)
    bestInforGain = 0
    bestFeature = -1

    for i in range(numFeature):
        featList = [number[i] for number in dataSet]  # 得到某个特征下所有值(某列)
        uniquelVals = set(featList)  # set无重复的属性特征值,得到所有无重复的属性取值

        # 计算每个属性i的概论熵
        newEntropy = 0
        for value in uniquelVals:
            subDataSet = splitDataSet(dataSet, i, value)  # 得到i属性下取i属性为value时的集合
            prob = len(subDataSet) / float(len(dataSet))  # 每个属性取值为value时所占比重
            newEntropy += prob * calcShannonEnt(subDataSet)
        inforGain = baseEntropy - newEntropy  # 当前属性i的信息增益

        if inforGain > bestInforGain:
            bestInforGain = inforGain
            bestFeature = i

    return bestFeature  # 返回最大信息增益属性下标


# 递归创建树,用于找出出现次数最多的分类名称
def majorityCnt(classList):
    classCount = {}
    for vote in classList:  # 统计当前划分下每中情况的个数
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.items, key=operator.itemgetter(1), reversed=True)  # reversed=True表示由大到小排序
    # 对字典里的元素按照value值由大到小排序

    return sortedClassCount[0][0]


def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]  # 创建数组存放所有标签值,取dataSet里最后一列(结果)
    # 类别相同,停止划分

    # 判断classList里是否全是一类,如果是一类,这两个值一定是相同的
    # count() 方法用于统计某个元素在列表中出现的次数
    if classList.count(classList[-1]) == len(classList):
        return classList[-1]  # 当全是一类时停止分割
    # 长度为1,返回出现次数最多的类别
    if len(classList[0]) == 1:  # 当没有更多特征时停止分割,即分到最后一个特征也没有把数据完全分开,就返回多数的那个结果
        return majorityCnt(classList)
    # 按照信息增益最高选取分类特征属性
    bestFeat = chooseBestFeatureToSplit(dataSet)  # 返回分类的特征序号,按照最大熵原则进行分类
    bestFeatLable = labels[bestFeat]  # 该特征的label, #存储分类特征的标签

    myTree = {bestFeatLable: {}}  # 构建树的字典
    del (labels[bestFeat])  # 从labels的list中删除该label

    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLables = labels[:]  # 子集合 ,将labels赋给sublabels,此时的labels已经删掉了用于分类的特征的标签
        # 构建数据的子集合,并进行递归
        myTree[bestFeatLable][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLables)
    return myTree


if __name__ == "__main__":
    my_Data, labels = createDataSet()

    # print(calcShannonEnt(my_Data))
    Mytree = createTree(my_Data, labels)
    print(Mytree)

#By MIC_H

参考博客见:https://www.cnblogs.com/wang2825/articles/8728913.html


3. C4.5算法

3.1 算法介绍

C4.5是在ID3上发展而来的。从建树的依据这个角度来看,ID3是用信息增益来选择特征建树,而C4.5则是用信息增益比来选择特征进行建树。

信息增益比的定义如下:

 

其中     

成为属性a的固有值。

由表达式可知,Gain(D,a)所表示的仍然是信息增益,与ID3算法中的Gain(D,a)并无差别,但重点在于IV(a)这一项:如果属性a的可能取值数目越多(即V越大),则IV(a)的值通常会越大,那么最终的Gain_ratio的值会相应减小,以此来解决ID3算法的不足。

 

3.2 算法实现步骤

①计算各个属性的熵;

② 计算信息增益;

③ 计算信息增益比;

④ 根据信息增益来构建决策树。

 

3.3 Python具体实现

(这个算法中的yes和no我也找不着是什么意思了,根据前面两个属性,我们就当成是是否买基金吧!)

完整代码和对代码的解释如下:

from math import log
import operator


def createDataSet():
    dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'yes'],
               [0, 1, 'no'],
               [0, 1, 'no'],
               [0, 0, 'no'],
               [0, 0, 'yes'],
               [0, 0, 'no']]
    labels = ['是否有房产', '是否有车']
    return dataSet, labels

#计算香农熵
def calcShannonEnt(dataSet):
    num = len(dataSet)    # 总共有多少行数据   8
    shannonEnt = 0.0  #初始化信息熵
    labelCounts = {}
    for item in dataSet:   # 遍历整个数据集,每次取一行
        label = item[-1]  #取该行最后一列的值  即标签
        if label not in labelCounts:  # 在字典中这个label是否存在
            labelCounts[label] = 0
        labelCounts[label] += 1    #{ 'yes':2,'no':3 }
    for key in labelCounts:
        p = float(labelCounts[key]/num)    # 即每个标签所占的比重
        #print( p )
        shannonEnt -= p * log(p,2)  #log base 2 计算信息熵
    return shannonEnt     # 返回根节点信息熵
    # labelCounts  {'yes': ..., 'no': ...}

def splitDataSet( dataSet,axis,value ):
    result = []
    for item in dataSet:     #  item:  [1,1,'yes']
        if item[axis] == value:
            r = item[:axis] + item[axis+1:]  # []+[1,'yes'] => r=> [1,'yes']
            # r = item[-1:]  #TODO: 只作熵运算的话,只用保留最后一列的值即可
            result.append(r)
    return result

# 选取当前数据集下,用于划分数据集的最优特征
def chooseBestFeatures(dataSet):
    # 1. 特征总数  2
    numFeatures = len(dataSet[0]) -1  # 获取当前数据集的特征个数,最后一列是分类标签    2
    # 2. 计算信息熵   1
    ent = calcShannonEnt(dataSet) # 计算当前数据集的信息熵    (根节点信息熵)
    # 3. 最佳信息熵
    bestGain = 0.0;
    # 4. 最佳特征号 (0,1)
    bestFeatureID = -1 # 初始化最优信息增益和最优的特征
    # 5.最佳信息增益率
    infogain = 0.0;
    # 循环特征(列)
    for i in range(numFeatures):
        list1 = [line[i] for line in dataSet] # 获取数据集中当前特征下的所有值
        uniqueValues = set(list1)  # 每一列去重后的值, set可以去重 set(list) 将list列表强制转换为set  {0,1}
        #print( uniqueValues )
        newEnt = 0.0   # 条件熵
        s = 0.0
        for value in uniqueValues:
            # 调用splitDataSet ( 列,set中的值 ) -> 得到子集
            subDataSet = splitDataSet(dataSet,i,value)      # (dataSet,0,0) (dataSet,0,1) (dataSet,1,0) (dataSet,1,1)
            # 计算概率
            prob = len(subDataSet)/float(len(dataSet))      #       5/8          3/8           4/8           4/8
            # 计算熵
            newEnt += prob * calcShannonEnt(subDataSet)
            s -= prob * log(prob, 2);
        print( '信息熵为:' + str(ent) )
        print( '第' + str(i) + '列的条件熵为:' + str(newEnt) )
        gain = ent - newEnt
        print( '第' + str(i) + '列的信息增益为:' + str(gain) )
        infogain = gain/s   # 求该列的信息增益比
        print( '第' + str(i) + '列的信息增益比为:' + str(infogain) )
        if (infogain > bestGain):
            # 比较每个特征的信息增益比,只要最好的信息增益比
            bestGain = infogain
            bestFeatureID = i
    return bestFeatureID

def classNum( classList ):
    '''
    classList: 分类的名称
    '''
    classCount = {}    # 存各种分类出现的频率 : {'yes',1,'no',2}
    for label in classList:
        classCount[label] = classCount.get(label,0) + 1
    # 对字典进行排序
    sortedClassCount = sorted( classCount.items(),key=operator.itemgetter(1),reverse=True )
    print( sortedClassCount )
    return sortedClassCount[0][0]


# 生成决策树的总方法
def createTree(dataSet, labels, depth=0, max_features=None, max_depth=None):
    # 求出 dataSet 中的样本所属的类别,即递归停止的条件一
    # 返回当前数据集下标签所有的值
    classList = [example[-1] for example in dataSet]  # ['yes','yes','yes','no','no','no','yes','no']
    # 终止条件1: 可以加上判断 这个classList是否纯净
    if classList.count(classList[0]) == len(classList):
        # 纯净的意思就是此数据中所有特征都相等
        # 当整个dataSet中的类别完全相同时类别已经纯净了,则停止继续划分,直接返回该类标签
        return classList[0]
        # 终止条件2: 列中的取值种类   <=max_features 时.   max_features 即划分时考虑的最大特征数  默认为 None
    if max_features == None:
        max_features = 1
    if len(dataSet[0]) <= max_features:
        return classNum(classList)  # 返回数量多的那一个 标签

    #  max_depth  树的最大深度,也就是说当树的深度到达max_depth的时候无论还有多少可以分支的特征,决策树都会停止运算

    if max_depth != None:
        if depth >= max_depth:
            return classNum(classList)
        depth = depth + 1

    # 获取最好的分类特征索引
    # dataSet = [1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'yes'], [0, 1, 'no'], [0, 1, 'no'], [0, 0, 'no'], [0, 0, 'yes'], [0, 0, 'no']
    bestFeat = chooseBestFeatures(dataSet)  # bestFeat: 0      bestFeat: 0
    # print( 'bestFeat:',bestFeat )

    # 获取该特征的名字
    # labels = ['是否有房产', '是否有车']
    bestFeatLabel = labels[bestFeat]
    # print( 'bestFeatLabel:',bestFeatLabel )   # bestFeatLabel: 是否有房产    bestFeatLabel: 是否有车

    # 这里直接使用字典变量来存储树信息,这对于回执树形图很重要
    myTree = {bestFeatLabel: {}}  # 当前数据集选取最好的特征存储在bestFeat中
    del (labels[bestFeat])  # 删除已经在选取的特征  此时  labels = ['是否有车']

    # 取出最优列的值
    featValues = [example[bestFeat] for example in dataSet]
    # featValues: [1, 1, 1, 0, 0, 0, 0, 0]      featValues: [1, 1, 0, 0, 0]
    # print( 'featValues:',featValues )
    # 去重
    uniqueVals = set(featValues)  # [1,0]
    # 根据这个列的这个 uniqueVals值来切分树的节点
    for value in uniqueVals:
        #  myTree  -> 房产 -> 1
        #             房产 -> 0
        subLabels = labels[:]
        # print( 'subLabels:',subLabels )
        temp = splitDataSet(dataSet, bestFeat, value)
        # print( 'temp:',temp )
        myTree[bestFeatLabel][value] = createTree(temp, subLabels, depth=depth, max_features=max_features,
                                                  max_depth=max_depth)
    return myTree


if __name__ == '__main__':
    dataSet, labels = createDataSet()
    print(createTree(dataSet, labels))

#By MIC_H

参考博客见:https://blog.csdn.net/LA401088242/article/details/89160796


4. Apriori算法

4.1 算法介绍

Apriori算法是第一个关联规则挖掘算法,也是最经典的算法。

它利用逐层搜索的迭代方法找出数据库中项集的关系,以形成规则,其过程由连接(类矩阵运算)与剪枝(去掉那些没必要的中间结果)组成。该算法中项集的概念即为项的集合。包含K个项的集合为k项集。项集出现的频率是包含项集的事务数,称为项集的频率。如果某项集满足最小支持度,则称它为频繁项集。

 

4.2 算法实现步骤

① 找出所有的频繁项集,这些项集出现的支持度要大于等于预定义的最小支持度;

② 然后由频集产生强关联规则,这些规则必须满足最小支持度和最小可信度;

③ 使用第1步找到的频集产生期望的规则,产生只包含集合的项的所有规则,其中每一条规则的右部只有一项。

 

4.3 Python具体实现

完整代码和对代码的解释如下:

def load_data_set():
    data_set = [['e1', 'e2', 'e5'],
                ['e2', 'e4'],
                ['e2', 'e3'],
                ['e1', 'e2', 'e4'],
                ['e1', 'e3'],
                ['e2', 'e3'],
                ['e1', 'e3'],
                ['e1', 'e2', 'e3', 'e5'],
                ['e1', 'e2', 'e3']]
    return data_set

# 转成Set,为生成频繁项目集时扫描数据库时以提供issubset()功能
def Create_C1(data_set):
    '''
    参数:数据库事务集
    '''
    C1 = set()
    for t in data_set:
        for item in t:
            item_set = frozenset([item])  # frozenset用于生成不可变集合
            # 为生成频繁项目集时扫描数据库时以提供issubset()功能
            C1.add(item_set)
    return C1

# 先验性
def is_apriori(Ck_item, Lk_sub_1):
    '''
    参数:候选频繁k项集,频繁k-1项集
    '''
    for item in Ck_item:
        sub_item = Ck_item - frozenset([item])
        if sub_item not in Lk_sub_1:
            return False
    return True

# 剪掉不满先验性质的候选K项集
def Create_Ck(Lk_sub_1, k):
    '''
    # 参数:频繁k-1项集,当前要生成的候选频繁几项集
    ****************
    存在先验性质:任一频繁项的所有非空子集也必须是频繁的,
    也就是说任何非频繁的(k-1)项集都不是频繁k项集的子集。
    因此,如果一个候选k项集Ck的(k-1)项子集不在Lk-1中,则该候选也不可能是频繁的,
    从而可以从Ck中删除,获得压缩后的Ck。
    下文代码中的is_apriori函数用于判断是否满足先验性质,
    create_Ck函数中包含剪枝步骤,即若不满足先验性质,剪枝
    '''
    Ck = set()
    len_Lk_sub_1 = len(Lk_sub_1)
    list_Lk_sub_1 = list(Lk_sub_1)
    for i in range(len_Lk_sub_1):  # i: [0, len_Lk_sub_1)
        for j in range(i + 1, len_Lk_sub_1):  # j: [i+1, len_Lk_sub_1)
            l1 = list(list_Lk_sub_1[i])
            l2 = list(list_Lk_sub_1[j])
            l1.sort()
            l2.sort()
            # 判断l1的前k-1-1个元素与l2的前k-1-1个元素对应位是否全部相同
            # list[s:t]:截取s到t范围的元素生成一个新list
            if l1[0:k - 2] == l2[0:k - 2]:
                Ck_item = list_Lk_sub_1[i] | list_Lk_sub_1[j]  # 合并
                if is_apriori(Ck_item, Lk_sub_1):
                    Ck.add(Ck_item)
    return Ck


def Generate_Lk_By_Ck(data_set, Ck, min_support, support_data):
    '''
    参数:数据库事务集,候选频繁k项集,最小支持度,项目集-支持度dic
    基于压缩后的Ck,扫描所有事务,对Ck中的每个项进行计数,
    然后删除不满足最小支持度的项,从而获得频繁k项集
    '''
    Lk = set()
    # 通过dic记录候选频繁k项集的事务支持个数
    item_count = {}
    for t in data_set:
        for Ck_item in Ck:
            if Ck_item.issubset(t):
                if Ck_item not in item_count:
                    item_count[Ck_item] = 1
                else:
                    item_count[Ck_item] += 1
    data_num = float(len(data_set))
    for item in item_count:
        if (item_count[item] / data_num) >= min_support:
            Lk.add(item)
            support_data[item] = item_count[item] / data_num
    return Lk  # 筛选出并返回频繁k项集

# 生成所有的频繁项目集
def Generate_L(data_set, max_k, min_support):
    '''
    参数:数据库事务集,求的最高项目集为k项,最小支持度
    '''
    # 创建一个频繁项目集为key,其支持度为value的dic
    support_data = {}
    C1 = Create_C1(data_set)
    L1 = Generate_Lk_By_Ck(data_set, C1, min_support, support_data)
    Lk_sub_1 = L1.copy()  # 对L1进行浅copy
    L = []
    L.append(Lk_sub_1)  # 末尾添加指定元素
    for k in range(2, max_k + 1):
        Ck = Create_Ck(Lk_sub_1, k) #根据先验性剪掉一部分
        Lk = Generate_Lk_By_Ck(data_set, Ck, min_support, support_data) #得出频繁K项集
        Lk_sub_1 = Lk.copy()
        L.append(Lk_sub_1)
    return L, support_data

# 生成关联规则
def Generate_Rule(L, support_data, min_confidence):
    '''
    参数:所有的频繁项目集,项目集-支持度dic,最小置信度
    '''
    rule_list = []
    sub_set_list = []
    for i in range(len(L)):
        for frequent_set in L[i]:
            for sub_set in sub_set_list:
                if sub_set.issubset(frequent_set):
                    # 计算置信度
                    conf = support_data[frequent_set] / support_data[sub_set]
                    # 将rule声明为tuple
                    rule = (sub_set, frequent_set - sub_set, conf)
                    # 筛选置信度大于等于最小置信度的规则
                    if conf >= min_confidence and rule not in rule_list:
                        rule_list.append(rule)
            sub_set_list.append(frequent_set)
    return rule_list


if __name__ == "__main__":
    data_set = load_data_set()
    '''
    print("Test")
    # 数据库事务打印
    for t in data_set:
        print(t)
    '''
    '''
    print("Test")
    # 候选频繁1项集打印
    C1 = Create_C1(data_set)
    for item in C1:
        print(item)
    '''
    '''
    # 频繁1项集打印
    print("Test")
    L = Generate_L(data_set, 1, 0.2)
    for item in L:
        print(item)
    '''
    '''
    # 频繁k项集打印
    print("Test")
    L, support_data = Generate_L(data_set, 2, 0.2)
    for item in L:
        print(item)
    '''
    '''
    # 关联规则测试
    print("Test")
    L, support_data = Generate_L(data_set, 3, 0.2)
    rule_list = Generate_Rule(L, support_data, 0.7)
    for item in support_data:
        print(item, ": ", support_data[item])
    print("-----------------------")
    for item in rule_list:
        print(item[0], "=>", item[1], "'s conf:", item[2])
    '''

    L, support_data = Generate_L(data_set, 3, 0.2)
    rule_list = Generate_Rule(L, support_data, 0.7)
    for Lk in L:
        print("=" * 55)
        print("frequent " + str(len(list(Lk)[0])) + "-itemsets\t\tsupport")
        print("=" * 55)
        for frequent_set in Lk:
            print(frequent_set, support_data[frequent_set])
    print()
    print("Rules")
    for item in rule_list:
        print(item[0], "=>", item[1], "'s conf: ", item[2])

# By MIC_H

最后出来的结果如下图所示:

参考博客见:https://blog.csdn.net/yo_bc/article/details/80379399#commentBox

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章