data.table에서 할 수있는 dtplyr로 할 수없는 것


9

내가 특별히 사이에, R에서 논쟁 데이터에 대한 내 학습 노력을 투자해야 dplyr, dtplyrdata.table?

  • 나는 dplyr주로 사용 하지만 데이터가 너무 크면 사용할 것 data.table입니다. 드문 경우입니다. 이제 dtplyrv1.0 이에 대한 인터페이스로 나오기 때문에 data.table표면 data.table에서 다시 인터페이스 사용에 대해 걱정할 필요가없는 것처럼 보입니다 .

  • 그래서의 가장 유용한 기능이나 측면 무엇을하는 data.table것을는 없습니다 사용하여 수행 할 dtplyr순간에, 그 가능성으로 수행되지 않습니다 dtplyr?

  • 그것의 얼굴에 dplyr의 혜택 data.table차종은 같은 소리 dtplyr추월합니다 dplyr. dplyr한 번만 사용 하면 dtplyr완전히 성숙 할만한 이유가 있습니까?

참고 : 나는 dplyrvs data.table( data.table vs dplyr 에서 와 같이 묻지 않습니다 . 하나는 다른 것을 할 수 없거나 나쁘게 할 수 있습니까? )하지만 특정 문제에 대해 다른 것보다 선호되는 이유는 무엇입니까? t이 dtplyr사용하는 도구가 될 수.


1
뭔가가 당신이 잘에서 할 수있는 dplyr당신이 잘 할 수없는가 data.table? 그렇지 않은 경우로 전환하는 data.table것이보다 좋습니다 dtplyr.
sindri_baldur

2
로부터 dtplyr추가 정보 '일부 data.table표현은 직접이 없다 dplyr상응. 예를 들어, 크로스 조인 또는 롤링 조인을로 표현할 수있는 방법이 없습니다 dplyr. ' ' dplyr시맨틱 과 일치시키기 위해 mutate()는 기본적으로 제자리에서 수정되지 않습니다. 즉, 대부분의 표현은 직접 mutate()사용하는 경우 필요하지 않은 사본을 만들어야합니다 data.table. ' 두 번째 부분에는 약간의 방법이 있지만 얼마나 자주 mutate사용 되는지 고려 하면 내 눈에는 꽤 큰 단점입니다.
ClancyStats

답변:


15

최선의 가이드를 제공하려고하지만 모든 {data.table}, {dplyr}, {dtplyr} 및 {base R}에 익숙해야하기 때문에 쉽지 않습니다. {data.table} 및 많은 {tidy-world} 패키지를 사용합니다 ({dplyr} 제외). dplyr보다 data.table 구문을 선호하지만 둘 다 사랑합니다. 모든 깔끔한 월드 패키지가 필요할 때마다 {dtplyr} 또는 {data.table}을 백엔드로 사용하기를 바랍니다.

다른 번역 (dplyr-to-sparkly / SQL 생각)과 마찬가지로 적어도 지금은 번역 할 수 있거나 번역 할 수없는 것들이 있습니다. 아마 언젠가 {dtplyr}가 100 % 번역을 할 수 있다는 것을 알고 있습니다. 아래 목록은 완전하지 않으며 100 % 정확합니다. 관련 주제 / 패키지 / 문제 / 등에 대한 지식을 바탕으로 최선을 다할 것입니다.

중요한 것은 완전히 정확하지 않은 답변에 대해서는 {data.table}의 어떤 측면에주의를 기울이고 {dtplyr}과 비교하여 스스로 답을 찾아야하는지에 대한 가이드를 제공하기를 바랍니다. 이 답변을 당연한 것으로 여기지 마십시오.

또한이 게시물을 모든 {dplyr}, {data.table} 또는 {dtplyr} 사용자 / 작성자에게 토론 및 협업을위한 리소스 중 하나로 사용하고 #RStats를 더욱 개선 할 수 있기를 바랍니다.

{data.table}은 빠르고 메모리 효율적인 작업에만 사용되지 않습니다. 나 자신을 포함하여 많은 사람들이 {data.table}의 우아한 구문을 선호합니다. 또한 frollapplyC로 작성된 롤링 패밀리 (예 :)와 같은 시계열 함수와 같은 다른 빠른 작업도 포함 합니다. tidyverse를 포함한 모든 함수와 함께 사용할 수 있습니다. {data.table} + {purrr}을 많이 사용합니다!

운영의 복잡성

이것은 쉽게 번역 될 수 있습니다

library(data.table)
library(dplyr)
library(flights)
data <- data.table(diamonds)

# dplyr 
diamonds %>%
  filter(cut != "Fair") %>% 
  group_by(cut) %>% 
  summarize(
    avg_price    = mean(price),
    median_price = as.numeric(median(price)),
    count        = n()
  ) %>%
  arrange(desc(count))

# data.table
data [
  ][cut != 'Fair', by = cut, .(
      avg_price    = mean(price),
      median_price = as.numeric(median(price)),
      count        = .N
    )
  ][order( - count)]

{data.table}은 매우 빠르며 메모리 효율성이 뛰어납니다. (거의?) 모든 것이 C부터 시작하여 참조 기준 업데이트 , 키 (Think SQL) 의 핵심 개념 및 패키지의 모든 곳에서 끊임없이 최적화 되기 때문에 C부터 구축 됩니다. (즉 fifelse, fread/fread기본 R에서 채택 된 기수 정렬 순서) 구문이 간결하고 일관성이 있는지 확인하면서 우아하다고 생각합니다.

Introduction에서 data.table에 이르기까지 , 서브셋, 그룹, 업데이트, 조인 등과 같은 주요 데이터 조작 작업 이 함께 유지됩니다.

  • 간결하고 일관된 구문 ...

  • 각 작업을 매핑해야하는인지 부담없이 유동적으로 분석 수행 ...

  • 각 작업에 필요한 데이터를 정확하게 파악하여 내부에서 매우 효과적으로 작업을 자동으로 최적화하여 매우 빠르고 메모리 효율적인 코드 생성

예를 들어 마지막 요점은

# Calculate the average arrival and departure delay for all flights with “JFK” as the origin airport in the month of June.
flights[origin == 'JFK' & month == 6L,
        .(m_arr = mean(arr_delay), m_dep = mean(dep_delay))]
  • 먼저 출발지 공항이 "JFK"이고 월이 6L 인 일치하는 행 인덱스를 찾기 위해 i에서 부분 집합을 찾습니다. 우리는 아직 그 행에 해당하는 전체 data.table을 부분 집합하지 않습니다.

  • 이제 j를보고 두 개의 열만 사용한다는 것을 알았습니다. 그리고 우리가해야 할 것은 그들의 mean ()을 계산하는 것입니다. 따라서 일치하는 행에 해당하는 열만 하위 집합으로 만들고 평균 ()을 계산합니다.

쿼리세 가지 주요 구성 요소 (i, j 및 by)가 [...] 내부에 있기 때문에 data.table 은 세 가지를 모두보고 평가 전에 쿼리를 모두 개별적으로 확인할 수는 없습니다 . 따라서 속도와 메모리 효율성을 위해 전체 하위 집합 (즉, arr_delay 및 dep_delay 이외의 열을 하위 집합으로 설정)을 피할 수 있습니다.

따라서 {data.table}의 이점을 활용하려면 {dtplr}의 번역이 그 점에서 정확해야합니다. 작업이 복잡할수록 번역이 어려워집니다. 위와 같은 간단한 작업의 경우 쉽게 번역 할 수 있습니다. 복잡한 것 또는 {dtplyr}에 의해 지원되지 않는 것의 경우, 위에서 언급 한대로 자신을 찾아야하며, 번역 된 구문과 벤치 마크를 비교하고 친숙한 관련 패키지가되어야합니다.

복잡한 작업이나 지원되지 않는 작업의 경우 아래에 몇 가지 예를 제공 할 수 있습니다. 다시, 나는 최선을 다하고 있습니다. 조심해

참조 별 업데이트

소개 / 세부 사항에 들어 가지 않지만 여기에 링크가 있습니다

주요 자원 : 참조 의미론

자세한 내용 : data.table이 다른 데이터에 대한 참조 (사본 대) 인시기를 정확히 이해

제 생각에는 {data.table}의 가장 중요한 기능인 참조 별 업데이트는 매우 빠르고 효율적인 메모리입니다. dplyr::mutate기본적으로 지원하지 않습니다. {dtplyr}에 대해 잘 모르므로 {dtplyr}이 지원할 수있는 작업의 양과 양을 확실하지 않습니다. 위에서 언급했듯이, 작업의 복잡성에 따라 번역에 영향을 미칩니다.

{data.table}에서 참조 별 업데이트 를 사용하는 두 가지 방법이 있습니다.

  • {data.table}의 대입 연산자 :=

  • set-family : set, setnames, setcolorder, setkey, setDT, fsetdiff, 그리고 더 많은

:=에 비해 더 일반적으로 사용됩니다 set. 복잡하고 큰 데이터 세트의 경우 참조 별 업데이트 가 최고 속도 및 메모리 효율성을 얻는 데 중요합니다. 쉬운 생각 방법 (100 % 정확하지는 않습니다. 세부 사항은 하드 / 얕은 복사 및 기타 여러 요소가 포함되어 있기 때문에 이보다 훨씬 복잡하므로) 10GB 및 10GB의 큰 데이터 세트를 처리하고 있다고 가정하십시오. . 하나의 열을 조작하려면 1GB 만 처리하면됩니다.

요점은 update-by-reference로 필요한 데이터 만 처리하면된다는 것입니다. 그렇기 때문에 {data.table}을 사용할 때, 특히 대규모 데이터 세트를 처리 할 때 가능하면 항상 참조 기준 업데이트 를 사용 합니다. 예를 들어 대규모 모델링 데이터 세트 조작

# Manipulating list columns

df <- purrr::map_dfr(1:1e5, ~ iris)
dt <- data.table(df)

# data.table
dt [,
    by = Species, .(data   = .( .SD )) ][,  # `.(` shorthand for `list`
    model   := map(data, ~ lm(Sepal.Length ~ Sepal.Width, data = . )) ][,
    summary := map(model, summary) ][,
    plot    := map(data, ~ ggplot( . , aes(Sepal.Length, Sepal.Width)) +
                           geom_point())]

# dplyr
df %>% 
  group_by(Species) %>% 
  nest() %>% 
  mutate(
    model   = map(data, ~ lm(Sepal.Length ~ Sepal.Width, data = . )),
    summary = map(model, summary),
    plot    = map(data, ~ ggplot( . , aes(Sepal.Length, Sepal.Width)) +
                          geom_point())
  )

list(.SD)tidyverse 사용자가 tidyr::nest?를 사용하므로 {dtlyr}에서 중첩 작업을 지원하지 않을 수 있습니다 . 따라서 후속 작업을 {data.table}의 방식으로 더 빠르고 적은 메모리로 변환 할 수 있는지 확실하지 않습니다.

참고 : data.table의 결과는 "밀리 초", dplyr는 "분"입니다.

df <- purrr::map_dfr(1:1e5, ~ iris)
dt <- copy(data.table(df))

bench::mark(
  check = FALSE,

  dt[, by = Species, .(data = list(.SD))],
  df %>% group_by(Species) %>% nest()
)
# # A tibble: 2 x 13
#   expression                                   min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
#   <bch:expr>                              <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
# 1 dt[, by = Species, .(data = list(.SD))] 361.94ms 402.04ms   2.49      705.8MB     1.24     2     1
# 2 df %>% group_by(Species) %>% nest()        6.85m    6.85m   0.00243     1.4GB     2.28     1   937
# # ... with 5 more variables: total_time <bch:tm>, result <list>, memory <list>, time <list>,
# #   gc <list>

참조 별 업데이트 사용 사례는 많으며 {data.table} 사용자는 더 많은 코드가 필요하기 때문에 항상 고급 버전을 사용하지 않습니다. {dtplyr}이 이러한 기본 지원 기능을 지원하는지 여부를 스스로 확인해야합니다.

동일한 기능에 대한 다중 참조 별 업데이트

주요 자원 : lapply ()를 사용하여 data.table에 여러 열을 우아하게 할당

이것은 일반적으로 사용되는 중 하나를 포함 :=하거나 set.

dt <- data.table( matrix(runif(10000), nrow = 100) )

# A few variants

for (col in paste0('V', 20:100))
  set(dt, j = col, value = sqrt(get(col)))

for (col in paste0('V', 20:100))
  dt[, (col) := sqrt(get(col))]

# I prefer `purrr::map` to `for`
library(purrr)
map(paste0('V', 20:100), ~ dt[, (.) := sqrt(get(.))])

{data.table}의 작성자에 따라 Matt Dowle

(많은 수의 열보다 많은 수의 행에 대해 루프 설정을하는 것이 더 일반적 일 수 있습니다.)

조인 + setkey + 참조 별 업데이트

최근에 비교적 큰 데이터와 비슷한 조인 패턴으로 빠른 조인이 필요했기 때문에 일반 조인 대신 참조 기준 업데이트 기능을 사용 합니다. 더 많은 코드가 필요하기 때문에 재사용 가능성과 가독성에 대한 비표준 평가를 통해 개인 패키지로 묶습니다 setjoin.

여기에서 몇 가지 벤치 마크를 수행했습니다 : data.table join + 참조 기준 업데이트 + setkey

요약

# For brevity, only the codes for join-operation are shown here. Please refer to the link for details

# Normal_join
x <- y[x, on = 'a']

# update_by_reference
x_2[y_2, on = 'a', c := c]

# setkey_n_update
setkey(x_3, a) [ setkey(y_3, a), on = 'a', c := c ]

참고 : dplyr::left_join또한 테스트되었으며 ~ 9,000 ms로 가장 느리고 {data.table} update_by_reference및 보다 많은 메모리를 setkey_n_update사용하지만 {data.table}의 normal_join보다 적은 메모리를 사용하십시오. 약 2.0GB의 메모리를 소비했습니다. {data.table}에만 집중하고 싶기 때문에 포함하지 않았습니다.

중요한 발견들

  • setkey + update하고 update있습니다 ~ 11과 ~ 6.5 배 빠른 속도에 비해 normal join각각
  • 첫 번째 결합에서 성능은 오버 헤드가 자체 성능 향상 을 크게 상쇄하는 setkey + update것과 유사합니다.updatesetkey
  • 두 번째 이후 조인에, 같은 setkey필요하지 않습니다 setkey + update보다 더 빨리이다 update~ 1.8 배 (또는보다 더 빨리 normal join~ 11 배)

영상

수행 및 메모리 효율적인 조인의 경우 update또는 setkey + update코드를 많이 사용하여 후자가 더 빠른 경우 또는를 사용하십시오.

간결성을 위해 의사 코드를 보자 . 논리는 동일합니다.

하나 또는 몇 개의 열

a <- data.table(x = ..., y = ..., z = ..., ...)
b <- data.table(x = ..., y = ..., z = ..., ...)

# `update`
a[b, on = .(x), y := y]
a[b, on = .(x),  `:=` (y = y, z = z, ...)]
# `setkey + update`
setkey(a, x) [ setkey(b, x), on = .(x), y := y ]
setkey(a, x) [ setkey(b, x), on = .(x),  `:=` (y = y, z = z, ...) ]

많은 열

cols <- c('x', 'y', ...)
# `update`
a[b, on = .(x), (cols) := mget( paste0('i.', cols) )]
# `setkey + update`
setkey(a, x) [ setkey(b, x), on = .(x), (cols) := mget( paste0('i.', cols) ) ]

빠르고 효율적인 메모리 조인을위한 래퍼 ... 많은 setjoin것들 ... 유사한 조인 패턴으로, 위와 같이-포함 update 또는 포함하지 않고 랩핑setkey

setjoin(a, b, on = ...)  # join all columns
setjoin(a, b, on = ..., select = c('columns_to_be_included', ...))
setjoin(a, b, on = ..., drop   = c('columns_to_be_excluded', ...))
# With that, you can even use it with `magrittr` pipe
a %>%
  setjoin(...) %>%
  setjoin(...)

setkey 하면 인수 on를 생략 할 수 있습니다. 또한 가독성, 특히 다른 사람들과의 공동 작업을 위해 포함될 수 있습니다.

큰 행 작업

  • 위에서 언급했듯이 set
  • 테이블을 미리 채우고 참조 기준 업데이트를 사용 하십시오. 기술을 사용하십시오.
  • 키를 사용하여 부분 집합 (예 setkey)

관련 자료 : data.table 오브젝트의 끝에 참조로 행 추가

참조 별 업데이트 요약

이들은 참조 별 업데이트의 일부 유스 케이스입니다. . 더 많은 것이 있습니다.

보시다시피, 대용량 데이터 처리의 고급 사용에는 참조 별 업데이트를 사용하는 많은 사용 사례와 기술이 있습니다. 위해 대용량 데이터 세트에 대한 있습니다. {data.table}에서 사용하기가 쉽지 않고 {dtplyr}에서 지원하는지 여부를 직접 확인할 수 있습니다.

필자 는 빠르고 효율적인 메모리 운영을위한 {data.table}의 가장 강력한 기능이라고 생각하므로이 게시물에서 참조 별 업데이트에 중점을 둡니다 . 즉, 너무 효율적으로 만드는 많은 다른 측면이 있으며, 이것이 {dtplyr}에 의해 기본적으로 지원되지 않는다고 생각합니다.

다른 주요 측면

지원되지 않거나 지원되지 않는 작업의 복잡성 및 참조 별 업데이트 또는 과 같은 data.table의 기본 기능과 관련이 있는지 여부에 따라 다릅니다 setkey. 그리고 변환 된 코드가 더 효율적인 코드인지 (data.table 사용자가 작성하는 것) 또한 다른 요소입니다 (즉, 코드는 변환되지만 효율적인 버전입니까?). 많은 것들이 서로 연결되어 있습니다.

  • setkey. 참조 키와 빠른 이진 검색 기반의 부분 집합
  • 보조 인덱스 및 자동 인덱싱
  • 데이터 분석에 .SD 사용
  • 시계열 함수 : think frollapply. 롤링 기능, 롤링 집계, 슬라이딩 윈도우, 이동 평균
  • 롤링 조인 , 비 동등 조인 , (일부) "크로스"조인
  • {data.table}은 속도 및 메모리 효율성의 토대를 구축했으며, 앞으로는 위에서 언급 한 시계열 함수를 구현하는 방식과 같이 많은 함수를 포함하도록 확장 할 수 있습니다.
  • 일반적으로, data.table의에 더 복잡한 작업 i, j또는 by작업, 나는 그것이 결합 특히, 열심히 번역을 생각한다 (당신은 거기에 거의 모든 표현식을 사용할 수 있습니다) 업데이트를 참조에 의한 , setkey및 기타 기본 data.table 같은 기능frollapply
  • 또 다른 요점은 기본 R 또는 tidyverse 사용과 관련이 있습니다. data.table + tidyverse (dplyr / readr / tidyr 제외)를 모두 사용합니다. 대규모 작업의 경우 종종 예를 들어 stringr::str_*제품군 대 기본 R 기능을 벤치마킹 하고 기본 R이 어느 정도 더 빠르다는 것을 알았습니다. 요점은, 단 정치 또는 data.table 또는 ...에 자신을 유지하지 말고, 다른 옵션을 탐색하여 작업을 완료하십시오.

이러한 측면 중 많은 부분이 위에서 언급 한 요점과 관련이 있습니다.

  • 운영의 복잡성

  • 참조 별 업데이트

{dtplyr}이 특히 결합 될 때 이러한 조작을 지원하는지 확인할 수 있습니다.

작거나 큰 데이터 세트를 처리 할 때 대화식 세션 동안 {data.table}의 또 다른 유용한 트릭은 프로그래밍계산 시간을 엄청나게 단축하겠다는 약속에 충실 합니다.

속도 및 '과급 된 행 이름'(변수 이름을 지정하지 않은 서브 세트) 모두에 반복적으로 사용되는 변수의 설정 키.

dt <- data.table(iris)
setkey(dt, Species) 

dt['setosa',    do_something(...), ...]
dt['virginica', do_another(...),   ...]
dt['setosa',    more(...),         ...]

# `by` argument can also be omitted, particularly useful during interactive session
# this ultimately becomes what I call 'naked' syntax, just type what you want to do, without any placeholders. 
# It's simply elegant
dt['setosa', do_something(...), Species, ...]

첫 번째 예와 같이 간단한 조작 만 수행하면 {dtplyr}이 작업을 완료 할 수 있습니다. 복잡하거나 지원되지 않는 것의 경우,이 안내서를 사용하여 {dtplyr}의 번역 된 것을 노련한 data.table 사용자가 data.table의 우아한 구문으로 빠르고 메모리 효율적으로 코딩하는 방법과 비교할 수 있습니다. 번역이 다른 대용량 데이터 사례를 처리하는 다른 기술이있을 수 있으므로 가장 효율적인 방법은 아닙니다. 더 큰 데이터 세트의 경우 {data.table}을 {disk.frame} , {fst}{drake} 및 기타 멋진 패키지와 결합하여 최대한 활용할 수 있습니다. 또한이 {big.data.table} 하지만 현재 비활성 상태입니다.

그것이 모두에게 도움이되기를 바랍니다. 좋은 하루 되세요 ☺☺


2

비 균등 조인과 롤링 조인이 떠 오릅니다. dplyr에 동등한 기능을 전혀 포함시킬 계획이 없으므로 dtplyr가 번역 할 것이 없습니다.

dplyr에도없는 재구성 (reshape2의 동일한 기능과 동등한 최적화 된 캐스트 및 용융)도 있습니다.

모든 * _if 및 * _at 함수는 현재 dtplyr로도 변환 할 수 없지만 작동 중입니다.


0

조인시 열 업데이트 일부 .SD 트릭 많은 f 함수 그리고 신은 #rdatatable이 단순한 라이브러리 그 이상이고 함수가 거의 없기 때문에 다른 것을 알고 있습니다.

자체 생태계 전체입니다

R을 시작한 이래로 dplyr가 필요하지 않았습니다. data.table이 너무 좋기 때문에

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.