*_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
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
s считали их равными.
Но ваша точка зрения хороша, я сейчас изучаю ее. Мы основываем хеширование строк на некотором порядке (который учитывает кодировку и т. Д.), Но на самом деле нам нужно только различать их, не обязательно ранжируя их.
TBC
Теперь получаем:
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, но скорость низкая.
Самый полезный комментарий
Теперь получаем:
что намного лучше, чем было раньше. Все еще выше предлагаемой реализации с использованием
factor
. Я мог бы просто пойти дальше и использовать это в следующий раз.