332

我在重新排列以下数据框时遇到问题:

set.seed(45)
dat1 <- data.frame(
    name = rep(c("firstName", "secondName"), each=4),
    numbers = rep(1:4, 2),
    value = rnorm(8)
    )

dat1
       name  numbers      value
1  firstName       1  0.3407997
2  firstName       2 -0.7033403
3  firstName       3 -0.3795377
4  firstName       4 -0.7460474
5 secondName       1 -0.8981073
6 secondName       2 -0.3347941
7 secondName       3 -0.5013782
8 secondName       4 -0.1745357

我想重塑它,使每个唯一的“名称”变量都是一个行名,“值”作为沿该行的观察值,“数字”作为列名。有点像这样:

     name          1          2          3         4
1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
5 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

我已经看过meltandcast和其他一些东西,但似乎没有一个可以完成这项工作。

4

12 回答 12

324

使用reshape功能:

reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")
于 2011-05-04T23:20:03.083 回答
154

新的(2014 年)tidyr包也简单地做到了这一点,gather()/是/spread()的术语。meltcast

编辑:现在,在 2019 年,tidyr v 1.0 已经启动并设置spreadgather一个弃用路径,而是更喜欢pivot_widerand pivot_longer,你可以在这个答案中找到它的描述。如果您想简要了解spread/gather.

library(tidyr)
spread(dat1, key = numbers, value = value)

来自github

tidyr是一个重构,reshape2旨在配合整洁的数据框架,并与数据分析携手并进,magrittr构建dplyr一个坚实的管道。

就像reshape2less than reshapetidyr一样, less than reshape2。它是专门为整理数据而设计的,而不是一般的重塑reshape2,或者重塑的一般聚合。特别是,内置方法仅适用于数据帧,tidyr不提供边距或聚合。

于 2014-07-29T19:37:09.363 回答
83

您可以使用reshape()函数或reshape 包中的melt()/函数来执行此操作。cast()对于第二个选项,示例代码是

library(reshape)
cast(dat1, name ~ numbers)

或使用reshape2

library(reshape2)
dcast(dat1, name ~ numbers)
于 2011-05-04T22:42:14.810 回答
54

如果性能是一个问题,另一种选择是使用' melt 和 dcast 函数data.table的扩展reshape2

参考:使用 data.tables 进行高效整形

library(data.table)

setDT(dat1)
dcast(dat1, name ~ numbers, value.var = "value")

#          name          1          2         3         4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814

而且,从 data.table v1.9.6 开始,我们可以对多列进行强制转换

## add an extra column
dat1[, value2 := value * 2]

## cast multiple value columns
dcast(dat1, name ~ numbers, value.var = c("value", "value2"))

#          name    value_1    value_2   value_3   value_4   value2_1   value2_2 value2_3  value2_4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078  0.3672866 -1.6712572 3.190562 0.6590155
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814 -1.6409368  0.9748581 1.476649 1.1515627
于 2016-03-27T22:35:51.693 回答
36
于 2019-07-12T20:14:34.283 回答
29

使用您的示例数据框,我们可以:

xtabs(value ~ name + numbers, data = dat1)
于 2011-05-04T22:58:48.690 回答
23

其他两个选项:

基础包:

df <- unstack(dat1, form = value ~ numbers)
rownames(df) <- unique(dat1$name)
df

sqldf包裹:

library(sqldf)
sqldf('SELECT name,
      MAX(CASE WHEN numbers = 1 THEN value ELSE NULL END) x1, 
      MAX(CASE WHEN numbers = 2 THEN value ELSE NULL END) x2,
      MAX(CASE WHEN numbers = 3 THEN value ELSE NULL END) x3,
      MAX(CASE WHEN numbers = 4 THEN value ELSE NULL END) x4
      FROM dat1
      GROUP BY name')
于 2015-07-14T17:44:08.150 回答
16

使用基本 Raggregate函数:

aggregate(value ~ name, dat1, I)

# name           value.1  value.2  value.3  value.4
#1 firstName      0.4145  -0.4747   0.0659   -0.5024
#2 secondName    -0.8259   0.1669  -0.8962    0.1681
于 2016-09-02T07:52:19.653 回答
14

基本reshape功能工作得很好:

df <- data.frame(
  year   = c(rep(2000, 12), rep(2001, 12)),
  month  = rep(1:12, 2),
  values = rnorm(24)
)
df_wide <- reshape(df, idvar="year", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

在哪里

  • idvar是分隔行的类的列
  • timevar是要广泛转换的类的列
  • v.names是包含数值的列
  • direction指定宽或长格式
  • 可选参数是类名之间和输出中sep使用的分隔符。timevarv.namesdata.frame

如果不存在,请在使用该函数idvar之前创建一个:reshape()

df$id   <- c(rep("year1", 12), rep("year2", 12))
df_wide <- reshape(df, idvar="id", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

请记住,这idvar是必需的!和部分很简单timevarv.names这个函数的输出比其他一些函数的输出更可预测,因为一切都是明确定义的。

于 2018-08-02T23:50:52.503 回答
10

Win-Vector 的天才数据科学家(制作和的人)提供了一个非常强大的新软件包vtreat,称为. 它实现了本文档和本博文中描述的“协调数据”原则。这个想法是,无论您如何组织数据,都应该可以使用“数据坐标”系统识别各个数据点。以下是 John Mount 最近博客文章的摘录:seplyrreplyrcdata

整个系统基于两个原语或运算符 cdata::moveValuesToRowsD() 和 cdata::moveValuesToColumnsD()。这些运算符具有枢轴、非枢轴、单热编码、转置、移动多行和多列以及许多其他转换作为简单的特殊情况。

根据 cdata 原语编写许多不同的操作很容易。这些运算符可以在内存或大数据规模上工作(使用数据库和 Apache Spark;对于大数据,使用 cdata::moveValuesToRowsN() 和 cdata::moveValuesToColumnsN() 变体)。转换由一个控制表控制,该控制表本身就是转换的图表(或图片)。

我们将首先构建控制表(有关详细信息,请参阅博客文章),然后将数据从行移动到列。

library(cdata)
# first build the control table
pivotControlTable <- buildPivotControlTableD(table = dat1, # reference to dataset
                        columnToTakeKeysFrom = 'numbers', # this will become column headers
                        columnToTakeValuesFrom = 'value', # this contains data
                        sep="_")                          # optional for making column names

# perform the move of data to columns
dat_wide <- moveValuesToColumnsD(tallTable =  dat1, # reference to dataset
                    keyColumns = c('name'),         # this(these) column(s) should stay untouched 
                    controlTable = pivotControlTable# control table above
                    ) 
dat_wide

#>         name  numbers_1  numbers_2  numbers_3  numbers_4
#> 1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
#> 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357
于 2017-12-23T23:01:37.873 回答
3

much easier way!

devtools::install_github("yikeshu0611/onetree") #install onetree package

library(onetree)
widedata=reshape_toWide(data = dat1,id = "name",j = "numbers",value.var.prefix = "value")
widedata

        name     value1     value2     value3     value4
   firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
  secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

if you want to go back from wide to long, only change Wide to Long, and no changes in objects.

reshape_toLong(data = widedata,id = "name",j = "numbers",value.var.prefix = "value")

        name numbers      value
   firstName       1  0.3407997
  secondName       1 -0.8981073
   firstName       2 -0.7033403
  secondName       2 -0.3347941
   firstName       3 -0.3795377
  secondName       3 -0.5013782
   firstName       4 -0.7460474
  secondName       4 -0.1745357
于 2019-07-26T05:47:41.757 回答
0

Using only dplyr and map.

library(dplyr)
library(purrr)
set.seed(45)
dat1 <- data.frame(
  name = rep(c("firstName", "secondName"), each=4),
  numbers = rep(1:4, 2), value = rnorm(8)
)
longer_to_wider <- function(data, name_from, value_from){
  group <- colnames(data)[!(colnames(data) %in% c(name_from,value_from))]
  data %>% group_by(.data[[group]]) %>%
    summarise( name = list(.data[[name_from]]), 
               value = list(.data[[value_from]])) %>%
    {
      d <- data.frame(
        name = .[[name_from]] %>% unlist() %>% unique()
      )
      e <- map_dfc(.[[group]],function(x){
          y <- data_frame(
            x = data %>% filter(.data[[group]] == x) %>% pull(value_from)
          )
          colnames(y) <- x
          y
      })
      cbind(d,e)
    }
}
longer_to_wider(dat1, "name", "value")
#    name          1          2          3          4
# 1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
# 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357
于 2021-11-02T15:36:29.123 回答