【发布时间】:2020-02-17 17:43:10
【问题描述】:
我有一个由整数属性 ID 标识的项目的 data.frame,这也是 data.frame 的行号。
每个项目都有一个与之关联的特征向量FP。每个FP 的元素都是唯一的(在FP 内)。例如c(1,2,7),但绝不是c(1,7,7)。
任意两个ID 之间的Tanimoto 距离定义为1 减去它们FP 的交点中唯一元素的数量,除以它们FP 的并集中的唯一元素数量的。
我需要在“maxmin”算法的上下文中计算这样的距离。参见例如this blog post。
需要注意的最重要的一点是,我必须不计算完整的距离矩阵(即使使用最好的算法,在我正在使用的数据集规模上也是不可行的)。
如上篇文章所述,根据 Roger Sayle 的方法,迭代 maxmin 选择器的优势在于可以避免计算大部分成对距离,而只计算少数相关的距离。因此我的问题。
到目前为止,这是我能想到的:
# make a random dataset
set.seed(1234567)
d <- sample(30:45, 1000, replace = T)
dd <- setNames(data.frame(do.call(rbind, sapply(d,function(n) list(sample(as.character(1:(45*2)), n, replace = F)), simplify = F))), "FP")
dd["ID"] <- 1:NROW(dd)
# define a pairwise distance function for ID's
distfun <- function(ID1,ID2) {
FP1 <- dd$FP[[ID1]]
FP2 <- dd$FP[[ID2]]
int <- length(intersect(FP1,FP2))
1 - int/(d[ID1]+d[ID2]-int)
}
# test performance of distance function
x <- sample(dd$ID, 200, replace = F)
y <- sample(dd$ID[!(dd$ID %in% x)], 200, replace = F)
pairwise.dist <- NULL
system.time(
for(i in x) {
for (j in y) {
dij <- distfun(i,j)
#pairwise.dist <- rbind(pairwise.dist,c(min(i,j),max(i,j),dij))
}
}
)
# user system elapsed
# 0.86 0.00 0.86
问题一:你认为距离函数可以做得更快吗?
我尝试制作特征的稀疏矩阵(下面代码中的ddu.tab,其中我省略了分母,分母很容易从交点计算)并将距离函数定义为向量运算,但那是 much 慢(我必须说有点令我惊讶)。
ddu <- do.call(rbind, sapply(dd$ID, function(x) {data.frame("ID"=x, "FP"=dd$FP[[x]], stringsAsFactors = F)}, simplify = F))
ddu.tab <- xtabs(~ID+FP, ddu, sparse = T)
system.time(
for(i in x) {
for (j in y) {
dij <- t(ddu.tab[i,]) %*% ddu.tab[j,]
#pairwise.dist <- rbind(pairwise.dist,c(min(i,j),max(i,j),dij))
}
}
)
# user system elapsed
# 32.35 0.03 32.66
问题 2 :实际上不如距离计算重要,但如果有人可以建议... rbind 对 pairwise.dist 的更新(显然)非常昂贵。我不知道我是否可以做不同的事情(意味着在每次迭代中不添加新元素),因为在 maxmin 应用程序中,要计算距离的 ID 对像在这个例子中那样预先不知道,并且pairwise.dist 不断被读取并添加新元素。
过去有人向我建议,列表可能比读/写矩阵更好。如果是这样,我可以将pairwise.dist 写为命名列表。
顺便说一句,仅供参考,在这个特定示例中,完整距离矩阵的计算速度非常快:
system.time(ddu.dist <- dist(ddu.tab, method = "binary"))
# user system elapsed
# 0.61 0.00 0.61
这似乎表明确实有一种快速计算二进制距离的方法。
如果有人可以建议和/或向我指出相关资源,那就太好了。
谢谢!
【问题讨论】:
-
关于第二个问题,如果您将
pairwise.dist预先创建为正确尺寸的矩阵,并且只需分配值,而不是rbinding,它会快得多。 -
谢谢安德鲁;但是,我尝试了您的建议,但速度要慢得多。我制作了一个充满 0 的稀疏矩阵,并在循环中一个一个地分配了它的元素。你能举一个比上面更好的例子吗?
-
我只是想设置
pairwise.dist <- matrix(NA, nrow=40000, ncol=3),在循环中设置一个增量计数器k,然后用pairwise.dist[k,] <- c(min(i,j),max(i,j),dij)替换您的注释行。但无论如何我下面的建议会更快。
标签: r performance distance