我有data.frame
看起来像这样。
x a 1
x b 2
x c 3
y a 3
y b 3
y c 2
我想用矩阵形式,所以我可以将它送到热图以制作情节。结果应该类似于:
a b c
x 1 2 3
y 3 3 2
我从reshape包中尝试了cast
,我尝试编写手动函数来执行此操作,但我似乎无法正确使用。
答案 0 :(得分:167)
有很多方法可以做到这一点。这个答案从我最喜欢的方式开始,但也收集各种方式从答案到分散在这个网站周围的类似问题。
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))
使用tidyverse:
这样做很酷的新方法是来自tidyr的spread
。它返回一个数据框,这可能是这个答案大多数读者想要的。但是,对于热图,您需要将其转换为真正的矩阵。
library(tidyr)
spread(tmp, y, z)
## x a b c
## 1 x 1 2 3
## 2 y 3 3 2
使用reshape2 :
tidyverse的第一步是reshape2包。我仍然认为,对于许多重塑任务,melt
和*cast
函数比tidyverse方式更清晰,更简单。
要使用矩阵acast
:
library(reshape2)
acast(tmp, x~y, value.var="z")
## a b c
## x 1 2 3
## y 3 3 2
要获取数据框,请使用dcast
,如下所示:Reshape data for values in one column。
dcast(tmp, x~y, value.var="z")
## x a b c
## 1 x 1 2 3
## 2 y 3 3 2
使用plyr :
在reshape2和tidyverse之间来plyr
,使用daply
函数,如下所示: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
使用矩阵索引:
这是一个古老的学校,但它是矩阵索引的一个很好的演示,在某些情况下非常有用。
with(tmp, {
out <- matrix(nrow=nlevels(x), ncol=nlevels(y),
dimnames=list(levels(x), levels(y)))
out[cbind(x, y)] <- z
out
})
使用xtabs
:
xtabs(z~x+y, data=tmp)
使用稀疏矩阵:
sparseMatrix
包中还有Matrix
,如下所示: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
使用reshape
:
您也可以使用基本R函数reshape
,如此处所示:Convert table into matrix by column names,但您必须稍后进行一些操作以删除额外的列并使名称正确(未显示) 。
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
答案 1 :(得分:2)
这个问题有些年头了,但也许有些人仍对其他答案感兴趣。
如果您不想加载任何包,可以使用此功能:
#' 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)
}
工作原理:
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 :(得分:2)
unstack
unstack(df, V3 ~ V2)
# a b c
# 1 1 2 3
# 2 3 3 2
这可能不是通用的解决方案,但在这种情况下效果很好。
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))
答案 3 :(得分:2)
出于完整性考虑,有NA
个解决方案。
tapply()
数据
with(d, tapply(z, list(x, y), sum))
# a b c
# x 1 2 3
# y 3 3 2
答案 4 :(得分:1)
tidyverse的tidyr软件包具有出色的功能。
假设您的变量从左到右分别命名为v1,v2和v3,而数据框则命名为dat:
dat %>%
spread(key = v2,
value = v3)
Ta da!
答案 5 :(得分:1)
从tidyr 0.8.3.9000
开始,引入了一个名为pivot_wider()
的新功能。它基本上是先前spread()
函数(which is, moreover, no longer under active development)的升级版本。来自pivoting vignette:
此插图说明了新的ivot_longer()和 ivot_wider()函数。他们的目标是改善 collect()和spread(),并结合发现的最新功能 在其他软件包中。
一段时间以来,很明显,从根本上讲 Spread()和collect()的设计有误。很多人找不到 名称直观,很难记住哪个方向 对应于传播和聚集。好像也 很难记住这些功能的参数, 意味着很多人(包括我!)必须咨询 每次都有文档。
如何使用它(使用@Aaron中的数据):
pivot_wider(data = tmp, names_from = y, values_from = z)
或以“完整的” tidyverse
方式:
tmp %>%
pivot_wider(names_from = y, values_from = z)