【问题标题】:Reshape three column data frame to matrix ("long" to "wide" format) [duplicate]将三列数据框重塑为矩阵(“长”到“宽”格式)[重复]
【发布时间】:2012-03-25 22:01:23
【问题描述】:

我有一个看起来像这样的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 并尝试编写手动函数来执行此操作,但我似乎无法正确处理。

【问题讨论】:

  • @AnandaMahto 对此也有很好的回答:stackoverflow.com/a/14515736/210673
  • @Jaap,你是如何决定关闭这个而不是另一个?我在这里的回答是对这个常见问题的权威回答,在我看来,这比其他问题的答案要有用得多。
  • 另一个问题有很多答案,每个都突出一个特定的解决方案,并不比这个更糟。那么,我是如何做出决定的呢?简单:另一个问题较早提出,并且有很多有价值的答案。
  • @Jaap:叹息。这里的文化仍然让我感到困惑和沮丧。在某一时刻,“权威”答案是首选,问题的时间安排并不重要。我想我需要保持“左”(就像我的用户名一样)。

标签: r matrix dataframe plyr reshape


【解决方案1】:

有很多方法可以做到这一点。此答案从迅速成为标准方法的方法开始,但还包括旧方法和各种其他方法,包括对散布在此站点上的类似问题的答案。

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.0pivot_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

旧的很酷的新方法是使用来自tidyrspread。它同样返回一个数据框。

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

使用稀疏矩阵:

Matrix 包中还有 sparseMatrix,如下所示: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

【讨论】:

  • acast(tmp, x~y, value.var="z") 将给出一个矩阵输出,x 作为 row.names
  • 您能评论一下不同方法的优缺点吗?
  • 在大多数小型数据集中,首要考虑因素应该是以未来分析师(包括未来的您)清楚且最不易受人类编码错误影响的方式进行编码。尽管这取决于您的优势和需求,但通常这被认为是新的 tidyverse 套件的优势之一。另一个考虑因素(虽然不是真正的优势/劣势)是您是否想要一个矩阵或数据框作为结果;这个问题特别要求一个矩阵,你可以在答案中看到一些技术直接给出,而一些给出数据框。
  • 计算时间也可能是大型数据集的考虑因素,尤其是当代码需要重复多次或在多个数据集上时。不过,我怀疑这部分取决于数据集的特定特征。如果您对此感到担忧,我建议您再问一个有关针对您的特定情况进行优化的问题;对于这群人来说,这样的问题一度就像猫薄荷。 :) 但我会重复我之前的观点:针对用户进行优化(通常)比针对计算机进行优化更重要。
【解决方案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

【讨论】:

    【解决方案3】:

    基础 R,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))
    

    【讨论】:

      【解决方案4】:

      为了完整起见,有一个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))
      

      【讨论】:

        【解决方案5】:

        tidyr 0.8.3.9000 开始,引入了一个名为pivot_wider() 的新功能。基本上是之前spread()功能(which is, moreover, no longer under active development)的升级版。来自pivoting vignette

        这个小插曲描述了新的 pivot_longer() 和 pivot_wider() 函数。他们的目标是提高可用性 收集()和传播(),并结合发现的最先进的功能 在其他包中。

        一段时间以来,很明显有一些基本的东西 spread() 和gather() 的设计错误。很多人找不到 名称直观,很难记住哪个方向 对应于传播和聚集。似乎也 令人惊讶地难以记住这些函数的参数, 这意味着很多人(包括我!)必须咨询 每次都有文档。

        如何使用它(使用来自@Aaron 的数据):

        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)
        

        【讨论】:

          【解决方案6】:

          来自 tidyverse 的 tidyr 包具有出色的功能。

          假设您的变量从左到右命名为 v1、v2 和 v3,并且您的数据框命名为 dat:

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

          哒哒!

          【讨论】:

          • 查看@Aaron 的答案
          • 不知何故错过了最后他覆盖传播的部分。很好的收获,谢谢。
          • tidyverse 解决方案现已移至顶部。
          猜你喜欢
          • 2021-09-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-23
          • 1970-01-01
          相关资源
          最近更新 更多