使用Apriori算法挖掘频繁项集

一、问题描述

频繁模式挖掘搜索给定数据集中反复出现的联系,使用Apriori算法挖掘1M电影数据集中用户喜爱的电影之间的关联,分析用户的观影喜好,帮助推荐电影,例如:如果一位用户观看并喜爱了电影《你的名字》,他有多大可能也会观看《秒速5厘米》。

二、主要设备(软件)

Ubuntu,python2.7

三、Apriori算法

算法使用的是逐层搜索的迭代方法,使用K频繁项集生成k+1频繁项集,扫描数据库,累计每个项的计数,收集满足最小支持度的项生成频繁1项集用它找出频繁2项集,每次都使用前一个找出后一个频繁项集,每个Lk都需要一次数据的完整扫描。

在从Lk-1找Lk时为了提高效率,压缩搜索空间,使用连接步和剪枝步。连接步:通过将Lk-1于自身连接产生候选集k项集的集合。剪枝规则:将不可能的解提前过滤,如果有一个项集不是频繁项集,那么它的超集也一定不频繁,不作为候选集,不做最小支持度测试即任何非频繁的(k-1)项集都不是频繁k项集的子集。


四、实验过程

收集数据

  使用了MovieLens数据集ml-1m,数据大小为24.6MB的ratings.dat,数据按列依次为'user_id', 'name_id', 'rating_id', 'timestaps'。


输入数据

使用了pandas,基于numpy的工具,解决数据分析任务。

数据结构:

Series:序列,类似一维数组

DataFrame:表格型数据结构,二维数组

导入数据集:pd.read_table() 读取表格的通用函数,使用时要指定文件中的内容分隔符。


定义与变量说明:

load_data_set()

加载数据集函数


rating_names

评价表的列名


rating_list

评价列表


ratings

训练集


D_set

name_id按user_id分组存放入子列表


pre_C1(D_set)

创建频繁1项集的候选集函数


C1

频繁1项集的候选集


pre_dict

频繁1项集项和个数的字典


cut(Ck, Lk_1)

剪枝函数


sub_Ck

k-1项候选子集的集合


createCk(Lk_1, k):

list_Lk_1

连接步:生成频繁k项集的候选集集合

K-1项频繁项集集合的列表形式


l1,l2

Lk-1的项集


createLk(CK, D_set, min_sup)

根据候选集选出频繁项集集合的函数筛去不是D_set子集的和非频繁项集


Lk

K频繁项集的集合


LkDict

存放k频繁项集和支持度的字典


Support =[]

存放所有k频繁项集集合的列表


Lk_1

k-1频繁项集集合


L

总频繁项集集合的列表


t_num

整个数据集的事务个数



训练算法

# -*- coding:utf-8 -*-

import pandas as pd

#使用pandas读取文件并选取前200个用户喜欢的电影,分割为训练集,标准:评分大于3视为该用户喜欢此电影

def load_data_set():

    rating_names = ['user_id', 'name_id', 'rating_id', 'timestaps']

    rating_list = pd.read_table('/home/wangyuhang/python_learn/ml-1m/ratings.dat', sep='::', header=None,engine="python",names=rating_names)

    rating_list["like"] = rating_list["rating_id"] > 3

    # print rating_list.head(5)

    ratings = rating_list[rating_list['user_id'].isin(range(200))]

    like_ratings = ratings[ratings['like']]

    rating_data = like_ratings.groupby('user_id')


    data = dict((k, v.values) for k, v in like_ratings.groupby("user_id")["name_id"])

    D_set = []

    for a in data.keys():

        D_set.append(data[a])

    return D_set



# 创建频繁1项集的候选集的集合

def pre_C1(D_set):

    # global L

    # global Support

    C1 = set()

    pre_dict = dict()

    for row in D_set:

        for item in row:

            item_set = frozenset([item])  # 通过循环对data_set中的每个子列表内部的每个元素产生不可变集合

            if item_set not in C1:

                C1.add(item_set)

                pre_dict[item_set] = 1

            else:

                pre_dict[item_set] = pre_dict[item_set] + 1

    return pre_dict




# 剪枝步:任何非频繁的(k-1)项集都不是频繁k项集的子集

def cut(Ck, Lk_1):

    for item in Ck:  # 候选k项集的集合

        sub_Ck = Ck - frozenset([item])  # k-1项子集的集合

        if sub_Ck not in Lk_1:  # 如果一个候选的k项集的(k-1)项子集不在频繁项集L(K-1)中则该候选也不可能是频繁的

            return False  # 返回false该候选集不频繁。

        else:

            return True



# 连接步:生成频繁k项集的候选集的集合,为找出Lk,通过将L(k-1)与自身连接产生候选k项集的集合。

# 执行L(k-1)自然连接,其中L(k-1)的元素l1,l2是可连接的,仅当它们前(K-2)个项相同。即L(k-1)的元素l1,l2是可连接的。

def createCk(Lk_1, k):

    CK = set()

    list_Lk_1 = list(Lk_1)  # 将K-1频繁项集的集合转换为列表形式

    for i in range(0, len(Lk_1)):

        for j in range(1, len(Lk_1)):

            l1 = list(list_Lk_1[i]) #l1,l2为Lk-1的项集

            l2 = list(list_Lk_1[j])

            l1.sort()

            l2.sort()

            if l1[0:k - 2] == l2[0:k - 2]:  # 若l1,l2的前k-2个项相同

                Ck_item = list_Lk_1[i] | list_Lk_1[j]  # 将l1,l2的值并集赋给候选集子集

                if cut(Ck_item, Lk_1):  # 剪枝后添加到k候选集

                    CK.add(Ck_item)

    return CK



# 根据候选集选出频繁项集的集合,筛去不是D_set子集的和非频繁项集

def createLk(CK, D_set, min_sup):

    Lk = set()

    pre_dict ={}

    LkDict={}

    for row in D_set:  # 对每个在D_set子集中的候选项计算支持度

        for item in CK:

            if item.issubset(row):

                if item not in pre_dict:

                    pre_dict[item] = 1

                else:

                    pre_dict[item] = pre_dict[item] + 1

    t_num = float(len(D_set)) #整个数据集的事务个数

    for x in pre_dict:

        if pre_dict[x]/t_num >=min_sup:  # 判断是否大于最小支持度

            Lk.add(x)

            LkDict[x]=pre_dict[x]/t_num

    return Lk,LkDict



def generate_L(D_set, k, min_sup): #生成频繁项集集合,接收createLk函数返回的每个频繁k项集集合和他们的支持度,并保存到列表Support中

    Support =[]

    C1 = pre_C1(D_set)    #生成1频繁项集候选集

    L1= createLk(C1, D_set, min_sup)[0] #生成1频繁项集

    Support.append(createLk(C1, D_set, min_sup)[1]) #将1频繁项集和支持度的字典添加到列表Support中

    Lk_1 = L1 #用1频繁项集初始化k-1频繁项集集合

    L = []

    L.append(Lk_1)

    for i in range(2, k + 1):

        Ci = createCk(Lk_1, i)

        Li= createLk(Ci, D_set, min_sup)[0]

        Support.append(createLk(Ci, D_set, min_sup)[1])

        Lk_1 = Li.copy()

        L.append(Lk_1)

        # Support.append(su[i])

    # print su

    return Support


    # 打印频繁项集和支持度

if __name__ == "__main__":

    """

    Test


    """

    print "请等待,该数据大小为前%d用户的所有观影数据:"% len(load_data_set())

    D_set = load_data_set()

    Support = generate_L(D_set, 3, min_sup=0.2)

    # len_data=float(len(D_set))

    for su in Support:

        print "-" * 50

        print "frequent " + str(len(list(su.keys())[0])) + "-itemsets\t\tsupport"

        print "-" * 50

        for key in su:

            print "%s:\t%s" % (key, su[key])


五、问题分析

问题:速度过慢,存在效率问题,一开始采用了从字典中剔除不大于最小支持度的方法,过于耗时,时间复杂度为 n^2

解决方法:单步调试查找耗时原因,改进数据结构和算法。

1、在createLk()中将原本的剔除方法改为增添大于最小支持度的项,同时在本函数中计算支持度

2、在generate_L()方法中将原先的先用su字典接收返回值,再添加到Support列表中改为直接用Support列表接收返回值,相应的修改最终打印过程为每个Support列表中的频繁项集和支持度组成的字典,打印其键和值。

 

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容