2014-06-18 3 views
7

합계 수를 미리 알 수없는 경우 구성 요소 별 추가를 수행하는 가장 좋은 방법은 무엇입니까? 보다 일반적으로, 컨텍스트에서 행렬 (또는 다차원 배열) 연산을 수행하는 좋은 방법이 있습니까? 나는 여러 가지 고정 변수 또는 카테고리별로 데이터를 정렬하고 그룹화 할 때 효율성이 뛰어나므로 data.table을 사용합니다. 각각은 다른 수의 관측치로 구성됩니다. 예를 들어data.table을 사용하여 행렬 연산 및 구성 요소 별 추가

:

  1. 각 행의 매트릭스를 반환하는 데이터의 각 관찰 (행)에 주어진 벡터 성분의 외적을 찾는다.
  2. 데이터 카테고리의 각 그룹의 모든 행에 대해 결과 행렬을 구성 요소별로 합합니다. 물론,하지 않는, %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. 
        } 
    

    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 
    

    내가 외적을 행렬 합을 계산하는 기능을 시도 : 여기

는 2 × 2 행렬로 도시되고 하나의 카테고리 sum은 배열 전체의 모든 요소를 ​​더하기 때문에 작동합니다.

나는 Reduce('+', .list)을 사용하여 this answer을 보았지만, 추가 할 모든 행렬 중 이미 list이 필요합니다. 나는 data.table 내에서 그렇게하는 방법을 생각하지 않은, 그래서 대신 내가 성가신 해결 방법이있어 :

# 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 

그리고 마침내 행렬로 다시 변환 할 수있는 표현하는 구성 요소를 제공합니다.

+0

일이 얼마나 큰 첫 번째 질문. 스택 오버플로에 오신 것을 환영합니다. –

답변

7

일반적으로 data.table은 열과 함께 작동하도록 설계되었습니다. 문제를 열렬한 작업으로 전환할수록 더 벗어날 수 있습니다. data.table.

다음은이 작업을 한 줄씩 수행하려는 시도입니다. 아마 더 좋은 방법이있을 것입니다. 이는 문제 해결에 대한 아이디어를 제공하기 위해 템플릿으로 더 많은 것을 의도 한 것입니다. 모든 경우에 가능하지는 않을 수도 있음을 알고 있지만.

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 

이것은 기본적으로 외장 제품을 열에 적용합니다. 이제는 그것을 모으기 만하면됩니다.

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


편집 : cols, c1, c2 약간 수정은 V2V3 대한 정확한 순서로 출력 도착.

+0

여기 많은 유용한 측면, 특히'CJ'와'.SD'의 사용뿐만 아니라'seq','grep'과 다른 익숙한 문자열 명령들도 있습니다. 이 템플릿은 mx n 행렬로 직접 확장되어 xcols 및 ycols에서 차원을 편리하게 유추합니다. 하나의 질문은 V2와 V3이 반대가 된 이유입니다. – Scott

+0

편집 해 주셔서 감사합니다. 원본을 "잘못된"주문이라고하고 싶지는 않습니다. 그것은 11, 21, 12, 22에 열거 된 행렬 요소의 열에 대한 기본적으로 채우기 때문입니다. 흥미로운 점은 'CJ'가 행 방향으로 진행된다는 것인데, 실제로 더 자연 스럽습니다. 아마 인덱스를 추적하는 데'cols <- paste ("V", combs $ V1, combs $ V2, sep = "")'를 사용합니다. – Scott

2

EDIT "X"들 및 "Y"S에서 단지 2 개 소자 용 는 변형 함수가 될 수있다 :

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)))) 
} 

"x_ls"및 "y_ls"의리스트이다 각각의 벡터.

하면 사용 :

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 

그리고 다른 "data.frames/테이블"에

:

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 

는 나도 몰라,하지만 방법 "data.table"에있는 것 아니다 함수 내에서 사용할 열을 명시 적으로 명시하십시오. 즉, 당신이에 해당 할 수있는 방법은 다음과 같습니다

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 

OLD 답변 :

이 아마도 당신이 좋아하는 함수를 정의 할 수 있습니다 :

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

깨끗하고 컴팩트합니다. x와 y 벡터의 임의의 차원을 수용하기 위해 단순히 함수 (및 인수)를 조정해야 할 수도 있습니다. – Scott

+0

@ Scott : 해결 방법을 사용하여 답변을 편집 한 적이 있습니다. 유용 할 수있는 방법이 확실하지 않지만 –