Tôi đã tìm kiếm cách kinh điển để thực hiện những gì tôi đang cố gắng nhưng dường như tôi có chút may mắn khi làm việc gì đó nhanh và thanh lịch. Tóm lại, tôi có một bảng lớn với nhiều cột giá trị và muốn nhân mỗi cột với một yếu tố tương ứng từ một bảng tra cứu. Tôi không thể tìm ra cách tự động chuyển vào cột nào mà tôi muốn nhân với giá trị tra cứu hoặc cách tham khảo giá trị tra cứu nói chung bên ngoài các biểu thức cơ bản. Đây là ví dụ của tôi, tôi đã thiết lập 3 triệu hàng với 10 cột giá trị, điều này không mất nhiều thời gian và phần nào đại diện cho kích thước dữ liệu (điều này sẽ được triển khai như một phần của vòng lặp lớn hơn nhiều). , do đó nhấn mạnh vào hiệu suất). Ngoài ra còn có một bảng tra cứu với 6 cấp độ và một số hệ số nhân cho các giá trị của chúng tôi_1: giá trị_10 cột.Fast data.table gán nhiều cột theo nhóm từ tra cứu
library(data.table)
setsize <- 3000000
value_num <- 10
factors <- c("factor_a", "factor_b", "factor_c", "factor_d", "factor_e", "factor_f")
random <- data.table(replicate(10, sample(factors, size = setsize, replace = T))
, replicate(10, rnorm(setsize, mean = 700, sd = 50)))
lookup <- data.table("V1" = factors, replicate(10, seq(.90, 1.5, length.out = length(factors))))
wps <- paste("value", c(1:10), sep = "_")
names(random)[11:20] <- wps
names(lookup)[2:11] <- wps
setkeyv(random, "V1")
setkeyv(lookup, "V1")
Giải pháp 1: Nó là khá nhanh nhưng tôi không thể tìm ra cách để quát tham khảo các i-cột như i.value_1
vì vậy tôi có thể chuyển chúng vào một vòng lặp hoặc tốt hơn chưa áp dụng chúng cùng một lúc.
f <- function() {
random[lookup, value_1 := value_1 * i.value_1, by = .EACHI]
random[lookup, value_2 := value_2 * i.value_2, by = .EACHI]
random[lookup, value_3 := value_3 * i.value_3, by = .EACHI]
random[lookup, value_4 := value_4 * i.value_4, by = .EACHI]
random[lookup, value_5 := value_5 * i.value_5, by = .EACHI]
random[lookup, value_6 := value_6 * i.value_6, by = .EACHI]
random[lookup, value_7 := value_7 * i.value_7, by = .EACHI]
random[lookup, value_8 := value_8 * i.value_8, by = .EACHI]
random[lookup, value_9 := value_9 * i.value_9, by = .EACHI]
random[lookup, value_10 := value_10 * i.value_10, by = .EACHI]
}
system.time(f())
user system elapsed
0.184 0.000 0.181
Giải pháp 2: Sau khi tôi không thể nhận được giải pháp 1 là chung, tôi đã thử phương pháp tiếp cận dựa trên set()
. Tuy nhiên, mặc dù cho phép tôi chỉ định các cột giá trị được nhắm mục tiêu trong vector ký tự wps
, nhưng nó thực sự chậm hơn nhiều so với ở trên. Tôi biết tôi đang sử dụng nó sai nhưng tôi không chắc chắn làm thế nào để cải thiện nó để loại bỏ tất cả các [.data.table overhead.
idx_groups <- random[,.(rowstart = min(.I), rowend = max(.I)), by = key(random)][lookup]
system.time(
for (i in 1:nrow(idx_groups)){
rows <- idx_groups[["rowstart"]][i]:idx_groups[["rowend"]][i]
for (j in wps) {
set(random, i=rows, j=j, value= random[rows][[j]] * idx_groups[[j]][i])
}
})
user system elapsed
3.940 0.024 3.967
Bất kỳ lời khuyên nào về cách cấu trúc tốt hơn các hoạt động này sẽ được đánh giá cao.
Edit: Tôi rất thất vọng với bản thân mình vì đã không cố gắng giải pháp rõ ràng điều này trước khi đăng câu hỏi này:
system.time(
for (col in wps){
random[lookup, (col) := list(get(col) * get(paste0("i.", col))), by = .EACHI, with = F]
})
user system elapsed
1.600 0.048 1.652
mà dường như làm những gì tôi muốn với tốc độ tương đối. Tuy nhiên nó vẫn còn chậm hơn 10 lần so với giải pháp đầu tiên ở trên (tôi chắc chắn do lặp đi lặp lại get()
) vì vậy tôi vẫn mở lời khuyên.
Chỉnh sửa 2: Thay thế get()
bằng eval(parse(text=col))
dường như đã thực hiện thủ thuật.
system.time(
for (col in wps){
random[lookup, (col) := list(eval(parse(text=col)) * eval(parse(text=paste0("i.", col)))), by = .EACHI, with = F]
})
user system elapsed
0.184 0.000 0.185
Chỉnh sửa 3: Một số câu trả lời làm việc tốt đã được cung cấp. Giải pháp của Rafael có lẽ là tốt nhất trong trường hợp chung, mặc dù tôi sẽ lưu ý rằng tôi có thể siết chặt thêm vài phần nghìn giây trong quá trình xây dựng cuộc gọi do Jangorecki đề xuất để đổi lấy chức năng trợ giúp tìm kiếm khá đáng sợ. Tôi đã đánh dấu nó là đã trả lời, cảm ơn sự giúp đỡ của mọi người.
tôi sẽ đoán sử dụng 'mget' thay vì' eval (parse (...)) 'nên đạt được kết quả tương tự (nhưng không thử nghiệm nó). Nếu bạn đã tự trả lời câu hỏi của mình, vui lòng đăng giải pháp đúng là "trả lời" (không phải là chỉnh sửa). THX :-) –
Câu hỏi này có thể hữu ích: https://stackoverflow.com/questions/30468455/dynamically-build-call-for-lookup-multiple-columns - bạn có thể thử giải pháp mới nhất từ đó, tôi tin rằng nó nên là hiệu quả nhất, vì tránh phân tích cú pháp và các trường vật chất hóa từ 'get'. Hãy tự trả lời nếu bạn tìm thấy giải pháp nhanh hơn/tốt hơn sau đó hiện được cung cấp. – jangorecki
R Yoda, thật không may 'mget' không hoạt động đối với tôi như' eval (parse (...)) '. Jangorecki, cảm ơn vì liên kết! Tôi nghĩ giải pháp cuối cùng của bạn là nhanh nhất mà tôi đã thấy và khả năng kiểm tra biểu thức J làm cho nó trực quan hơn một chút về những gì đang diễn ra. Tôi sẽ đăng phiên bản của tôi của nó như là câu trả lời cho câu hỏi này. – etrippler