Dplyr: 与通过因子列连接相比,通过字符列连接比较慢。

创建于 2015-09-05  ·  5评论  ·  资料来源: tidyverse/dplyr

当键是字符时, *_join()函数很慢。 你有什么计划来改善这个吗?

library("dplyr")

set.seed(71)
size1 <- 4*10^5
size2 <- size1 * 0.1
df1 <- data.frame(id=paste0("SERVICE_", 1:size1), value=rnorm(size1), stringsAsFactors=FALSE)
df2 <- data.frame(id=paste0("SERVICE_", sample(1:size1, size2)), value=rnorm(size2), stringsAsFactors=FALSE)

print(system.time(ljd <- dplyr::left_join(df1, df2, "id")))
#>    user  system elapsed 
#>   15.50    0.07   15.56 

我认为*_join()在大多数情况下可以通过预先分解密钥来更快。 此外,我认为应该将键视为因素,因为没有人会尝试通过某些列连接,其中每一行都有不同的值。

print(system.time({
  lvl <- unique(c(df1$id, df2$id))
  ljd <- dplyr::left_join(mutate(df1, id=factor(id, levels = lvl)),
                          mutate(df2, id=factor(id, levels = lvl)),
                          "id")
  })
)
#>    user  system elapsed 
#>    0.33    0.10    0.42 

最有用的评论

现在得到:

set.seed(71)
size1 <- 4*10^5
size2 <- size1 * 0.1
df1 <- data.frame(id=paste0("SERVICE_", 1:size1), value=rnorm(size1), stringsAsFactors=FALSE)
df2 <- data.frame(id=paste0("SERVICE_", sample(1:size1, size2)), value=rnorm(size2), stringsAsFactors=FALSE)

print(system.time(ljd <- dplyr::left_join(df1, df2, "id")))
#>   user  system elapsed
#>  0.333   0.013   0.346
print(system.time({
  lvl <- unique(c(df1$id, df2$id))
  ljd <- dplyr::left_join(mutate(df1, id=factor(id, levels = lvl)),
                          mutate(df2, id=factor(id, levels = lvl)),
                          "id") %>% mutate( id = as.character(id) )
  })
)
#>    user  system elapsed
#>  0.255   0.007   0.262

这比我们以前的要好得多。 仍然高于使用factor的建议实现。 我可能会继续使用它。

所有5条评论

dplyr 应该利用 R 的全局字符串缓存在使用内存对象时进行字符串匹配。 您只需要检查相关的字符指针是否相同。

https://github.com/wch/r-source/blob/4a2026e8e/src/main/relop.c#L564 -L569, https : //github.com/wch/r-source/blob/9d4e23e/src /main/memory.c#L3878 -L3894 用于 C 实现。

这比实际上要复杂一些。 有时两个不同的指针是相同的字符串但具有不同的编码,我们希望join将它们视为相等。

但是你的观点很好,我现在正在研究它。 我们基于某种排序(尊重编码等)对字符串进行散列,但实际上我们只需要能够区分它们,而不必对它们进行排名。

待定

现在得到:

set.seed(71)
size1 <- 4*10^5
size2 <- size1 * 0.1
df1 <- data.frame(id=paste0("SERVICE_", 1:size1), value=rnorm(size1), stringsAsFactors=FALSE)
df2 <- data.frame(id=paste0("SERVICE_", sample(1:size1, size2)), value=rnorm(size2), stringsAsFactors=FALSE)

print(system.time(ljd <- dplyr::left_join(df1, df2, "id")))
#>   user  system elapsed
#>  0.333   0.013   0.346
print(system.time({
  lvl <- unique(c(df1$id, df2$id))
  ljd <- dplyr::left_join(mutate(df1, id=factor(id, levels = lvl)),
                          mutate(df2, id=factor(id, levels = lvl)),
                          "id") %>% mutate( id = as.character(id) )
  })
)
#>    user  system elapsed
#>  0.255   0.007   0.262

这比我们以前的要好得多。 仍然高于使用factor的建议实现。 我可能会继续使用它。

哇,好快!!! 非常感谢:+1:

@romainfrancois这是临时更新吗? 我是用最新版本的dplyr运行这个测试的,版本是0.4.3,但是速度慢。

此页面是否有帮助?
0 / 5 - 0 等级