2014-10-14 3 views
2

Извините, если это покажется тривиальным, но после некоторого поиска в Интернете я не смог найти решение.R: Есть ли простой способ сделать работу ave() для матриц?

У меня есть матрица и фактор-фактор, связанные с столбцами. Цель состоит в том, чтобы получить rowMeans для всех факторов отдельно и поддерживать исходную структуру матрицы. Вероятно, это было бы что-то вроде ave(), но работало бы над 2-мерными массивами.

Вот грубая демонстрация:

(mat <- rbind(1:5,6:10,11:15)) 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 1 2 3 4 5 
[2,] 6 7 8 9 10 
[3,] 11 12 13 14 15 

groups <- c(1,1,1,2,2) 

mat[,groups==1] <- rowMeans(mat[,groups==1]) # I am asking about this part 
mat[,groups==2] <- rowMeans(mat[,groups==2]) # ... 

mat 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 2 2 2 4.5 4.5 
[2,] 7 7 7 9.5 9.5 
[3,] 12 12 12 14.5 14.5 

На практике эта матрица будет иметь миллионы строк (и меньше столбцов). Поэтому решения, которые работают по очереди, могут быть слишком медленными.

Я собираюсь написать свою собственную функцию, но это похоже на то, что может иметь легкое однострочное решение.

ответ

2

Может быть, как это:

mat.list <- Map(matrix, split(mat, groups[col(mat)]), nrow = nrow(mat)) 
mean.list <- Map(rowMeans, mat.list) 
do.call(cbind, mean.list[groups]) 

Или для большей скорости:

idx.list <- split(seq_len(ncol(mat)), groups) 
get.cols <- function(mat, idx) mat[, idx, drop = FALSE] 
mat.list <- lapply(idx.list, get.cols, mat = mat) 
mean.list <- lapply(mat.list, rowMeans) 
do.call(cbind, mean.list[groups]) 
+0

Я с радостью принимаю ваш ответ. Это правильно и дал мне несколько новых идей. Однако я чувствую, что в цикле было бы легче понять в этом случае :) На самом деле я думал, что у меня отсутствует базовая функция с одним словом. –

3

1) Предполагая, что вы хотите заменить каждый элемент каждой строки со средним значением этой строки, попробуйте это где m ваша матрица:

ave(m, row(m)) 

Если это не то, что вы хотите, пожалуйста, укажите полный пример, включающий входные и выходные данные.

2) Для обновленного вопроса попробуйте следующее:

t(ave(t(m), group, t(row(m)))) 

или это эквивалентное изменение:

ave(m, matrix(group, nrow(m), ncol(m), byrow = TRUE), row(m)) 
+0

Я чувствую, что второй пример близок. Но он не воспроизводит желаемый результат в моих руках. т. е. получается мат [2,]: (6,0 7,5 7,5 9,0 10,0). Должно быть (7 7 7 9,5 9,5) –

+0

Хорошая точка. См. Ревизию. –

+0

Интересный подход. Выучил некоторые вещи от вас сегодня. (не знал о строках() или col()). –

1

Было бы неплохо, если бы была оптимизированная функция для этого, что-то вроде rowGroupMeans, но я не знаю об этом.

Мое решение использовать rowsum следующим образом:

means <- rowsum(t(mat), groups)/tabulate(groups) 
t(means)[, groups] 

     1 1 1 2 2 
[1,] 2 2 2 4.5 4.5 
[2,] 7 7 7 9.5 9.5 
[3,] 12 12 12 14.5 14.5 

Это весы очень хорошо к большим проблемам, например,

mat <- matrix(1:100e6, ncol = 100) 
groups <- rep(1:10, each = 10) 

## Map solution 
for (i in 1:3){ 
    print(system.time({ 
     mat.list <- Map(matrix, split(mat, groups[col(mat)]), nrow = nrow(mat)) 
     mean.list <- Map(rowMeans, mat.list) 
     ans1 <- do.call(cbind, mean.list[groups]) 
    })) 
} 

    user system elapsed 
    8.20 1.26 9.66 
    user system elapsed 
    11.84 1.94 13.90 
    user system elapsed 
    10.70 1.89 12.79 

## rowsum solution 
for (i in 1:3){ 
    print(system.time({ 
     means <- rowsum(t(mat), groups)/tabulate(groups) 
     ans2 <- t(means)[,groups] 
    })) 
} 

    user system elapsed 
    1.56 0.22 1.78 
    user system elapsed 
    1.48 0.27 1.74 
    user system elapsed 
    1.57 0.14 1.72 

Как уже отмечено ave решение не масштабируется - моя R сессия разбился, когда я попытался запустить тайминги для этого.

+0

Спасибо за ответ. Ваше решение является самым элегантным для rowMeans и очень быстрым. Однако я использовал «rowMeans» в качестве примера (может быть, это было непонятно) и стремился к полному подходу типа ave(). То есть я хочу, чтобы в конце указать другие типы функций (например, rowMedians). Наверное, я напишу для себя какую-то обертку. Все ответы здесь дали мне несколько идей. Еще раз спасибо. –

Смежные вопросы