2016-05-24 27 views
5

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.

+0

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 :-) –

+1

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

+0

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

Trả lời

4

Bạn cũng có thể sử dụng lapply:

cols <- noquote(paste0("value_",1:10)) 

random[lookup, (cols) := lapply (cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ] 

Trong trường hợp dữ liệu của bạn quá lớn và bạn muốn nhìn thấy một thanh tiến trình hoạt động, bạn có thể sử dụng pblapply:

library(pbapply) 

random[lookup, (cols) := pblapply(cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ] 
+0

Chỉ chậm hơn vài phần nghìn giây so với cấu trúc cuộc gọi và tránh sự cần thiết của chức năng trợ giúp, vì vậy cảm ơn bạn đã trả lời. Tôi tò mò tại sao bạn lại đưa 'với = F', có vẻ như nó không cần thiết và không ảnh hưởng đến thời gian? – etrippler

+0

@etrippler, tôi rất vui được giúp đỡ. Bạn đúng, không cần sử dụng 'with = F'. [Đây là giải thích về thời điểm sử dụng] (https://rawgit.com/wiki/Rdatatable/data.table/vignettes/datatable-intro.html) –

+1

Sử dụng 'Bản đồ' ở đây đẹp hơn nhiều:' (cols) : = Bản đồ (\ '* \', mget (cols), mget (icols)) 'trong đó' icols = paste0 ("i.", Cols) '. – Arun

2

Nhờ jangorecki để chỉ ra câu trả lời của mình here, nó tự động xây dựng biểu thức J bằng cách sử dụng hàm trợ giúp và sau đó đánh giá tất cả cùng một lúc. Nó tránh được chi phí phân tích/nhận và dường như là giải pháp nhanh nhất mà tôi sẽ nhận được.Tôi cũng giống như khả năng chỉ định thủ công hàm được gọi (một số trường hợp tôi có thể muốn / thay vì *) và kiểm tra biểu thức J trước khi nó được đánh giá.

batch.lookup = function(x) { 
    as.call(list(as.name(":="),x 
       ,as.call(c(
       list(as.name("list")), 
       sapply(x, function(x) call("*", as.name(x), as.name(paste0("i.",x))), simplify=FALSE) 
       )) 
)) 
} 

print(batch.lookup(wps)) 

`:=`(c("value_1", "value_2", "value_3", "value_4", "value_5", 
"value_6", "value_7", "value_8", "value_9", "value_10"), list(value_1 = value_1 * 
    i.value_1, value_2 = value_2 * i.value_2, value_3 = value_3 * 
    i.value_3, value_4 = value_4 * i.value_4, value_5 = value_5 * 
    i.value_5, value_6 = value_6 * i.value_6, value_7 = value_7 * 
    i.value_7, value_8 = value_8 * i.value_8, value_9 = value_9 * 
    i.value_9, value_10 = value_10 * i.value_10)) 

system.time(
    random[lookup, eval(batch.lookup(wps)), by = .EACHI]) 

    user system elapsed 
    0.14 0.04 0.18 
3

Đây là khoảng 2x chậm hơn so với văn bản xây dựng phân tích cú pháp/cuộc gọi, nhưng là một chút dễ đọc hơn:

random[lookup, (wps) := Map('*', mget(wps), mget(paste0('i.', wps))), by = .EACHI] 
Các vấn đề liên quan