2013-02-21 3 views
3

В этом проблема. Существует матрица с N строк и столбцов С, а два фактора: ids и group, обе длины N. Например:Перестройте матрицу в R, используя два фактора:

m <- matrix(1:25, nrow= 5, byrow= T) 
id <- factor(c("A", "A", "A", "B", "B")) 
group <- factor(c("a", "b", "c", "a", "c")) 

Не все комбинации факторов присутствуют, но каждая комбинация факторов присутствует только один раз. Задача состоит в том, чтобы преобразовать матрицу m таким образом, чтобы она имела length(levels(id)) строк и length(levels(group)) * C столбцов. Другими словами, создайте матрицу, где каждая переменная соответствует комбинации между исходным столбцом и всеми возможными уровнями фактора group. Отсутствующие значения (для несуществующих комбинаций id и группы) заменяются NA. Вот требуемый выход из приведенного выше примера:

a.1 a.2 a.3 a.4 a.5 b.1 b.2 b.3 b.4 b.5 c.1 c.2 c.3 c.4 c.5 
A 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
B 16 17 18 19 20 NA NA NA NA NA 21 22 23 24 25 

я написал свою собственную функцию, но это очень неэффективно, и я уверен, что он дублирует функциональность что-то очень простое.

matrixReshuffle <- function(m, ids.row, factor.group) { 

    nr <- nrow(m) 
    nc <- ncol(m) 
    if(is.null(colnames(m))) colnames(m) <- 1:nc 

    ret <- NULL 
    for(id in levels(ids.row)) { 

    r <- c() 

    for(fg in levels(factor.group)) { 

     d <- m[ ids.row == id & factor.group == fg,, drop= F ] 
     if(nrow(d) > 1) 
     stop(sprintf("Too many matches for ids.row= %s and factor.group= %s", id, fg)) 
     else if(nrow(d) < 1) { 
     r <- c(r, rep(NA, nc)) 
     } else { 
     r <- c(r, d[1,]) 
     } 

    } 

    ret <- rbind(ret, r) 

    } 

    colnames(ret) <- paste(rep(levels(factor.group), each= nc), rep(colnames(m), length(levels(factor.group))), sep= ".") 
    rownames(ret) <- levels(ids.row) 

    return(ret) 
} 

ответ

2

После @ предложений Аарону:

Использование melt и acast из reshape2:

require(reshape2) 
df <- as.data.frame(m) 
names(df) <- seq_len(ncol(df)) 
df.m <- melt(df) 
df.m$id <- rep(id, nrow(df.m)/length(id)) 
df.m$group <- rep(group, nrow(df.m)/length(group)) 

o <- acast(df.m, id ~ group+variable, value.var="value") 
colnames(o) <- sub("_", ".", colnames(o)) 

# a.1 a.2 a.3 a.4 a.5 b.1 b.2 b.3 b.4 b.5 c.1 c.2 c.3 c.4 c.5 
# A 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
# B 16 17 18 19 20 NA NA NA NA NA 21 22 23 24 25 

Вы можете преобразовать это обратно в матрицу.

+0

+1 Отличное решение; как я сказал в своем комментарии к собственному ответу, это то, что я бы предпочел. Вы можете рассмотреть 'acast', чтобы дать матрицу напрямую, и чтобы имена соответствовали, возможно, имена (df) <- 1: 5' перед таянием и' colnames (out) <- sub ("_", ". ", colnames (out))' после кастинга (где 'out' является результатом литья). – Aaron

+0

@ Аарон, да, я должен был использовать 'acast'. Спасибо за это. Я хотел дать общую идею и оставить остальную часть редактирования OP. Но теперь, после вашего комментария, я отредактировал его, чтобы он был полным. – Arun

+0

@ января, спасибо за редактирование. – Arun

2

Для всех матричных индексации вентиляторов там ...

C <- ncol(m) 
to.row <- matrix(rep(as.numeric(id), C), ncol=C) 
to.col <- sweep(col(m),1,(as.numeric(group)-1)*C,`+`) 
out <- array(dim=c(nlevels(id), nlevels(group)*C), 
      dimnames=list(levels(id), as.vector(t(outer(levels(group), 1:C, paste, sep="."))))) 
out[cbind(as.vector(to.row), as.vector(to.col))] <- m 
out 
# a.1 a.2 a.3 a.4 a.5 b.1 b.2 b.3 b.4 b.5 c.1 c.2 c.3 c.4 c.5 
# A 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
# B 16 17 18 19 20 NA NA NA NA NA 21 22 23 24 25 
+1

Я действительно ценю чек, но, честно говоря, решение @ Arun с использованием 'reshape2' - это то, что я бы предпочел; это более простое решение, и идеи легче обобщить на другие потребности в перестройке. – Aaron

1

Это вариант @ ответ Аруна, слегка модифицирована таким образом, что проще (для меня), чтобы понять. Кроме того, я всегда опасаюсь репликации групповых факторов; Я обнаружил, что на практике это один из потенциальных источников систематической ошибки. Лучше прямо взять на себя идентификатор и группу и позволить расплавить() выполнять работу по репликации факторов. Но это всего лишь незначительные вещи.

# add the aggregating variables to the matrix, converted to data frame 
df <- data.frame(m) 
df$id <- id 
df$group <- group 

# reshape the data frame 
require(reshape2) 
df.m <- melt(df, c("id", "group")) 
df <- dcast(df.m, id ~ group + variable) 

# df has the required shape, but convert it back to a matrix 
rownames(df) <- df$id 
df$id <- NULL 
m.reshaped <- as.matrix(df) 
Смежные вопросы