R ความเร็วของข้อมูลตาราง
ฉันมีปัญหาด้านประสิทธิภาพที่เฉพาะเจาะจงซึ่งฉันต้องการขยายให้มากขึ้นโดยทั่วไปหากเป็นไปได้
บริบท:
ฉันเล่นบน google colab ด้วยตัวอย่างโค้ด python สำหรับเอเจนต์ Q-Learning ซึ่งเชื่อมโยงสถานะและการกระทำกับค่าโดยใช้ defaultdict:
self._qvalues = defaultdict(lambda: defaultdict(lambda: 0))
return self._qvalues[state][action]
ไม่ใช่ผู้เชี่ยวชาญ แต่ความเข้าใจของฉันคือส่งคืนค่าหรือเพิ่มและส่งคืน 0 หากไม่พบคีย์
ฉันกำลังปรับส่วนนี้ใน R
ปัญหาคือฉันไม่มีชุดค่าผสมของรัฐ / ค่าเท่าไหร่และในทางเทคนิคฉันไม่ควรรู้ว่าฉันเดากี่รัฐ
ในตอนแรกที่ผมเดินไปทางที่ผิดกับrbind
ของdata.frame
s และที่ช้ามาก
จากนั้นฉันก็แทนที่วัตถุ R ของฉันด้วยไฟล์data.frame(state, action, value = NA_real)
. มันใช้งานได้ แต่ก็ยังช้ามาก ปัญหาอื่นคือวัตถุ data.frame ของฉันมีขนาดสูงสุดซึ่งอาจเป็นปัญหาในอนาคต
จากนั้นฉันก็เปลี่ยน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 จะเข้าถึงวัตถุได้ 1,000x10e4 ในสมมติว่า 15 วินาทีในขณะที่รหัสของฉันทำการเข้าถึง 100x100 ใน 28 ฉันได้รับการปรับปรุง 2 วินาทีโดยการคอมไพล์ไบต์ แต่ก็ยังแย่เกินไป
การใช้profvis
ฉันเห็นว่าเวลาส่วนใหญ่ใช้ไปกับการเข้าถึง data.table ในการเรียกสองครั้งนี้:
qval <- self$qvalues[J(state, action), nomatch = NA_real_]$qvalue
self$qvalues[J(state, action)]$qvalue <- value
ฉันไม่รู้จริงๆว่า Google มีอะไรบ้าง แต่เดสก์ท็อปของฉันเป็นสัตว์ร้าย นอกจากนี้ฉันเห็นว่าเกณฑ์มาตรฐานบางอย่างระบุว่าdata.table
เร็วกว่าpandas
ดังนั้นฉันคิดว่าปัญหาอยู่ที่ตัวเลือกคอนเทนเนอร์ของฉัน
คำถาม:
- การใช้ data.table ของฉันผิดหรือไม่และสามารถแก้ไขเพื่อปรับปรุงและจับคู่การใช้งาน python ได้หรือไม่
- การออกแบบอื่นเป็นไปได้หรือไม่ที่จะหลีกเลี่ยงการประกาศชุดค่าผสมสถานะ / การกระทำทั้งหมดซึ่งอาจเป็นปัญหาได้หากมิติข้อมูลมีขนาดใหญ่เกินไป
- ฉันเคยเห็นเกี่ยวกับแพ็คเกจแฮชมันเป็นวิธีที่จะไปหรือไม่?
ขอบคุณมากสำหรับตัวชี้ใด ๆ !
อัปเดต:
ขอบคุณสำหรับทุกท่าน. สิ่งที่ฉันทำคือแทนที่ 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 วินาทีซึ่งเป็นการปรับปรุงครั้งใหญ่ แต่ก็ยังช้ามากเมื่อเทียบกับการ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
รวดเร็วสำหรับการค้นหาและปรับแต่งในตารางข้อมูลขนาดใหญ่ แต่การเพิ่มแถวทีละแถวจะไม่รวดเร็วเหมือนพจนานุกรม python ฉันคาดหวังว่ามันจะเป็นการคัดลอกทั้งตารางทุกครั้งที่คุณเพิ่มแถวซึ่งไม่ใช่สิ่งที่คุณต้องการอย่างชัดเจน
คุณสามารถลองใช้สภาพแวดล้อม (ซึ่งคล้ายกับแฮชแมป) หรือถ้าคุณต้องการทำใน 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
จะมีการใช้งานก็ตาม