标签:
最近在学大数据这门课,课上讲到了一个关于尿布与啤酒的故事,说是发现在超市中尿布如果和啤酒放在一起能跟提高销量,原因是买尿布的多是父亲,这些人看到啤酒后就想买(这是什么逻辑)。当然,这个故事被证明是虚构的了信息来源。
不过这个故事引出了一个问题,如果在一群放在不同类目(baskets)中的物品(items)中寻找成对(pair)的物品,且物品在不同类目中出现了至少threshold次,那么应该怎样做是有效率(空间上)的呢?
最naive的方法对于N个items,需要的操作数是 
假设
为了解决占用内存过大问题,引入了Aprior算法。
先看下Wikipedia的说明。先验算法(英语:Apriori algorithm)是关联式规则中的经典算法之一。在关联式规则中,一般对于给定的项目集合baskets(例如,零售交易集合,每个集合都列出的单个商品的购买信息),算法通常尝试在项目集合中找出至少有threshold个相同的子集。先验算法采用自底向上的处理方法,即频繁子集每次只扩展一个对象(该步骤被称为候选集产生),并且候选集由数据进行检验。当不再产生符合条件的扩展对象时,算法终止。
看起来好简单的算法,很快就实现了,但是跑起来慢得要死...最开始的版本,跑了一个晚上也没有跑完数据,优化后的依然不行,经过再次优化,最终优化版本30s跑完,可是我错过了Assignment提交的deadline

简单讲,Aprior算法就是利用了单调性:如果一个集合I,I中的物品都至少出现了threshold次,那么任意I的子集,不可能出现的次数少于threshold次(threshold在这里是指一个门限值)。
反过来讲,如果一个物品i出现次数不到threshold,那么凡是包含了i的集合,都不可能出现超过threshold次。
一般的实现思路是:
这样需要的内存就是最常出现的items的平方了
一图胜千言,
该流程迭代到
有10000个basket,同时有10000个数,第i个basket里放的都是能够整除i的数,举个例子:
解决这个问题,我们按照上方提到的算法,先构造k-tuple,将其通过过滤器,获取符合条件的的k-tuple,然后再将k-tuple构造成(k+1)-tuple,再继续迭代即可。
举一个简单的例子:
实际上,在生成(k+1)-tuple, 和过滤器这一步,有很多细节。如果剪枝剪得不充分,就会时间复杂度特别高,运行起来极其耗时。
根据这个算法,我们需要一个这样的函数:construct_filter(baskets_set, last_result, length)
这个函数的作用是,将candidate items输入,构造新的,长度为length的tuple,并对其过滤输出。其中baskets_set是我们要用于检查新tuple是否合格的源
第一个问题,如何构造(k+1)-tuple?
我的解决方法是,从
| 1 2 3 4 5 6 7 8 | foratuple inlast_result:    forindex inrange(len(atuple)):        candidate_set.add(atuple[index])        ifatuple[index] insource.keys():            source[atuple[index]].add(atuple)        else:            source[atuple[index]] =set()            source[atuple[index]].add(atuple) | 
显然,这样的实现有个很大的问题,在过滤时要遍历整个baskets,不必要的计算使得运行时间几个小时都跑不完。根据Apriori,
反过来讲,如果一个物品i出现次数不到threshold,那么凡是包含了i的集合,都不可能出现超过threshold次。
这里应该剪枝,不应该遍历整个baskets,而应该遍历
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | foratuple inlast_result:    # shrinke the set    temp_set =candidate_set -set(atuple)    # construct new (k+1)tuple    temp_list =[]    fornum intemp_set:        # make sure this tuple never checked before        temp_list =list(atuple)        temp_list.append(num)        temp_list.sort()        current_tuple =tuple(temp_list)        ifcurrent_tuple notinhistory:            history.add(current_tuple)        else:            continue        # count the frequent via basket_set        # find the source of num:source[num] is a set        temp_basket_set =set()        foraset inbasket_set[atuple]:            ifnum inbaskets[aset]:                temp_basket_set.add(aset)        iflen(temp_basket_set) > threshold:            result.append(current_tuple)            new_basket_set[current_tuple] =temp_basket_set            print("one answer:"+str(current_tuple)) | 
在这次作业中,我耗费了大量时间,错过了deadline,存在以下问题:
总而言之,自己过于自信,在自己不熟悉的情况下没有敬畏之心,在自己没有构思好整体思路时直接处理细节,导致我在处理过程中跟无头苍蝇一样。
通过这次作业,对python比较熟悉了,不得不说,python的api设计的很符合人的直觉,set的运算也非常好用。
标签:
原文地址:http://www.cnblogs.com/yilujuechen/p/4850903.html