2014-06-18 18 views
7

Cách tốt nhất để làm thành phần khôn ngoan Ngoài ra nếu số lượng ma trận được tổng kết không được biết trước? Nói chung, có cách nào tốt để thực hiện các hoạt động ma trận (hoặc mảng đa chiều) trong ngữ cảnh của ? Tôi sử dụng data.table cho hiệu quả của nó khi phân loại và nhóm dữ liệu theo một số biến cố định hoặc danh mục, mỗi biến bao gồm một số quan sát khác nhau.hoạt động ma trận và bổ sung thành phần khôn ngoan bằng cách sử dụng data.table

Ví dụ:

  1. Tìm sản phẩm bên ngoài các thành phần vector được đưa ra trong mỗi quan sát (hàng) của dữ liệu, trở về một ma trận cho mỗi hàng.
  2. Tổng hợp ma trận kết quả thành thạo trên tất cả các hàng của từng nhóm danh mục dữ liệu.

đây minh họa bằng các ma trận 2x2 và chỉ có một loại:

library(data.table) 

# example data, number of rows differs by category t 
N <- 5 
dt <- data.table(t = rep(c("a", "b"), each = 3, len = N), 
       x1 = rep(1:2, len = N), x2 = rep(3:5, len = N), 
       y1 = rep(1:3, len = N), y2 = rep(2:5, len = N)) 
setkey(dt, t) 
> dt 
    t x1 x2 y1 y2 
1: a 1 3 1 2 
2: a 2 4 2 3 
3: a 1 5 3 4 
4: b 2 3 1 5 
5: b 1 4 2 2 

Tôi đã cố gắng một hàm để tính toán ma trận tổng trên sản phẩm bên ngoài, %o%

mat_sum <- function(x1, x2, y1, y2){ 
    x <- c(x1, x2) # x vector 
    y <- c(y1, y2) # y vector 
    xy <- x %o% y # outer product (i.e. 2x2 matrix) 
    sum(xy) # <<< THIS RETURNS A SINGLE VALUE, NOT WHAT I WANT. 
    } 

đó, tất nhiên, không làm việc vì sum thêm tất cả các phần tử trên các mảng.

Tôi đã thấy this answer sử dụng Reduce('+', .list) nhưng điều đó dường như yêu cầu phải có một list của tất cả ma trận được thêm vào. Tôi chưa tìm ra cách để làm điều đó trong vòng data.table, vì vậy thay vào đó tôi đã có một cồng kềnh công việc xung quanh:

# extract each outer product component first... 
mat_comps <- function(x1, x2, y1, y2){ 
    x <- c(x1, x2) # x vector 
    y <- c(y1, y2) # y vector 
    xy <- x %o% y # outer product (i.e. 2x2 matrix) 
    xy11 <- xy[1,1] 
    xy21 <- xy[2,1] 
    xy12 <- xy[1,2] 
    xy22 <- xy[2,2] 
    return(c(xy11, xy21, xy12, xy22)) 
} 

# ...then running this function on dt, 
# taking extra step (making column 'n') to apply it row-by-row... 
dt[, n := 1:nrow(dt)] 
dt[, c("xy11", "xy21", "xy12", "xy22") := as.list(mat_comps(x1, x2, y1, y2)), 
    by = n] 

# ...then sum them individually, now grouping by t 
s <- dt[, list(s11 = sum(xy11), 
       s21 = sum(xy21), 
       s12 = sum(xy12), 
       s22 = sum(xy22)), 
     by = key(dt)] 
> s 
    t s11 s21 s12 s22 
1: a 8 26 12 38 
2: b 4 11 12 23 

và cung cấp cho các thành phần tóm tắt, mà cuối cùng có thể được chuyển đổi trở lại ma trận.

+0

+1 Thật là một câu hỏi lớn đầu tiên. Chào mừng bạn đến với Stack Overflow. –

Trả lời

7

Nói chung, data.table được thiết kế để hoạt động với các cột. Bạn càng biến vấn đề của mình thành các hoạt động thông minh hơn, bạn càng có thể thoát ra khỏi data.table.

Đây là một nỗ lực để hoàn thành hoạt động này một cách khôn ngoan. Có lẽ có những cách tốt hơn. Đây là dự định nhiều hơn như một mẫu, để cung cấp một ý tưởng về tiếp cận vấn đề (mặc dù tôi hiểu rằng nó có thể không được có thể trong mọi trường hợp).

xcols <- grep("^x", names(dt)) 
ycols <- grep("^y", names(dt)) 
combs <- CJ(ycols, xcols) 
len <- seq_len(nrow(combs)) 
cols = paste("V", len, sep="") 
for (i in len) { 
    c1 = combs$V2[i] 
    c2 = combs$V1[i] 
    set(dt, i=NULL, j=cols[i], value = dt[[c1]] * dt[[c2]]) 
} 

# t x1 x2 y1 y2 V1 V2 V3 V4 
# 1: a 1 3 1 2 1 3 2 6 
# 2: a 2 4 2 3 4 8 6 12 
# 3: a 1 5 3 4 3 15 4 20 
# 4: b 2 3 1 5 2 3 10 15 
# 5: b 1 4 2 2 2 8 2 8 

Điều này về cơ bản áp dụng cho sản phẩm bên ngoài. Bây giờ nó chỉ là vấn đề tổng hợp nó.

dt[, lapply(.SD, sum), by=t, .SDcols=cols] 

# t V1 V2 V3 V4 
# 1: a 8 26 12 38 
# 2: b 4 11 12 23 

HTH


Edit: Modified cols, c1, c2 một chút để có được đầu ra theo trình tự chính xác cho V2V3.

+0

Nhiều khía cạnh hữu ích ở đây, đặc biệt là sử dụng 'CJ' và' .SD', nhưng cũng là 'seq',' grep' và các lệnh chuỗi khác mà tôi không quen thuộc lắm. Mẫu này mở rộng trực tiếp đến ma trận m-by-n, thuận tiện suy ra các kích thước từ xcols và ycols. Một câu hỏi là tại sao V2 và V3 bị đảo ngược – Scott

+0

Cảm ơn bạn đã chỉnh sửa, mặc dù tôi không muốn gọi đơn hàng gốc là "không chính xác". Đó là do các phần tử ma trận mặc định của R mà tôi liệt kê là 11, 21, 12, 22. Điều thú vị là 'CJ' trái ngược với hàng khôn ngoan, mà tôi thực sự thấy tự nhiên hơn. Có lẽ tôi sử dụng 'cols <- paste (" V ", lược $ V1, chải $ V2, sep =" ")' để giúp tôi theo dõi các chỉ số. – Scott

2

EDIT: Đối với không chỉ có 2 yếu tố trong "x" s và "y" s, một chức năng sửa đổi có thể là:

ff2 = function(x_ls, y_ls) 
{ 
    combs_ls = lapply(seq_along(x_ls[[1]]), 
        function(i) list(sapply(x_ls, "[[", i), 
             sapply(y_ls, "[[", i))) 
    rowSums(sapply(combs_ls, function(x) as.vector(do.call(outer, x)))) 
} 

ở đâu, "x_ls" và "y_ls" là danh sách các các vectơ tương ứng.

Sử dụng nó:

dt[, as.list(ff2(list(x1, x2), list(y1, y2))), by = t] 
# t V1 V2 V3 V4 
#1: a 8 26 12 38 
#2: b 4 11 12 23 

Và trên khác "data.frames/bảng":

set.seed(101) 
DF = data.frame(group = rep(letters[1:3], c(4, 2, 3)), 
       x1 = sample(1:20, 9, T), x2 = sample(1:20, 9, T), 
       x3 = sample(1:20, 9, T), x4 = sample(1:20, 9, T), 
       y1 = sample(1:20, 9, T), y2 = sample(1:20, 9, T), 
       y3 = sample(1:20, 9, T), y4 = sample(1:20, 9, T))    
DT = as.data.table(DF) 

DT[, as.list(ff2(list(x1, x2, x3, x4), 
       list(y1, y2, y3, y4))), by = group] 
# group V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 
#1:  a 338 661 457 378 551 616 652 468 460 773 536 519 416 766 442 532 
#2:  b 108 261 171 99 29 77 43 29 154 386 238 146 161 313 287 121 
#3:  c 345 351 432 293 401 421 425 475 492 558 621 502 510 408 479 492 

Tôi không biết, tuy nhiên, như thế nào sẽ là một trong "data.table" không nêu rõ các cột để sử dụng bên trong hàm; ví dụ: làm thế nào bạn có thể làm tương đương với:

do.call(rbind, lapply(split(DF[-1], DF$group), 
         function(x) 
          do.call(ff2, c(list(x[grep("^x", names(x))]), 
             list(x[grep("^y", names(x))]))))) 
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] 
#a 338 661 457 378 551 616 652 468 460 773 536 519 416 766 442 532 
#b 108 261 171 99 29 77 43 29 154 386 238 146 161 313 287 121 
#c 345 351 432 293 401 421 425 475 492 558 621 502 510 408 479 492 

ĐÁP OLD:

lẽ bạn có thể xác định chức năng của bạn như:

ff1 = function(x1, x2, y1, y2) 
    rowSums(sapply(seq_along(x1), 
        function(i) as.vector(c(x1[i], x2[i]) %o% c(y1[i], y2[i])))) 

dt[, as.list(ff1(x1, x2, y1, y2)), by = list(t)] 
# t V1 V2 V3 V4 
#1: a 8 26 12 38 
#2: b 4 11 12 23 
+0

Đây là thiết bị gọn gàng và gọn gàng. Người ta có thể chỉ cần điều chỉnh hàm (và đối số) để phù hợp với các tham số tùy ý của v và y vectơ. – Scott

+0

@Scott: Tôi vừa chỉnh sửa câu trả lời với một giải pháp thay thế, mặc dù tôi không chắc rằng nó hữu ích như thế nào –

Các vấn đề liên quan