我有一个看起来像这样的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
函数,也尝试手动编写了一个函数来实现此功能,但似乎无法正确执行。我有一个看起来像这样的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
函数,也尝试手动编写了一个函数来实现此功能,但似乎无法正确执行。有许多方法可以实现这个目标。本答案介绍了目前最常用的方法,并列举了一些旧的方法和其他类似问题答案中散落的各种方法。
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 1.0.0
中的 pivot_wider
。它返回一个数据框,这可能是大多数读者想要的。但对于热力图,您需要将其转换为真正的矩阵。
library(tidyr)
pivot_wider(tmp, names_from = y, values_from = z)
## # A tibble: 2 x 4
## x a b c
## <fct> <dbl> <dbl> <dbl>
## 1 x 1 2 3
## 2 y 3 3 2
现在推荐的方法是使用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包。
要获取矩阵,请使用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://dev59.com/UlrUa4cB1Zd3GeqPghvr#7020101
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)
使用稀疏矩阵:
在Matrix
包中还有一个sparseMatrix
,如此处所示:R-通过列名将BIG表转换为矩阵
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
,如此处所建议:将表格按列名转换为矩阵,不过您需要进行一些后续处理以删除额外的列并且正确获取名称(未展示)。
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
acast(tmp, x~y, value.var="z")
将会给出一个矩阵输出,其中 x
作为行名。 - mnelunstack
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))
这个问题已经有些年头了,但也许还有一些人对替代答案感兴趣。
如果你不想加载任何包,你可以使用这个函数:
#' 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
tidyr 0.8.3.9000
开始,引入了一个名为 pivot_wider()
的新函数。它基本上是以前的 spread()
函数的升级版本(而且不再处于活跃开发状态)。来自 pivoting vignette:
如何使用它(使用 @Aaron 提供的数据):本文档介绍了新的 pivot_longer() 和 pivot_wider() 函数的使用。它们的目标是改进 gather() 和 spread() 的可用性,并结合其他包中的最新功能。
有段时间以来,很明显 spread() 和 gather() 的设计存在根本性问题。许多人认为这些名称不直观,很难记住哪个方向对应扩展,哪个方向对应收集。似乎也很难记住这些函数的参数,这意味着许多人(包括我!)每次都要查阅文档。
pivot_wider(data = tmp, names_from = y, values_from = z)
x a b c
<fct> <dbl> <dbl> <dbl>
1 x 1 2 3
2 y 3 3 2
tidyverse
风格:tmp %>%
pivot_wider(names_from = y, values_from = z)
tapply()
的解决方案。with(d, tapply(z, list(x, y), sum))
# a b c
# x 1 2 3
# y 3 3 2
数据
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))
dat %>%
spread(key = v2,
value = v3)