Dplyr: Присоединение по символьному столбцу происходит медленно, по сравнению с присоединением по факторному столбцу.

Созданный на 5 сент. 2015  ·  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 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, но скорость низкая.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги