【问题标题】:Speed Doing Many Joins in R快速在 R 中进行多次连接
【发布时间】:2015-03-02 07:26:26
【问题描述】:

我有一个类似的数据框:

n = c(rep("x", 3), rep("y", 5), rep("z", 2)) 
s = c("aa", "bb", "cc", "dd", "ee", "aa", "bb", "cc", "dd", "ff") 
df = data.frame(n, s) 

如果我要在 df$s 上加入它们,我想找到每个唯一 df$n 与其他每个 df$n 的匹配数。以下工作,但它很慢,而且我有很大的数据集。有没有更快的方法来解决这个问题?

place <- unique(df$n)
df_answer <- data.frame(place1 ="test1", place2 = "test2", matches = 2)
for(i in place) { 
  for(k in place) { 
    m1 <- inner_join(filter(df, n == i), filter(df, n == k), by = "s")
    m2 <- data.frame(place1 = i, place2 = k, matches = length(m1$s))
    df_answer <- rbind(df_answer, m2)
  } 
} 
df_answer <- filter(df_answer, place1 != "test1")

【问题讨论】:

标签: r dplyr


【解决方案1】:

您可能只需使用几个merge 调用就可以解决很多这种循环等问题:

ans <- expand.grid(place1=unique(df$n),place2=unique(df$n))
counts <- aggregate(s ~ ., data=
           setNames(merge(df, df, by="s",all=TRUE),c("s","place1","place2")), FUN=length)
merge(ans, counts, all=TRUE)

#  place1 place2  s
#1      x      x  3
#2      x      y  3
#3      x      z NA
#4      y      x  3
#5      y      y  5
#6      y      z  1
#7      z      x NA
#8      z      y  1
#9      z      z  2

我对@9​​87654323@ 毫无希望,但也许这样的事情是类似的:

expand.grid(n.x=unique(df$n), n.y=unique(df$n)) %>%
left_join(
          inner_join(df,df,by="s") %>% 
          group_by(n.x,n.y) %>% 
          summarise(s=length(s))
         )

【讨论】:

  • data.tableftw...setkey(setDT(df), s)[df][, length(s), key = .(n, i.n)][CJ(unique(df$n), unique(df$n))]
  • @DavidArenburg - 将其发布为答案。
  • 这太棒了。谢谢。
【解决方案2】:

您应该始终避免在循环中使用rbind。原因是每次使用它都会创建数据集的副本,并且随着数据集的增长,这些副本的制作时间会越来越长。我怀疑这是您的代码运行缓慢而不是使用inner_join 的原因。对此的解决方案是将每次迭代的输出存储在一个列表中,并在最后rbind 一次存储列表中的所有对象。

有一个更快的方法来得到你的答案,通过使用

length(intersect(filter(df, n == i)$s, filter(df, n == k)$s))

计算匹配的数量,避免连接,因为你实际上计算的是这两组交集的元素数量。这是一个对称操作,因此您不需要为每对执行两次。所以我会将循环重写为

place <- unique(df$n)
df_answer <- vector("list", length(place) * (length(place) - 1))
j <- 1
for (i in seq_along(place)) { 
    for (k in seq_len(i)) { 
        df_answer[[j]] <- data.frame(
                  place1 = place[i],
                  place2 = place[k], 
                  matches = length(intersect(filter(df, n == place[i])$s,
                            filter(df, n == place[k])$s)))
        j <- j + 1
    } 
} 
df_answer <- do.call(rbind, df_answer) # Convert to data frame format

另请注意,在您的原始答案中,您不需要创建具有单行的数据框然后将其删除。您可以创建没有这样行的数据框

data.frame(place1 = character(0), place2 = character(0), matches = integer(0))

您可以通过避免i == k 的情况来进一步加快您的代码,因为此后所有行都匹配所以它只是nrow(filter(df, n == place[i]))

【讨论】:

  • 这是我收到的最快的答案。谢谢。
猜你喜欢
  • 2016-09-18
  • 2010-09-19
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 2019-10-04
  • 2011-08-19
  • 2023-03-17
相关资源
最近更新 更多