冰山立方体BUC算法是一种计算稀疏立方体的构建算法,在构建数据立方体的过程中,如果构建完整的数据仓库可能会花费大量的计算、存储和时间成本。
不过因为在应用过程中大部分的数据都是稀疏的,我们就可以通过冰山立方体的方式简化数据立方体。具体的效果就是,对于本来2^n种排列组合,通过类似BUC的算法,排除过稀疏的数据之后就只剩下很少的一部分,大大提高了数据立方体的效率。
BUC算法的构建有点类似二叉树的剪枝操作。我找到了一些现成的程序,发现基本上都是在操作一些过于简单的数据(只有不超过十组,两三个维度),这里我重新用递归的方法构建了BUC算法,并且针对了水果分类的数据,规模上要更大一些,希望对想要了解BUC算法的同学有所帮助。
我们用到的水果数据集由爱丁堡大学教授 Iain Murray 所创。他买了很多种类的橙子、柠檬和苹果,并把它们的相关数据记录在表格中。然后密歇根大学的一些学者将这些水果数据编排了格式,点击这里可以下载这些数据:Machine-Learning-with-Python/fruit_data_with_colors.txt
import numpy as np
import pandas as pd
这里用到了numpy和pandas,是比较基础的python库,安装也很简单,就不多作介绍了。
fruit = pd.read_table('fruit.txt')
我把原始数据存在了fruit变量里,因为txt文件放在了同目录,这里就直接读取了。
#离散化mass width height color_score
fruit['mass_bins'] = pd.cut(fruit['mass'],4,labels=False).astype('str')
fruit['width_bins'] = pd.cut(fruit['width'],4,labels=False).astype('str')
fruit['height_bins'] = pd.cut(fruit['height'],4,labels=False).astype('str')
fruit['color_score_bins'] = pd.cut(fruit['color_score'],4,labels=False).astype('str')
这里因为构建数据立方体的每个节点都是离散的,所以先对数据里面的连续特征做了离散化,python提供了很方便的离散工具,这里面4代表把数据由大到小分成了四个等间距组,结尾的astype是把结果变成字符串类型,这样方面后面和其他特征同时做处理(因为fruit_name,fruit_subtype等特征都是字符串)
#drop连续值的列
fruit_bins = fruit.drop(['fruit_label', 'mass', 'width', 'height', 'color_score'], axis=1)
这里的处理主要是我觉得数据仓库的一个原则是不改变原数据,所以重新创建了变量来进一步的操作,另一个原因也是单独领出来会更加清晰一点,后续的工作更加简单。
#初始化BUC参数
minimum_support = 3
label =[]
unit = []
result = []
result_value = []
t = []
for i in fruit_bins:
label.append(i)
这里面设置了minimum_support最小支持度,比它小的元组都做了剪枝处理。
label用来存放特征的名称
unit是个临时变量满足条件后会把unit的结果压入result结果中,这里面我还遇到了一些小麻烦,感兴趣的朋友可以看我另一篇帖子:python里面用append把一个数组压到另一个数组里面,原来的数组如果再append东西,后来的数组就跟着变化怎么办?
result用来存放元组的结果
result_value用来存放相应元组对应的元组数
t 是个中间变量,那篇帖子里有介绍
#BUC递归算法
def BUC( partial_data , i):
global unit
global result
global t
buc_temp = partial_data.groupby(label[i]).size().sort_values(ascending = False)
for [j,k] in zip(buc_temp.values,buc_temp.index):
if j < minimum_support :
t.extend(unit)
result.append(t)
result_value.append(j)
t = []
del(unit[-1])
return
else :
unit.append(k)
buc_temp = partial_data.query(label[i] + ' == ["' + k + '"]')
if i >= 5:
t.extend(unit)
result.append(t)
result_value.append(j)
t = []
del(unit[-1])
continue
else:
t.extend(unit)
result.append(t)
result_value.append(j)
t = []
BUC(buc_temp , i+1)
if i >= 1:
del(unit[-1])
具体实现的途径其实不复杂,就是要注意判定子程序结束的条件,理清逻辑后代码还是很容易实现的。
#跑起来吧
result = []
unit = []
BUC(fruit_bins, 0)
主函数就非常简单了
#去除重复的元素
results = []
results_value = []
for i , j in zip(result,range(len(result))):
if not i in results:
results.append(i)
results_value.append(result_value[j])
print(len(results))
print(len(results_value))
在上一步得到的结果里有很多的重复组,这一步把其中重复的部分删去。
#结果展示
for a,b in zip(results,results_value):
print(str(a) + " : " + str(b))
结果如下(展示前5行):
['apple'] : 19
['apple', 'cripps_pink'] : 6
['apple', 'cripps_pink', '1'] : 5
['apple', 'cripps_pink', '1', '1'] : 5
['apple', 'cripps_pink', '1', '1', '2'] : 3
其实到这一步还没做完,本来应该把已经离散化的几个特征再恢复回去,不能像这里这样,用1、2、3、4来表示,偷个懒吧。