海林老师《数据挖掘》课程作业系列
要求:自己写R/Python代码、函数实现一系列算法
其他参见:
全文逻辑:(读者可将所有代码按顺序复制到RStudio,全选ctrl+A,运行ctrl+enter,查看结果)
- 分析
- 算法/函数
- 测试数据
- 测试代码
- 测试结果(截图)
分析:这个比较难!!
#支持包:library(hash)
#输入数据集itemset,最小支持度min_sup,最小置信度min_conf。(均为小数)
#输出:频繁项集集合,及关联规则
#返回:关联规则数据框
#itemset包含:tid,items。
#如:itemset<-data.frame(tid=c("T1","T2","T3","T4","T5","T6","T7","T8","T9"),items=c("1,2,5","2,4","2,3","1,2,4","1,3","2,3","1,3","1,2,3,5","1,2,3"))
算法实现(编写函数):
#如下
myapriori<-function(itemset,min_sup,min_conf){
library(hash)
itemset$items<-as.character(itemset$items)
items_num<-nrow(itemset)
#字符串拼接。输入c("1","2","3")输出"1,2,3"
p_c<-function(c){
x=""
for (i in 1:length(c)) {
x=paste0(x,',',c[i])
}
return(substr(x,2,nchar(x)))
}
#返回CK_list,即若输入{1},{2},{3}k=2,则输出{1,2}{1,3},{2,3}
#LK_1_list:待自连接的列表,K:连接后元素的长度(K>1)
aprioriGen<-function(LK_1_list, k){
#返回的项集元素个数(若元素的前 k-2 相同,就进行合并)
CK_list = NULL
lenLk = length(LK_1_list)
for( i in 1:lenLk){
if((i+1)<=lenLk){
for (j in (i+1):lenLk){
L1 = LK_1_list[[i]][0:(k-2)]
L2 = LK_1_list[[j]][0:(k-2)]
L1=sort( L1)
L2=sort(L2)
# 第一次 L1,L2 为空,元素直接进行合并,返回元素两两合并的数据集
if (all(L1 == L2)){
CK_list=c(CK_list,list(union(LK_1_list[[i]],LK_1_list[[j]])))#并集
}
}
}
}
#剪枝降维
if(k>2){
CK_list_new=NULL
for (i in 1:length(CK_list)) {
if(all(combn(CK_list[[i]],k-1,simplify = F) %in% LK_1_list)){
CK_list_new=c(CK_list_new,CK_list[i])
}
}
CK_list<-CK_list_new
}
return(CK_list)
}
#返回频繁k项集数据框形式LK
#items_split原数据列表,CK_list为k项集列表形式,min_sup最小支持度
#注释掉的为:先扫描模式再扫描数据库(此方法不好。。。用下面的)······················
# pf<-function(items_split,CK_list,min_sup=0.5){
# CK<-data.frame(tid=NA,count=NA,supp=NA)
# for (i in 1:length(CK_list)) {
# count=0
# for (j in 1:length(items_split)) {
# if(all(CK_list[[i]]%in%items_split[[j]])){
# count=count+1
# }
# }
# tid=p_c(CK_list[[i]])
# supp=round(count/items_num,3)
# CK<-rbind(CK,c(tid,count,supp))
# }
# CK<-na.omit(CK)
# LK<-subset(CK,CK[,3]>=min_sup)
# return(LK)
# }
#先扫描数据库,再扫描模式!!!!
pf<-function(items_split,CK_list,min_sup=0.5){
count=rep(0,length(CK_list))
for (i in 1:length(items_split)) {
for (j in 1:length(CK_list)) {
if(all(CK_list[[j]]%in%items_split[[i]])){
count[j]=count[j]+1
}
}
}
tid=c()
for (x in 1:length(CK_list)) {
tid=c(tid,p_c(CK_list[[x]]))
}
supp=round(count/items_num,3)
CK<-data.frame(tid=tid,count=count,supp=supp,stringsAsFactors = F)
LK<-subset(CK,CK[,3]>=min_sup)
return(LK)
}
#frequent输出频繁项集(输入项集要为字符),包括频次
frequent<-function(itemset,min_sup=0.5){
#原始项集列表
item_list<-strsplit(itemset$items,',')
#计数1项集C1
C1<-as.data.frame(table(unlist(item_list)))
names(C1)<-c("tid","count")
C1$supp<-round((C1$count)/items_num,3)
#找出频繁1项集L1
L1<-subset(C1,C1$supp>=min_sup)
#L1<-na.omit(C1)
#L1<-L1[,1:2]
print(".............L1如下................")
print(L1)
L1_list<-as.list(as.character(L1[,1]))
L1_list_count<-as.list(L1[,2])
k=2
L=list(L1_list)
L_count=list(L1_list_count)
while(length(L[[k-1]])>0){
#根据LK_1XLK_1,产生初始K项集CK_list
CK_list<-aprioriGen(L[[k-1]],k)
if(is.null(CK_list)){
break
}
#扫描事务,对k项集计数CK和LK
LK<-pf(item_list,CK_list,min_sup)
print(paste0("...........L",k,"如下............."))
print(LK)
#将LK转换为LK_list
LK_list<-strsplit(LK$tid,',')
LK_list_count<-as.list(as.numeric(LK$count))
L=c(L,list(LK_list))
L_count=c(L_count,list(LK_list_count))
k=k+1
}
frequent=list(L,L_count)
return(frequent)
}
#利用frequent函数和数据集得到频繁模式
frequent1<-frequent(itemset,min_sup)
#将频次转换为键值对
h=hash()
for (i in 1:length(frequent1[[1]])) {
for (j in 1:length(frequent1[[1]][[i]])) {
keys=p_c(frequent1[[1]][[i]][[j]])#查找的时候可用h[[keys]]
values=frequent1[[2]][[i]][j]
.set(h, keys, values)
}
}
#计算可信度,
#如{1}{2}{1,2};求{1}=>{2};或者{1,2}{3}{1,2,3}求{1,2}=>{3}
#字符:a为并集,b为要求的,保留三位有效数字
conf<-function(a,b,h){
return(round(as.numeric(h[[a]])/as.numeric(h[[b]]),3))
}
#计算支持度
supp<-function(a,h,items_num){
return(round(as.numeric(h[[a]])/items_num,3))
}
#将数据分割,左k个;f为待分割列表如:{1 2}{1 3}{2 3}将被分成{1}{2}...
#返回数据框,包括原串,及两个分串和置信度
confidence=data.frame(tid=NA,b1=NA,b2=NA)
fg<-function(confidence,f,k){
for (i in 1:length(f)) {
tid=p_c(f[[i]])
b=combn(f[[i]],k,simplify=F)
for (j in 1:length(b)) {
b1=as.character(b[[j]])
b2=as.character(setdiff(f[[i]],b1))##求差集
b1=p_c(b1)#拼接字符
b2=p_c(b2)
confidence=rbind(confidence,c(tid,b1,b2))
}
}
confidence=na.omit(confidence)
#求置信度,给confidence增加一列支持度和置信度
for (i in 1:nrow(confidence)) {
confidence[i,"supp"]<-supp(confidence[i,1],h,items_num)
confidence[i,"conf"]<-conf(confidence[i,1],confidence[i,2],h)
}
return(confidence)
}
#输入频繁项集,及最小置信度;输出符合的关联规则
glgz<-function(fre,min_conf){
fre1=fre[[1]]
for (i in 1:length(fre1)) {
for (j in 1:length(fre1[[i]])) {
m=length(fre1[[i]][[j]])
while(m>1){
confidence=fg(confidence,fre1[[i]][j],m-1)
m=m-1
}
}
}
confidence=subset(confidence,confidence$conf>=min_conf)
return(confidence)
}
result=glgz(frequent1,min_conf)
print(paste("满足:最小支持度为:",min_sup,"; 最小置信度为:",min_conf))
print("----------------------------------------------------------")
for(i in 1:nrow(result)){
print(paste("{",result[i,2],"}","=>","{",result[i,3],"}"," ",result[i,4]," ",result[i,5]))
}
return(result)
}
数据测试:
测试数据:书上的测试数据
itemset<-data.frame(tid=c("T1","T2","T3","T4","T5","T6","T7","T8","T9"),items=c("1,2,5","2,4","2,3","1,2,4","1,3","2,3","1,3","1,2,3,5","1,2,3"))
a=myapriori(itemset,0.2,0.5)