data.table의 R 속도
가능한 한 더 일반적으로 확장하고 싶은 특정 성능 문제가 있습니다.
문맥:
나는 defaultdict를 사용하여 상태와 작업을 값에 연결하는 Q-Learning 에이전트를위한 파이썬 코드 샘플로 Google colab에서 놀았습니다.
self._qvalues = defaultdict(lambda: defaultdict(lambda: 0))
return self._qvalues[state][action]
전문가는 아니지만 내 이해는 값을 반환하거나 키를 찾지 못하면 0을 반환합니다.
나는 R
에서 이것의 일부를 적용 하고 있습니다. 문제는 내가 얼마나 많은 상태 / 값 조합을 가지고 있는지, 기술적으로 내가 얼마나 많은 상태를 추측하는지 알 수 없다는 것입니다.
처음에 나는 rbind
of data.frame
s와 함께 잘못된 길을 갔고 그것은 매우 느 렸습니다.
그런 다음 내 R 개체를 data.frame(state, action, value = NA_real)
. 작동하지만 여전히 매우 느립니다. 또 다른 문제는 내 data.frame 객체의 최대 크기가 미래에 문제가 될 수 있다는 것입니다.
나는 내를 chanded data.frame
A와 data.table
나는 마침내 (상태, 액션)하여 인덱스 나에게 최악의 성능을 준.
qvalues <- data.table(qstate = rep(seq(nbstates), each = nbactions),
qaction = rep(seq(nbactions), times = nbstates),
qvalue = NA_real_,
stringsAsFactors = FALSE)
setkey(qvalues, "qstate", "qaction")
문제:
googlecolab / python과 내 로컬 R 구현을 비교하면 Google은 15 초에 객체에 대한 1000x10e4 액세스를 수행하고 내 코드는 28 초에 100x100 액세스를 수행합니다. 바이트 컴파일로 2s 향상을 얻었지만 여전히 너무 나쁩니다.
를 사용하면 profvis
대부분의 시간이 다음 두 호출에서 data.table에 액세스하는 데 소비됩니다.
qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
self$qvalues[J(state, action)]$qvalue <- value
나는 구글이 무엇을 가지고 있는지 정말로 모른다. 그러나 나의 데스크탑은 짐승이다. 또한 data.table
보다 빠르다 는 일부 벤치 마크를 보았 pandas
으므로 문제는 내가 선택한 컨테이너에 있다고 생각합니다.
질문 :
- data.table 사용이 잘못되었으며 파이썬 구현을 개선하고 일치시키기 위해 수정할 수 있습니까?
- 차원이 너무 커지면 문제가 될 수있는 모든 상태 / 동작 조합을 선언하는 것을 방지 할 수있는 또 다른 디자인이 있습니까?
- 해시 패키지에 대해 본 적이 있습니다. 갈 길이입니까?
포인터 주셔서 감사합니다!
최신 정보:
모든 입력에 감사드립니다. 그래서 내가 한 일은 귀하의 제안을 사용하여 내 data.table에 대한 3 개의 액세스를 대체하는 것입니다.
#self$qvalues[J(state, action)]$qvalue <- value
self$qvalues[J(state, action), qvalue := value] #self$qvalues[J(state, action),]$qvalue <- 0 self$qvalues[J(state, action), qvalue := 0]
#qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
qval <- self$qvalues[J(state, action), nomatch = NA_real_, qvalue]
이로 인해 런타임이 33 초에서 21 초로 크게 개선되었지만 Python defaultdict
구현에 비해 여전히 매우 느립니다 .
나는 다음 사항에 주목했다.
일괄 작업 : 함수 호출이 이전 호출에 따라 달라 지므로 할 수 없다고 생각합니다.
peudospin> 시간이 많이 걸린다는 것에 놀랐습니다. 나도 그렇다.하지만 그것이 profvis가 말하는 것이다 :

QAgent$set("public", "get_qvalue", function( state, action) {
#qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
qval <- self$qvalues[J(state, action), nomatch = NA_real_, qvalue] if (is.na(qval)) { #self$qvalues[self$qvalues$qstate == state & self$qvalues$qaction == action,]$qvalue <- 0 #self$qvalues[J(state, action),]$qvalue <- 0 self$qvalues[J(state, action), qvalue := 0]
return(0)
}
return(qval)
})
이 시점에서 더 이상 제안이 없으면 data.table이 이런 종류의 작업에 너무 느리다는 결론을 내릴 것이므로 env
또는 collections
. (제안 된대로 : R 목록에서 빠른 단일 항목 조회 대 data.table 대 해시 )
결론:
나는 대체 data.table
A의 collections::dict
및 병목 현상이 완전히 사라졌다.
답변
data.table
매우 큰 데이터 테이블에서 조회 및 조작을 수행하는 데는 빠르지 만 파이썬 사전과 같이 행을 하나씩 추가하는 데는 빠르지 않습니다. 나는 당신이 분명히 원하는 것이 아닌 행을 추가 할 때마다 전체 테이블을 복사 할 것이라고 기대합니다.
환경 (해시 맵과 같은 것)을 사용해 보거나 R에서이 작업을 정말로 수행하려면 전문가 패키지가 필요할 수 있습니다. 여기 에 몇 가지 옵션이있는 답변에 대한 링크 가 있습니다.
기준
library(data.table)
Sys.setenv('R_MAX_VSIZE'=32000000000) # add to the ram limit
setDTthreads(threads=0) # use maximum threads possible
nbstates <- 1e3
nbactions <- 1e5
cartesian <- function(nbstates,nbactions){
x= data.table(qstate=1:nbactions)
y= data.table(qaction=1:nbstates)
k = NULL
x = x[, c(k=1, .SD)]
setkey(x, k)
y = y[, c(k=1, .SD)]
setkey(y, NULL)
x[y, allow.cartesian=TRUE][, c("k", "qvalue") := list(NULL, NA_real_)][]
}
#comparing seq with `:`
(bench = microbenchmark::microbenchmark(
1:1e9,
seq(1e9),
times=1000L
))
#> Unit: nanoseconds
#> expr min lq mean median uq max neval
#> 1:1e+09 120 143 176.264 156.0 201 5097 1000
#> seq(1e+09) 3039 3165 3333.339 3242.5 3371 21648 1000
ggplot2::autoplot(bench)
(bench = microbenchmark::microbenchmark(
"Cartesian product" = cartesian(nbstates,nbactions),
"data.table assignement"=qvalues <- data.table(qstate = rep(seq(nbstates), each = nbactions),
qaction = rep(seq(nbactions), times = nbstates),
qvalue = NA_real_,
stringsAsFactors = FALSE),
times=100L))
#> Unit: seconds
#> expr min lq mean median uq max neval
#> Cartesian product 3.181805 3.690535 4.093756 3.992223 4.306766 7.662306 100
#> data.table assignement 5.207858 5.554164 5.965930 5.895183 6.279175 7.670521 100
#> data.table (1:nb) 5.006773 5.609738 5.828659 5.80034 5.979303 6.727074 100
#>
#>
ggplot2::autoplot(bench)
을 seq
호출하는 것보다 사용 이 더 많은 시간 을 소비 한다는 것은 분명 합니다 1:nb
. 또한 데카르트 곱을 사용하면 1:nb
사용되는 경우에도 코드가 더 빨라집니다.