如何用R data.table无循环实现函数赋值与连接赋值?
用data.table实现无循环的层级聚类赋值与连接赋值
1. 按ID分组生成层级聚类对象(替代第一个for循环)
你原来通过for循环给tSummary的Clust列赋值层级聚类对象,完全可以用data.table的原生分组操作实现,不需要循环:
解决方案代码
library(data.table) # 生成示例数据 tAmounts <- data.table( rbind( cbind(ID="Apple", Amt=as.numeric(EuStockMarkets[1:50,2])), cbind(ID="Orange", Amt=as.numeric(EuStockMarkets[1:30,3])), cbind(ID="Lemon", Amt=as.numeric(EuStockMarkets[1:60,4])) ) ) setkey(tAmounts, ID, Amt) # 直接按ID分组生成聚类对象,无需循环 tSummary <- tAmounts[, .( N = .N, Clust = list(hclust(dist(Amt), method = "average")) ), by = ID]
说明
data.table的by分组会对每个ID组独立计算j中的表达式:
.N直接返回每组的行数- 用
list()包裹hclust(dist(Amt), ...),是因为hclust返回的是一个对象,需要将其作为列表元素存入Clust列,确保每个组对应一个聚类对象。
你提到的类似tSummary[, Clust:=hclust(dist(Amt), method="average"), by="ID"]的写法,需要调整为用list()包裹hclust调用,否则会因对象长度不匹配报错,正确的修改写法是:
# 如果是对已存在的tSummary表修改 tSummary <- tAmounts[, .(N = .N), by = ID] tSummary[, Clust := list(hclust(dist(tAmounts[ID == .BY$ID]$Amt), method = "average")), by = ID]
2. 连接操作中基于聚类对象生成分组编号(替代第二个for循环)
你原来通过循环tCuts的每一行给tClust赋值ClustNum,可以用data.table的连接+分组操作实现无循环赋值:
解决方案代码
# 生成切割高度表 tCuts <- CJ(ID=unique(tAmounts$ID), Cut=seq(20,100,20)) setkey(tCuts, ID, Cut) # 生成包含所有ID-Cut-Amt组合的表 tClust <- tCuts[tAmounts, on="ID", allow.cartesian=TRUE] setkey(tClust, ID, Cut, Amt) # 无循环赋值ClustNum tClust[, ClustNum := cutree(tSummary[ID == .BY$ID]$Clust[[1]], h = .BY$Cut), by = .(ID, Cut)]
说明
.BY是data.table分组中的特殊变量,代表当前组的键值(这里是ID和Cut的组合)- 对每个
ID-Cut组,从tSummary中取出对应ID的聚类对象,用当前组的Cut值作为cutree的高度参数,直接生成该组所有Amt对应的聚类编号。
如果你想更贴近你提到的tClust[tCuts, ClustNum:=cutree(Clust, h=Cut)]写法,可以用**连接+by=.EACHI**实现:
# 先将聚类对象关联到tCuts tCuts_with_clust <- tCuts[tSummary, on = "ID"] # 通过连接+逐组计算赋值 tClust[, ClustNum := tCuts_with_clust[.SD, cutree(Clust[[1]], h = Cut), by = .EACHI, on = .(ID, Cut)]$V1]
by=.EACHI会针对tCuts_with_clust中的每个ID-Cut组合,匹配tClust中对应的行并计算聚类编号。
内容的提问来源于stack exchange,提问作者C8H10N4O2
相关产品推荐
相关产品推荐

