J'ai un data.frame
ça ressemble à ça.
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 afin que je puisse le nourrir à 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 paquetage reshape et j'ai essayé d'écrire une fonction manuelle pour le faire, mais je ne semble pas être capable de le faire correctement.
Il y a plusieurs façons de le faire. Cette réponse commence par mes méthodes préférées, mais recueille également diverses méthodes à partir de réponses à des questions similaires disséminées sur 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))
Utilisation du tidyverse:
La nouvelle façon de procéder consiste à utiliser spread
de Tidyr. Il retourne une trame de données, ce qui est probablement ce que la plupart des lecteurs de cette réponse voudront. Pour une carte thermique, cependant, vous auriez besoin de convertir cela en une vraie matrice.
library(tidyr)
spread(tmp, y, z)
## x a b c
## 1 x 1 2 3
## 2 y 3 3 2
Utilisation de reshape2 :
Le paquetage reshape2 a été l’un des premiers pas vers l’aide de Tidyverse. Je pense toujours que pour de nombreuses tâches de refaçonnage, les fonctions melt
et *cast
Sont plus propres et plus simples que les méthodes simples.
Pour obtenir une matrice, utilisez acast
:
library(reshape2)
acast(tmp, x~y, value.var="z")
## a b c
## x 1 2 3
## y 3 3 2
Ou pour obtenir un cadre de données, utilisez dcast
, comme ici: Remodelez les données pour les valeurs dans une colonne .
dcast(tmp, x~y, value.var="z")
## x a b c
## 1 x 1 2 3
## 2 y 3 3 2
Utiliser plyr :
Entre reshape2 et tidyverse, plyr
, avec la fonction daply
, comme indiqué ici: https://stackoverflow.com/a/7020101/21067
library(plyr)
daply(tmp, .(x, y), function(x) x$z)
## y
## x a b c
## x 1 2 3
## y 3 3 2
Utilisation de l'indexation matricielle:
C’est un peu la vieille école, mais c’est une belle démonstration de l’indexation matricielle, qui peut être très utile dans certaines situations.
with(tmp, {
out <- matrix(nrow=nlevels(x), ncol=nlevels(y),
dimnames=list(levels(x), levels(y)))
out[cbind(x, y)] <- z
out
})
Utilisation de xtabs
:
xtabs(z~x+y, data=tmp)
En utilisant une matrice creuse:
Il y a aussi sparseMatrix
dans le paquet Matrix
, comme on le voit ici: R - convertir une table BIG en matrice par noms de colonnes
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
Utilisation de reshape
:
Vous pouvez également utiliser la fonction de base R reshape
, comme suggéré ici: Convertir un tableau en matrice par noms de colonnes , bien que vous deviez faire une petite manipulation par la suite pour supprimer des colonnes supplémentaires et obtenir les noms à droite (non représentés).
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
La question a quelques années mais peut-être que certaines personnes sont toujours intéressées par des réponses alternatives.
Si vous ne voulez pas charger de paquet, 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 ça marche:
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
Par souci d'exhaustivité, il existe une solution tapply()
autour.
with(d, tapply(z, list(x, y), sum))
# a b c
# x 1 2 3
# y 3 3 2
Données
d <- structure(list(x = structure(c(1L, 1L, 1L, 2L, 2L, 2L), .Label = c("x",
"y"), class = "factor"), y = structure(c(1L, 2L, 3L, 1L, 2L,
3L), .Label = c("a", "b", "c"), class = "factor"), z = c(1, 2,
3, 3, 3, 2)), class = "data.frame", row.names = c(NA, -6L))
unstack
unstack(df, V3 ~ V2)
# a b c
# 1 1 2 3
# 2 3 3 2
Cela peut ne pas être une solution générale mais fonctionne bien dans ce cas.
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))
À partir de tidyr 0.8.3.9000
, Une nouvelle fonction appelée pivot_wider()
est introduite. Il s’agit en fait d’une version améliorée de la précédente spread()
fonction (qui n’est d'ailleurs plus en développement actif) . De vignette pivotante :
Cette vignette décrit l'utilisation des nouvelles fonctions pivot_longer () et pivot_wider (). Leur objectif est d’améliorer la convivialité de collecte () et de propagation (), et d’incorporer des fonctionnalités de pointe trouvées dans d’autres packages.
Depuis quelque temps, il était évident qu’il y avait quelque chose de fondamentalement faux dans la conception de spread () et de apply (). Beaucoup de gens ne trouvent pas les noms intuitifs et ont du mal à se rappeler quelle direction correspond à la diffusion et quelle direction. Il semble également étonnamment difficile de retenir les arguments de ces fonctions, ce qui signifie que de nombreuses personnes (y compris moi!) Doivent consulter la documentation à chaque fois.
Comment l'utiliser (en utilisant les données de @Aaron):
pivot_wider(data = tmp, names_from = y, values_from = z)
Ou de manière "complète" tidyverse
:
tmp %>%
pivot_wider(names_from = y, values_from = z)
Le paquet tidyr de tidyverse a une excellente fonction pour ce faire.
En supposant que vos variables s'appellent v1, v2 et v3, de gauche à droite, et que votre cadre de données s'appelle dat:
dat %>%
spread(key = v2,
value = v3)
Ta da!