Remodeler la base de données à trois colonnes en une matrice (format" long "à" large"))

j'ai un data.frame qui ressemble à ceci.

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

je veux ceci sous forme de matrice pour que je puisse l'alimenter à heatmap pour faire un tracé. Le résultat devrait ressembler à quelque chose comme:

    a    b    c
x   1    2    3
y   3    3    2

j'ai essayé cast à partir du paquet reshape et j'ai essayé d'écrire une fonction manuelle pour le faire mais je ne semble pas être en mesure de le faire correctement.

106
demandé sur majom 2012-03-08 16:03:00

4 réponses

il y a plusieurs façons de le faire. Cette réponse commence par mes façons préférées, mais recueille également de diverses façons à partir de réponses à des questions similaires dispersées autour de ce 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))

à l'Aide de reshape2:

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

utilisant l'indexation matricielle:

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

par xtabs :

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

vous pouvez également utiliser reshape , comme suggéré ici: table de conversion dans matrix by column names , bien que vous ayez à faire une petite manipulation par la suite pour enlever une colonne supplémentaire et obtenir les bons noms (Non montré).

> 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

il y a aussi sparseMatrix dans le paquet Matrix , comme on le voit ici: R - convertissez la grande table en matrice par les noms de colonne

> 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

la fonction daply de la bibliothèque plyr pourrait également être utilisée, comme ici: 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 de reshape2 fonctionne également, comme ici: données de Reshape pour les valeurs dans une colonne , mais vous obtenez une donnée.cadre avec colonne pour la valeur x .

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

de même, spread de "tidyr" travaillerait aussi pour une telle transformation:

library(tidyr)
spread(tmp, y, z)
#   x a b c
# 1 x 1 2 3
# 2 y 3 3 2
156
répondu Aaron 2017-05-23 11:54:50

la question Est vieille de quelques années mais peut-être que certaines personnes sont encore intéressées par des réponses alternatives.

si vous ne voulez pas charger de paquets, vous pouvez utiliser cette fonction:

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

}

Comment cela fonctionne:

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
2
répondu daniel.neumann 2017-08-29 18:29:16

le paquet tidyr de la tidyverse a une excellente fonction qui fait ceci.

en supposant que vos variables sont appelées v1, v2 et v3, de gauche à droite, et que votre base de données est appelée dat:

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

Ta da!

0
répondu Ahsen Omar Majid 2018-08-12 19:38:19

de la base de R, unstack

unstack(df, V3 ~ V2)
#   a b c
# 1 1 2 3
# 2 3 3 2

cette solution n'est peut-être pas générale, mais elle fonctionne bien dans ce cas.

données

df<-structure(list(V1 = structure(c(1L, 1L, 1L, 2L, 2L, 2L), .Label = c("x", 
"y"), class = "factor"), V2 = structure(c(1L, 2L, 3L, 1L, 2L, 
3L), .Label = c("a", "b", "c"), class = "factor"), V3 = c(1L, 
2L, 3L, 3L, 3L, 2L)), .Names = c("V1", "V2", "V3"), class = "data.frame", row.names = c(NA, 
-6L))
0
répondu lebatsnok 2018-10-05 20:11:50