我有一个与此类似的数据框:
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")
答案 0 :(得分:3)
你可能只需要使用几个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
我对dplyr
没有希望,但也许这样的事情会类似:
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))
)
答案 1 :(得分: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]))