1.首先处理输入数据文件中数据的读入,数据存放在BUC.csv文件中,
每一行为一组维度组合序列,表示该序列出现一次。读取该文件,将数据保存在列表record中,保存形式如下:
record = [[‘a1’,‘b1’,‘c1’],[‘a2’,‘b1’,‘c2’]...]
返回record记录的同时返回不同维度的类别,如[‘a’,‘b’,‘c’]
本部分代码如下:
#读取文件,并将数据存储到record中
def readFile(filename):
record = []
fr = open(filename,'r')
for line in fr:
line = line.replace('\n','').split(',')
record.append(line)
#print([x[0] for x in record[0]])
Dims = ([x[0] for x in record[0]])
return record,Dims
2.根据第一步取得的总记录record和设置的最小支持度阈值min_sup,判断总记录条数是否大于min_sup,若总记录条数小于min_sup,则不可能有频繁项出现。代码如下:
#首先检查所有记录总数是否大于min_Sup
def Examine(record,min_sup):
num = 0
for rec in record:
num += 1
if num >= min_sup:
return True
else:
return False
3.根据第一步取得的总记录record和设置的最小支持度阈值min_sup,求出每个维度的每一个取值出现的次数,并返回那些出现次数满足min_sup的值,即返回单个项的频繁项,代码如下:
#计算每个维度的取值个数(如A类有a1、a2、a3、a4,计数为4)DimCountAll 如[4、2、2、1]
#并返回所有维度的取值集合 DimAll 如[[a1、a2、a3、a4]、[b1、b2]、[c1、c2]、[d1]]
#计算每一个维度的每一个取值出现的次数
#只返回那些出现次数满足min_Sup的值,
def Count_for_every_value(record,min_sup):
dic = dict()
for dim in range(0,len(record[0])):
for value in range(len(record)):
if record[value][dim] not in dic:
dic[record[value][dim]] = 1
else:
dic[record[value][dim]] += 1
result = {x: dic[x] for x in dic if dic[x] >= min_sup}
return result
4.正式进行BUC计算,首先根据步骤二中筛选出的频繁项K1i(存储在dim_list中),针对每一个频繁项,都遍历一遍所有的记录,对所有包含当前频繁项的序列的子序列C2i(存储在item_count中)的出现次数进行计数,并筛选出出现次数大于min_sup的C2i,记作K2i(存储在清空后的dim_list中,清空dim_list是为了防止输出时重复输出之前输出过的频繁项)。此时,完成了第一次迭代,将当前之存放了K2i的dim_list和当前迭代次数d作为参数进行第二次迭代。以此类推,直至完成所有迭代过程。
关于细节方面,针对单项的频繁项是很好处理的,针对包含多项的频繁项,我首先用中间变量将
[‘a1 b1’] 这种包含两项(或多项)的频繁项转化为a1b1,将每一条记录转化成形如a1b1c1d1,通过判断a1b1在不在a1b1c1d1里面,来组合成如C3i这种新的待选子序列并对其计数;为了防止针对每次迭代都从每条记录的头部开始,我声明了一个表示当前频繁项所包含项数的偏移量offset,从而大大减少了工作量。
代码如下:
5.运行程序,其中BUC.csv文件内容如下:
#record为所有的记录,count_for_every_value为所有频繁序列的计数字典,
#Dims为所有类别的列表,d为当前递归到了第几个类
def BUC(record,count_for_every_value,Dims,d,min_sup):
if d > len(Dims):
return 0
dim_list = list(count_for_every_value)
#遍历当前筛选出的每一个频繁项
item_count = {}
for dim_value in dim_list:
#sentence针对每一个如['a1','b1']这样的多项频繁项,将其转化为 a1b1
if len(dim_value.split(' ')) >=1:
sentence = ""
# offset记录当前的组合序列有几项,
# 如有2项,则在所有记录中进行进一步查找频繁项的过程中从每个记录的第三项找起
offset = 0
for dim in dim_value.split():
offset +=1
sentence += dim
#针对每一个频繁项,遍历每一条记录
for rec in record:
#变量temp将rec从['a1','b1','c1','d1']的形式变为a1b1c1d1
temp = ""
for dim in rec:
temp += dim
#若当前记录存在该频繁项,则进行如下操作
if sentence in temp:
#遍历每一条记录的 从频繁项往后的 每一项
for item in rec[offset+1:]:
#如果当前项和频繁项不是同一项(这一个判断可能多余了)
#对当前项和频繁项的组合进行计数,并存储到item_count中,
# 如['a1 b1 c1' : 3 , 'a1 b2' : 5]
if item[0] not in dim_value:
item = ' '.join([dim_value]+[item])
if item not in item_count:
item_count[item] = 1
else:
item_count[item] += 1
#dim_list用于存储当前次递归所得到的所有频繁项
dim_list = []
for item in item_count:
if item_count[item] >= min_sup:
dim_list.append(item)
#当dim_list不为空时,输出dim_list
if len(dim_list) > 0:
print(dim_list)
#进行下一次递归操作,不要忘记d+1,用于递归结束的判断
d += 1
BUC(record,dim_list,Dims,d,min_sup)
return 0
5.运行程序,其中BUC.csv文件内容如下:
a1,b1,c1,d1
a1,b2,c2,d2
a1,b3,c2,d1
a1,b4,c1,d2
a2,b1,c1,d1
a2,b2,c2,d2
a2,b3,c2,d1
a2,b4,c1,d2
a3,b1,c1,d1
a3,b2,c2,d2
a3,b3,c2,d1
a3,b4,c1,d2
a4,b1,c1,d1
a4,b2,c2,d2
a4,b3,c2,d1
a4,b4,c1,d2
输出如下:
['a1', 'a2', 'a3', 'a4', 'b1', 'b2', 'b3', 'b4', 'c1', 'c2', 'd1', 'd2']
['b1 c1', 'b1 d1', 'b2 c2', 'b2 d2', 'b3 c2', 'b3 d1', 'b4 c1', 'b4 d2', 'c1 d1', 'c1 d2', 'c2 d2', 'c2 d1', 'd1 c1', 'd1 c2', 'd2 c2', 'd2 c1']
['b1 c1 d1', 'b2 c2 d2', 'b3 c2 d1', 'b4 c1 d2']
#读取文件,并将数据存储到record中
def readFile(filename):
record = []
fr = open(filename,'r')
for line in fr:
line = line.replace('\n','').split(',')
record.append(line)
#print([x[0] for x in record[0]])
Dims = ([x[0] for x in record[0]])
return record,Dims
#首先检查所有记录总数是否大于min_Sup
def Examine(record,min_sup):
num = 0
for rec in record:
num += 1
if num >= min_sup:
return True
else:
return False
#计算每个维度的取值个数(如A类有a1、a2、a3、a4,计数为4)DimCountAll 如[4、2、2、1]
#并返回所有维度的取值集合 DimAll 如[[a1、a2、a3、a4]、[b1、b2]、[c1、c2]、[d1]]
#但是这里并没有用到这一函数。
def CountAllDim(record):
DimCountAll = []
DimAll = []
for i in range(0,len(record[0])):
#temp用于暂存针对当前这一维度的不同取值
temp = []
for j in range(0,len(record)):
if record[j][i] not in temp:
temp.append(record[j][i])
DimAll.append(temp)
DimCountAll.append(len(temp))
return DimCountAll,DimAll
#计算每一个维度的每一个取值出现的次数
#只返回那些出现次数满足min_Sup的值,
def Count_for_every_value(record,min_sup):
dic = dict()
for dim in range(0,len(record[0])):
for value in range(len(record)):
if record[value][dim] not in dic:
dic[record[value][dim]] = 1
else:
dic[record[value][dim]] += 1
result = {x: dic[x] for x in dic if dic[x] >= min_sup}
return result
#record为所有的记录,count_for_every_value为所有频繁序列的计数字典,
# Dims为所有类别的列表,d为当前递归到了第几个类
def BUC(record,count_for_every_value,Dims,d,min_sup):
if d > len(Dims):
return 0
dim_list = list(count_for_every_value)
#遍历当前筛选出的每一个频繁项
item_count = {}
for dim_value in dim_list:
#sentence针对每一个如['a1','b1']这样的多项频繁项,将其转化为 a1b1
if len(dim_value.split(' ')) >=1:
sentence = ""
# offset记录当前的组合序列有几项,
# 如有2项,则在所有记录中进行进一步查找频繁项的过程中从每个记录的第三项找起
offset = 0
for dim in dim_value.split():
offset +=1
sentence += dim
#针对每一个频繁项,遍历每一条记录
for rec in record:
#变量temp将rec从['a1','b1','c1','d1']的形式变为a1b1c1d1
temp = ""
for dim in rec:
temp += dim
#若当前记录存在该频繁项,则进行如下操作
if sentence in temp:
#遍历每一条记录的 从频繁项往后的 每一项
for item in rec[offset+1:]:
#如果当前项和频繁项不是同一项(这一个判断可能多余了)
#对当前项和频繁项的组合进行计数,并存储到item_count中,
# 如['a1 b1 c1' : 3 , 'a1 b2' : 5]
if item[0] not in dim_value:
item = ' '.join([dim_value]+[item])
if item not in item_count:
item_count[item] = 1
else:
item_count[item] += 1
#dim_list用于存储当前次递归所得到的所有频繁项
dim_list = []
for item in item_count:
if item_count[item] >= min_sup:
dim_list.append(item)
#当dim_list不为空时,输出dim_list
if len(dim_list) > 0:
print(dim_list)
#进行下一次递归操作,不要忘记d+1,用于递归结束的判断
d += 1
BUC(record,dim_list,Dims,d,min_sup)
return 0
if __name__ == '__main__':
result = []
filename = './BUC.csv'
record,Dims = readFile(filename)
min_sup = 3
if Examine(record, min_sup):
count_for_every_value = Count_for_every_value(record,min_sup)
#list的作用是将字典的key值全部取出,并生成list
#即先将单项的频繁序列输出
print(list(count_for_every_value))
BUC(record,count_for_every_value,Dims,2,min_sup)
#if Examine(record,3):
#DimCountAll,DimAll=CountAllDim(record)
这次实现的算法还有很大的优化空间,比如先从分区最多的维度进行BUC的剪枝操作,本例中的**CountAllDim(record)**函数也是为这一目的而编写,但因为时间关系并没有实现它,可以留待以后完成这一优化。