2012-03-08 10 views
96

Mam numer data.frame, który wygląda tak.Przekształć ramkę danych trzy kolumny w macierz (format "długi" na "szeroki")

x a 1 
x b 2 
x c 3 
y a 3 
y b 3 
y c 2 

Chcę to w formie matrycy, więc mogę nakarmić go do mapy cieplnej, aby fabuła. Wynik powinien wyglądać mniej więcej tak:

a b c 
x 1 2 3 
y 3 3 2 

Próbowałem cast z pakietu przekształcenia i próbowałem pisać ręczną funkcję, aby to zrobić, ale nie wydaje się być w stanie uzyskać to prawo.

+1

@AnandaMahto ma również wielkie odpowiedzi na ten temat tutaj: http : //stackoverflow.com/a/14515736/210673 – Aaron

Odpowiedz

146

Istnieje wiele sposobów, aby to zrobić. Ta odpowiedź zaczyna się od moich ulubionych sposobów, ale zbiera różne sposoby odpowiedzi na podobne pytania rozproszone po tej stronie.

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

Korzystanie reshape2:

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

Korzystanie indeksowanie matrix:

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

Korzystanie xtabs:

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

Można również użyć reshape, jak sugeruje jej e: Convert table into matrix by column names, ale musisz później wykonać trochę manipulacji, aby usunąć dodatkowe kolumny i uzyskać poprawne nazwy (nie pokazano).

> reshape(tmp, idvar="x", timevar="y", direction="wide") 
    x z.a z.b z.c 
1 x 1 2 3 
4 y 3 3 2 

Jest też sparseMatrix wewnątrz opakowania Matrix, jak widać tutaj: R - convert BIG table into matrix by column names

> 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" 
    a b c 
x 1 2 3 
y 3 3 2 

daply funkcja z biblioteki plyr może być również używany, jak tutaj: https://stackoverflow.com/a/7020101/210673

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

dcast z reshape2 również działa, jak tutaj: Reshape data for values in one column, ale otrzymujesz data.frame z kolumną dla wartości x.

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

Podobnie spread z „tidyr” będzie również pracować dla takiej transformacji:

library(tidyr) 
spread(tmp, y, z) 
# x a b c 
# 1 x 1 2 3 
# 2 y 3 3 2 
+2

'acast (tmp, x ~ y, value.var =" z ")' da wynik macierzy, z 'x' jako row.names – mnel

2

Pytanie jest kilka lat, ale być może niektórzy ludzie są nadal zainteresowani alternatywnych odpowiedzi.

Jeśli nie chcesz, aby załadować żadnych pakietów, można użyć tej funkcji:

#' Converts three columns of a data.frame into a matrix -- e.g. 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) 

} 

Jak to działa:

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 
> a b c 
> x 1 2 3 
> y 3 3 2