速度在 R 中做很多连接
Speed Doing Many Joins in R
我有一个类似于这个的数据框:
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")
您应该始终避免在循环中使用 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]))
您可能只需使用几个 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))
)
我有一个类似于这个的数据框:
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")
您应该始终避免在循环中使用 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]))
您可能只需使用几个 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))
)