【机器学习】【Apriori算法-2】Apriori算法的Python实现 + 代码讲解

1.Apriori算法原理详解

    请详见:Apriori算法原理详解+示例展示数学求解过程

2.Python实现Apriori算法

2.1算法的重要Python操作知识点

实现Apriori算法时,重要Python知识点:

1)如何实现二维list 转化为set

2)如何判断list A是list B的子集

    此处A和B是一维序列;另外A是B的有序子集,比如[1, 3]是[1,2,3]的有序子集,但不是[3, 2, 1]的有序子集

3)如何实现由[[1], [2], [3]]得到[[1, 2], [1, 3], [2, 3]]

4)如何实现由[[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]]得到[[1,2,3], [1,2,4], [1,3,4], [2,3,4]]

    这个是Apriori的重要操作。是根据旧的支持数据集得到新的支持数据集的关键操作。

5)算法的另外一个关键操作是,计算一个序列在序列列表里面的出现次数

注:下面2.2是精简的Python代码,2.3是展示数学求解过程的Python代码,如果是理解算法思路可以看2.3的代码,其他可以看2.2代码。

2.2精简的Python代码

# -*- coding: utf-8 -*-
"""
@author: Tom
Talk is cheap, show me the code
Aim:实现Apriori算法
"""

import numpy as np

class CApriori(object):
    '''
    实现Apriori算法
    '''
    def __init__(self, goods, minSupport):
        self.goods = goods           #交易商品列表
        #最小支持度,支持度过滤时支持度小于此值的频繁项会被过滤掉
        self.minSupport = minSupport 
        
        self.N = np.shape(goods)[0]  #交易次数
        self.goodsSet = set([])      #商品集合, 元素是单个商品
        self.max_len  = 0            #最长交易的商品总数
        #支持数据集,元素是[频繁项, 支持项],频繁项=商品序列list, 支持项=支持度*交易总次数
        self.supportData = []        
        
        self._init() #初始化
        self._work() #开始迭代运算直到找到支持数据集
        
    def _isSubset(self, A, B):
        '''判断序列a是否序列b的子集,且是有序子集,此处有序子集详见下面Note
        :param a, 一维序列
        :param b, 一维序列
        :return True:a是b的子集,False:a不是b的子集
        :Note [1, 3] 是 [1, 2, 3]的有序子集,[3, 1]不是[1, 2, 3]的有序子集
        '''
        A,B = list(A),list(B)
        if np.shape(A)[0] == 0:
            return False

        pre_ind = -1
        for e in A:
            if e not in B: #不是子集
                return False
            elif B.index(e) < pre_ind: #不满足有序
                return False
            pre_ind = B.index(e)

        return True

    def _support(self, item, goods):
        '''
        :param item, 频繁项
        :param goods, 交易商品列表
        :return 频繁项的支持度
        '''
        subset_cnt = [self._isSubset(item, e) for e in goods]
        cnt = subset_cnt.count(True)
        support = cnt * 1.0 / self.N
        return support
        
    def _init(self):
        '''初始化支持数据集和迭代计数器
        '''
        self.supportData = []
        #设置迭代计数器
        for item in self.goods:
            if np.shape(item)[0] > self.max_len:
                self.max_len = np.shape(item)[0]
        #交易商品数据,一维list
        goods_data = []
        for e in self.goods:
            goods_data.extend(e)

        #交易商品集合,set
        self.goodsSet = set(goods_data)
        
        #初始数据集(频繁项,支持项)
        for i in range(len(self.goodsSet)):
            e = list(self.goodsSet)[i] #频繁项,单个商品
            cnt = goods_data.count(e)  #支持项
            support = cnt *1.0 / self.N
            if (support >= self.minSupport):
                self.supportData.append([[e], cnt])
        return self.supportData, self.max_len
        
    def _uniq(self, supportData):
        '''去除支持数据集中的重复频繁项,重复频繁项的产生示例:
         [1, 2, 3] 和 [1, 3, 5] 组合成频繁项: [1, 2, 3, 5]
         [1, 2, 3] 和 [2, 3, 5] 组合成频繁项: [1, 2, 3, 5]
        '''
        newSupportData = []
        data = []  #频繁项
        for e in supportData:
            if e[0] not in data:
                data.append(e[0])
                newSupportData.append(e)
        return newSupportData
        
    def _work(self):
        '''Apriori发现频繁项和支持项,即支持数据集
        '''
        preData = self.supportData
        
        #Apriori算法发现频繁项集的过程代码
        new_supportData = []
        for i in range(np.shape(preData)[0]):
            e = preData[i][0] #就频繁项, current item in current supportdata
            #旧频繁项发现新的频繁项,只考虑后面的旧频繁项配对发现新的频繁项(提高算法时间性能)
            for j in np.arange(start=i+1, stop=len(preData)):
                be = preData[j][0] #item at the back of current item 
                #发现新数据集的频繁项, new_e
                new_e = []
                if 1 == np.shape(e)[0]:#旧频繁项是初始频繁项
                    new_e = e + be
                elif be.count(e[-1]) > 0 and be[-1] != e[-1]:
                    ind = be.index(e[-1])
                    new_e = e + be[ind+1:len(be)]
                if 0 == np.shape(new_e)[0]:
                    continue
                #支持度过滤
                support = self._support(new_e, self.goods)
                if (support >= self.minSupport):
                    new_supportData.append([new_e, support*self.N]) #[频繁项,支持项]
        #更新支持数据集,使用重复频繁项去重后的支持数据集
        self.supportData = self._uniq(new_supportData)
        if 0 == np.shape(self.supportData)[0] or self.max_len == np.shape(self.supportData[0][0])[0]:
            return self.supportData #exit apriori algorithm
        else:
            return self._work() #开始下次迭代计算
        
    def GetSupportData(self):
        return self.supportData

if __name__=='__main__':
    goods = [[1, 2, 5],
             [2, 4],
             [2, 3],
             [1, 2, 4],
             [1, 3],
             [2, 3],
             [1, 3],
             [1, 2, 3, 5],
             [1, 2, 3, 5],
             [1, 2, 3]]
    minSupport = 0.2
    apr = CApriori(goods, minSupport)
    
    supportData = apr.GetSupportData()
    print('最小支持度:', minSupport)
    print('交易商品列表:\n', goods)
    print('Apriori得到的支持数据集:\n', np.array(supportData))
    

运行结果

最小支持度: 0.2
交易商品列表:
 [[1, 2, 5], [2, 4], [2, 3], [1, 2, 4], [1, 3], [2, 3], [1, 3], [1, 2, 3, 5], [1, 2, 3, 5], [1, 2, 3]]
Apriori得到的支持数据集:
 [[[1, 2, 3, 5] 2.0]]

2.3Python实现代码

人肉出品,代码详见:

# -*- coding: utf-8 -*-
"""
@author: Tom
Talk is cheap, show me the code
Aim:实现Apriori算法
"""

import numpy as np

class CApriori(object):
    '''
    实现Apriori算法
    '''
    def __init__(self, goods, minSupport):
        self.goods = goods           #交易商品列表
        #最小支持度,支持度过滤时支持度小于此值的频繁项会被过滤掉
        self.minSupport = minSupport 
        
        self.N = np.shape(goods)[0]  #交易次数
        self.goodsSet = set([])      #商品集合, 元素是单个商品
        self.max_len  = 0            #最长交易的商品总数
        self.debug_cnt = 0           #记录迭代次数,调试使用,可以删除此变量
        #支持数据集,元素是[频繁项, 支持项],频繁项=商品序列list, 支持项=支持度*交易总次数
        self.supportData = []        
        
        self._init() #初始化
        self._work() #开始迭代运算直到找到支持数据集
        
    def _isSubset(self, A, B):
        '''判断序列a是否序列b的子集,且是有序子集,此处有序子集详见下面Note
        :param a, 一维序列
        :param b, 一维序列
        :return True:a是b的子集,False:a不是b的子集
        :Note [1, 3] 是 [1, 2, 3]的有序子集,[3, 1]不是[1, 2, 3]的有序子集
        '''
        A,B = list(A),list(B)
        if np.shape(A)[0] == 0:
            return False

        pre_ind = -1
        for e in A:
            if e not in B: #不是子集
                return False
            elif B.index(e) < pre_ind: #不满足有序
                return False
            pre_ind = B.index(e)

        return True

    def _support(self, item, goods):
        '''
        :param item, 频繁项
        :param goods, 交易商品列表
        :return 频繁项的支持度
        '''
        subset_cnt = [self._isSubset(item, e) for e in goods]
        cnt = subset_cnt.count(True)
        support = cnt * 1.0 / self.N
        return support
        
    def _init(self):
        '''初始化支持数据集和迭代计数器
        '''
        N,goods,minSupport = self.N, self.goods,self.minSupport
        self.supportData = []
        
        #设置迭代计数器
        for item in goods:
            if np.shape(item)[0] > self.max_len:
                self.max_len = np.shape(item)[0]
        
        #交易商品数据,一维list
        goods_data = []
        for e in goods:
            goods_data.extend(e)

        #交易商品集合,set
        self.goodsSet = set(goods_data)
        
        #初始数据集(频繁项,支持项)
        for i in range(len(self.goodsSet)):
            e = list(self.goodsSet)[i] #初始频繁项
            cnt = goods_data.count(e)
            support = cnt *1.0 / N
            if (support >= minSupport):
                self.supportData.append([[e], cnt])
                
        #debug
        self.debug_cnt += 1
        print('=================迭代执行次数:', self.debug_cnt)
        print('交易商品列表:\n', goods)
        print('最长交易记录的商品总数为:', self.max_len)
        print('交易商品集合:\n', self.goodsSet)
        print('初始数据集:\n', self.supportData)
        
    def _uniq(self, supportData):
        '''去除支持数据集中的重复频繁项,重复频繁项的产生示例:
         [1, 2, 3] 和 [1, 3, 5] 组合成频繁项: [1, 2, 3, 5]
         [1, 2, 3] 和 [2, 3, 5] 组合成频繁项: [1, 2, 3, 5]
        '''
        newSupportData = []
        data = []  #频繁项
        for e in supportData:
            if e[0] not in data:
                data.append(e[0])
                newSupportData.append(e)
        return newSupportData
        
    def _work(self):
        '''Apriori的主体函数,发现新的频繁项和支持项,即由旧的支持数据集发现新的支持数据集,直到发现完成
        '''        
        self.debug_cnt += 1
        print('\n=================迭代执行次数:', self.debug_cnt)
        N,goods,minSupport = self.N, self.goods,self.minSupport
        preData = self.supportData
        
        #Apriori算法发现频繁项集的过程代码
        new_supportData = []
        for i in range(np.shape(preData)[0]):
            print('\n',preData[i][0],'go to 发现新的频繁项:')
            #旧频繁项e
            e = preData[i][0] #current item in current supportdata
            #旧频繁项发现新的频繁项,只考虑后面的旧频繁项配对发现新的频繁项(提高算法时间性能)
            for j in np.arange(start=i+1, stop=len(preData)):
                be = preData[j][0] #item at the back of current item 
                #发现新数据集的频繁项, new_e
                new_e = []
                if 1 == np.shape(e)[0]:#旧频繁项是初始频繁项
                    new_e = e + be
                elif be.count(e[-1]) > 0 and be[-1] != e[-1]:
                    ind = be.index(e[-1])
                    new_e = e + be[ind+1:len(be)]
                if 0 == np.shape(new_e)[0]:
                    print('\t',e,'和',be ,'无法组合成新的频繁项.')
                    continue
                #支持度过滤
                support = self._support(new_e, goods)
                if (support >= minSupport):
                    new_supportData.append([new_e, support*N])
                    print('\t',e,'和',be ,'组合成频繁项:',new_e,'支持度:',support,'经过支持度过滤,增加此频繁项:', np.array([new_e, support*N]))
                else: #debug
                    print('\t',e,'和',be ,'组合成频繁项:',new_e,'支持度:',support,'经过支持度过滤,丢弃此频繁项:', np.array([new_e, support*N]))
        #更新支持数据集,使用重复频繁项去重后的支持数据集
        self.supportData = self._uniq(new_supportData)
        print('\nnew_supportData:\n', np.array(new_supportData))
        if 0 == np.shape(self.supportData)[0] or self.max_len == np.shape(self.supportData[0][0])[0]:
            print('Apriori succeed, supportData:\n', np.array(self.supportData))
        else:
            return self._work()

        print('======exit Apriori======\n')
        return self.supportData
        
    def GetSupportData(self):
        return self.supportData

if __name__=='__main__':
    goods = [[1, 2, 5],
             [2, 4],
             [2, 3],
             [1, 2, 4],
             [1, 3],
             [2, 3],
             [1, 3],
             [1, 2, 3, 5],
             [1, 2, 3, 5],
             [1, 2, 3]]
    minSupport = 0.2
    apr = CApriori(goods, minSupport)
    
    supportData = apr.GetSupportData()
    print('最小支持度:', minSupport)
    print('交易商品列表:\n', goods)
    print('最小支持度为%f时的支持数据集为:\n'%minSupport, np.array(supportData))
    

3.运行结果

=================迭代执行次数: 1
交易商品列表:
 [[1, 2, 5], [2, 4], [2, 3], [1, 2, 4], [1, 3], [2, 3], [1, 3], [1, 2, 3, 5], [1, 2, 3, 5], [1, 2, 3]]
最长交易记录的商品总数为: 4
交易商品集合:
 {1, 2, 3, 4, 5}
初始数据集:
 [[[1], 7], [[2], 8], [[3], 7], [[4], 2], [[5], 3]]

=================迭代执行次数: 2

 [1] go to 发现新的频繁项:
         [1] 和 [2] 组合成频繁项: [1, 2] 支持度: 0.5 经过支持度过滤,增加此频繁项: [[1, 2] 5.0]
         [1] 和 [3] 组合成频繁项: [1, 3] 支持度: 0.5 经过支持度过滤,增加此频繁项: [[1, 3] 5.0]
         [1] 和 [4] 组合成频繁项: [1, 4] 支持度: 0.1 经过支持度过滤,丢弃此频繁项: [[1, 4] 1.0]
         [1] 和 [5] 组合成频繁项: [1, 5] 支持度: 0.3 经过支持度过滤,增加此频繁项: [[1, 5] 3.0]

 [2] go to 发现新的频繁项:
         [2] 和 [3] 组合成频繁项: [2, 3] 支持度: 0.5 经过支持度过滤,增加此频繁项: [[2, 3] 5.0]
         [2] 和 [4] 组合成频繁项: [2, 4] 支持度: 0.2 经过支持度过滤,增加此频繁项: [[2, 4] 2.0]
         [2] 和 [5] 组合成频繁项: [2, 5] 支持度: 0.3 经过支持度过滤,增加此频繁项: [[2, 5] 3.0]

 [3] go to 发现新的频繁项:
         [3] 和 [4] 组合成频繁项: [3, 4] 支持度: 0.0 经过支持度过滤,丢弃此频繁项: [[3, 4] 0.0]
         [3] 和 [5] 组合成频繁项: [3, 5] 支持度: 0.2 经过支持度过滤,增加此频繁项: [[3, 5] 2.0]

 [4] go to 发现新的频繁项:
         [4] 和 [5] 组合成频繁项: [4, 5] 支持度: 0.0 经过支持度过滤,丢弃此频繁项: [[4, 5] 0.0]

 [5] go to 发现新的频繁项:

new_supportData:
 [[[1, 2] 5.0]
 [[1, 3] 5.0]
 [[1, 5] 3.0]
 [[2, 3] 5.0]
 [[2, 4] 2.0]
 [[2, 5] 3.0]
 [[3, 5] 2.0]]

=================迭代执行次数: 3

 [1, 2] go to 发现新的频繁项:
         [1, 2] 和 [1, 3] 无法组合成新的频繁项.
         [1, 2] 和 [1, 5] 无法组合成新的频繁项.
         [1, 2] 和 [2, 3] 组合成频繁项: [1, 2, 3] 支持度: 0.3 经过支持度过滤,增加此频繁项: [[1, 2, 3] 3.0]
         [1, 2] 和 [2, 4] 组合成频繁项: [1, 2, 4] 支持度: 0.1 经过支持度过滤,丢弃此频繁项: [[1, 2, 4] 1.0]
         [1, 2] 和 [2, 5] 组合成频繁项: [1, 2, 5] 支持度: 0.3 经过支持度过滤,增加此频繁项: [[1, 2, 5] 3.0]
         [1, 2] 和 [3, 5] 无法组合成新的频繁项.

 [1, 3] go to 发现新的频繁项:
         [1, 3] 和 [1, 5] 无法组合成新的频繁项.
         [1, 3] 和 [2, 3] 无法组合成新的频繁项.
         [1, 3] 和 [2, 4] 无法组合成新的频繁项.
         [1, 3] 和 [2, 5] 无法组合成新的频繁项.
         [1, 3] 和 [3, 5] 组合成频繁项: [1, 3, 5] 支持度: 0.2 经过支持度过滤,增加此频繁项: [[1, 3, 5] 2.0]

 [1, 5] go to 发现新的频繁项:
         [1, 5] 和 [2, 3] 无法组合成新的频繁项.
         [1, 5] 和 [2, 4] 无法组合成新的频繁项.
         [1, 5] 和 [2, 5] 无法组合成新的频繁项.
         [1, 5] 和 [3, 5] 无法组合成新的频繁项.

 [2, 3] go to 发现新的频繁项:
         [2, 3] 和 [2, 4] 无法组合成新的频繁项.
         [2, 3] 和 [2, 5] 无法组合成新的频繁项.
         [2, 3] 和 [3, 5] 组合成频繁项: [2, 3, 5] 支持度: 0.2 经过支持度过滤,增加此频繁项: [[2, 3, 5] 2.0]

 [2, 4] go to 发现新的频繁项:
         [2, 4] 和 [2, 5] 无法组合成新的频繁项.
         [2, 4] 和 [3, 5] 无法组合成新的频繁项.

 [2, 5] go to 发现新的频繁项:
         [2, 5] 和 [3, 5] 无法组合成新的频繁项.

 [3, 5] go to 发现新的频繁项:

new_supportData:
 [[[1, 2, 3] 3.0]
 [[1, 2, 5] 3.0]
 [[1, 3, 5] 2.0]
 [[2, 3, 5] 2.0]]

=================迭代执行次数: 4

 [1, 2, 3] go to 发现新的频繁项:
         [1, 2, 3] 和 [1, 2, 5] 无法组合成新的频繁项.
         [1, 2, 3] 和 [1, 3, 5] 组合成频繁项: [1, 2, 3, 5] 支持度: 0.2 经过支持度过滤,增加此频繁项: [[1, 2, 3, 5] 2.0]
         [1, 2, 3] 和 [2, 3, 5] 组合成频繁项: [1, 2, 3, 5] 支持度: 0.2 经过支持度过滤,增加此频繁项: [[1, 2, 3, 5] 2.0]

 [1, 2, 5] go to 发现新的频繁项:
         [1, 2, 5] 和 [1, 3, 5] 无法组合成新的频繁项.
         [1, 2, 5] 和 [2, 3, 5] 无法组合成新的频繁项.

 [1, 3, 5] go to 发现新的频繁项:
         [1, 3, 5] 和 [2, 3, 5] 无法组合成新的频繁项.

 [2, 3, 5] go to 发现新的频繁项:

new_supportData:
 [[[1, 2, 3, 5] 2.0]
 [[1, 2, 3, 5] 2.0]]
Apriori succeed, supportData:
 [[[1, 2, 3, 5] 2.0]]
======exit Apriori======

最小支持度: 0.2
交易商品列表:
 [[1, 2, 5], [2, 4], [2, 3], [1, 2, 4], [1, 3], [2, 3], [1, 3], [1, 2, 3, 5], [1, 2, 3, 5], [1, 2, 3]]
最小支持度为0.200000时的支持数据集为:
 [[[1, 2, 3, 5] 2.0]]

(end)

 
 

你可能感兴趣的:(人工智能,机器学习,跟我一起学机器学习,Machine,Learning)