Remodele o quadro de dados de três colunas para a matriz (formato “longo” para “amplo”)

Eu tenho um data.frame que se parece com isso.

 xa 1 xb 2 xc 3 ya 3 yb 3 yc 2 

Eu quero isso em forma de matriz para que eu possa alimentá-lo ao mapa de calor para fazer um gráfico. O resultado deve ser algo como:

  abc x 1 2 3 y 3 3 2 

Eu tentei cast partir do pacote de remodelação e tentei escrever uma function manual para fazer isso, mas eu não pareço ser capaz de acertar.

Existem diversas formas de fazer isto. Esta resposta começa com as minhas formas favoritas, mas também recolhe várias formas de respostas a perguntas semelhantes espalhadas por este site.

 tmp < - data.frame(x=gl(2,3, labels=letters[24:25]), y=gl(3,1,6, labels=letters[1:3]), z=c(1,2,3,3,3,2)) 

Usando o reshape2:

 library(reshape2) acast(tmp, x~y, value.var="z") 

Usando indexação matricial:

 with(tmp, { out < - matrix(nrow=nlevels(x), ncol=nlevels(y), dimnames=list(levels(x), levels(y))) out[cbind(x, y)] <- z out }) 

Usando xtabs :

 xtabs(z~x+y, data=tmp) 

Você também pode usar o reshape , como sugerido aqui: Converter tabela em nomes de colunas por matriz , embora você tenha que fazer um pouco de manipulação depois para remover uma coluna extra e obter os nomes corretos (não mostrados).

 > reshape(tmp, idvar="x", timevar="y", direction="wide") x za zb zc 1 x 1 2 3 4 y 3 3 2 

Há também sparseMatrix dentro do pacote Matrix , como visto aqui: R - converte a tabela BIG em matriz por nomes de colunas

 > with(tmp, sparseMatrix(i = as.numeric(x), j=as.numeric(y), x=z, + dimnames=list(levels(x), levels(y)))) 2 x 3 sparse Matrix of class "dgCMatrix" abc x 1 2 3 y 3 3 2 

A function plyr biblioteca plyr também pode ser usada, como aqui: https://stackoverflow.com/a/7020101/210673

 > library(plyr) > daply(tmp, .(x, y), function(x) x$z) y xabc x 1 2 3 y 3 3 2 

dcast de reshape2 também funciona, como aqui: Remodele os dados para valores em uma coluna , mas você obtém um data.frame com uma coluna para o valor x .

 > dcast(tmp, x~y, value.var="z") xabc 1 x 1 2 3 2 y 3 3 2 

Da mesma forma, a spread de "tidyr" também funcionaria para tal transformação:

 library(tidyr) spread(tmp, y, z) # xabc # 1 x 1 2 3 # 2 y 3 3 2 

A questão tem alguns anos, mas talvez algumas pessoas ainda estejam interessadas em respostas alternativas.

Se você não quiser carregar nenhum pacote, use esta function:

 #' Converts three columns of a data.frame into a matrix -- eg to plot #' the data via image() later on. Two of the columns form the row and #' col dimensions of the matrix. The third column provides values for #' the matrix. #' #' @param data data.frame: input data #' @param rowtitle string: row-dimension; name of the column in data, which distinct values should be used as row names in the output matrix #' @param coltitle string: col-dimension; name of the column in data, which distinct values should be used as column names in the output matrix #' @param datatitle string: name of the column in data, which values should be filled into the output matrix #' @param rowdecreasing logical: should the row names be in ascending (FALSE) or in descending (TRUE) order? #' @param coldecreasing logical: should the col names be in ascending (FALSE) or in descending (TRUE) order? #' @param default_value numeric: default value of matrix entries if no value exists in data.frame for the entries #' @return matrix: matrix containing values of data[[datatitle]] with rownames data[[rowtitle]] and colnames data[coltitle] #' @author Daniel Neumann #' @date 2017-08-29 data.frame2matrix = function(data, rowtitle, coltitle, datatitle, rowdecreasing = FALSE, coldecreasing = FALSE, default_value = NA) { # check, whether titles exist as columns names in the data.frame data if ( (!(rowtitle%in%names(data))) || (!(coltitle%in%names(data))) || (!(datatitle%in%names(data))) ) { stop('data.frame2matrix: bad row-, col-, or datatitle.') } # get number of rows in data ndata = dim(data)[1] # extract rownames and colnames for the matrix from the data.frame rownames = sort(unique(data[[rowtitle]]), decreasing = rowdecreasing) nrows = length(rownames) colnames = sort(unique(data[[coltitle]]), decreasing = coldecreasing) ncols = length(colnames) # initialize the matrix out_matrix = matrix(NA, nrow = nrows, ncol = ncols, dimnames=list(rownames, colnames)) # iterate rows of data for (i1 in 1:ndata) { # get matrix-row and matrix-column indices for the current data-row iR = which(rownames==data[[rowtitle]][i1]) iC = which(colnames==data[[coltitle]][i1]) # throw an error if the matrix entry (iR,iC) is already filled. if (!is.na(out_matrix[iR, iC])) stop('data.frame2matrix: double entry in data.frame') out_matrix[iR, iC] = data[[datatitle]][i1] } # set empty matrix entries to the default value out_matrix[is.na(out_matrix)] = default_value # return matrix return(out_matrix) } 

Como funciona:

 myData = as.data.frame(list('dim1'=c('x', 'x', 'x', 'y','y','y'), 'dim2'=c('a','b','c','a','b','c'), 'values'=c(1,2,3,3,3,2))) myMatrix = data.frame2matrix(myData, 'dim1', 'dim2', 'values') myMatrix > abc > x 1 2 3 > y 3 3 2 

O pacote tidyr do tidyverse tem uma excelente function que faz isso.

Supondo que suas variables ​​sejam nomeadas v1, v2 e v3, da esquerda para a direita, e seu quadro de dados é denominado dat:

 dat %>% spread(key = v2, value = v3) 

Ta da!