최선의 가이드를 제공하려고하지만 모든 {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}의 우아한 구문을 선호합니다. 또한 frollapply
C로 작성된 롤링 패밀리 (예 :)와 같은 시계열 함수와 같은 다른 빠른 작업도 포함 합니다. 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
것과 유사합니다.update
setkey
- 두 번째 이후 조인에, 같은
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} 하지만 현재 비활성 상태입니다.
그것이 모두에게 도움이되기를 바랍니다. 좋은 하루 되세요 ☺☺
dplyr
당신이 잘 할 수없는가data.table
? 그렇지 않은 경우로 전환하는data.table
것이보다 좋습니다dtplyr
.