Risagoma tre frame dati da colonna a matrice (formato “da lungo” a “largo”)

Ho un data.frame che assomiglia a questo.

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

Voglio questo in forma di matrice in modo che possa nutrirlo per heatmap per fare una trama. Il risultato dovrebbe assomigliare a qualcosa:

  abc x 1 2 3 y 3 3 2 

Ho provato il cast dal pacchetto reshape e ho provato a scrivere una funzione manuale per farlo, ma non riesco a farlo bene.

    Ci sono molti modi per farlo. Questa risposta inizia con i miei metodi preferiti, ma raccoglie anche vari modi dalle risposte a domande simili sparse per questo sito.

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

    Utilizzando reshape2:

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

    Utilizzo dell'indicizzazione della matrice:

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

    Usare xtabs :

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

    Puoi anche usare reshape , come suggerito qui: Converti tabella in matrice per nome di colonna , anche se devi fare una piccola manipolazione in seguito per rimuovere una colonna in più e ottenere i nomi corretti (non mostrati).

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

    C'è anche sparseMatrix nel pacchetto Matrix , come visto qui: R - converti la tabella BIG in matrice per nome di colonna

     > 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 

    È anche ansible utilizzare la funzione plyr libreria plyr , come qui: 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 da reshape2 funziona anche, come qui: Risagoma i dati per i valori in una colonna , ma ottieni un data.frame con una colonna per il valore x .

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

    Allo stesso modo, la spread da "tidyr" funzionerebbe anche per una tale trasformazione:

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

    La domanda è vecchia di alcuni anni, ma forse alcune persone sono ancora interessate a risposte alternative.

    Se non si desidera caricare alcun pacchetto, è ansible utilizzare questa funzione:

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

    Come funziona:

     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 

    Il pacchetto tidyr del tidyverse ha una funzione eccellente che fa questo.

    Supponendo che le tue variabili siano nominate v1, v2 e v3, da sinistra a destra, e il tuo data frame è denominato dat:

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

    Ta da!