【问题标题】:how to make a fast pairwise Tanimoto distance function in R如何在R中制作一个快速的成对Tanimoto距离函数
【发布时间】: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 :实际上不如距离计算重要,但如果有人可以建议... rbindpairwise.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 &lt;- matrix(NA, nrow=40000, ncol=3),在循环中设置一个增量计数器k,然后用pairwise.dist[k,] &lt;- c(min(i,j),max(i,j),dij) 替换您的注释行。但无论如何我下面的建议会更快。

标签: r performance distance


【解决方案1】:

不确定是否要加快距离函数本身,但您可以使用 tidyverse 替换双循环

library(tidyverse)

results <- crossing(x = x, y = y) %>%             #all x,y combinations
  filter(x < y) %>%                               #remove duplicates
  mutate(pairwise.dist = map2_dbl(x, y, distfun)) #apply distance function

【讨论】:

  • 谢谢安德鲁;我也可以使用sapply;在这里我更关心距离函数和计算距离的有效存储,正如解释的那样。
猜你喜欢
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
  • 2021-02-04
  • 2021-05-09
相关资源
最近更新 更多