如何将数据从长格式转换为宽格式


262

我在重新整理以下数据框时遇到麻烦:

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

我已经看过了meltcast还有其他一些东西,但似乎都没有。



4
@弗兰克:这是一个更好的标题。长格式宽格式是使用的标准术语。通过搜索这些术语找不到其他答案。
smci 2014年

还有一个问题:如何改回来?
HappyLiang

Answers:


255

使用reshape功能:

reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")

13
+1,您无需依赖外部程序包,因为reshape附带了stats。更不用说它更快了!=)
aL3xa

@indra_patil-我可能会使用其他答案之一所示的reshape2包。您可以创建一个特定于您的用例的新问题,如果无法解决,可以将其发布。
追逐

5
reshape是可怕的函数API的杰出示例。这几乎是无用的。
NoBackingDown

14
reshape意见和类似的说法名字是不是所有的帮助。但是,我发现很长一段时间都需要提供数据data =。frame,idvar=标识组v.names的变量,=将以宽格式变为多列timevar的变量,=包含将附加值的变量转换v.names为宽格式direction = wide,和sep = "_"。足够清楚吗?;)
Brian D

3
我想说的是R仍然以大约2比1的
比分

129

新的(2014年)tidyr包也做到这一点简单地说,与gather()/ spread()是的条款melt/ cast

编辑:现在,在2019年,tidyr v 1.0已启动并设置,spread并且gather已弃用,而首选pivot_widerand pivot_longer,您可以在此答案中找到。如果您想简要了解的简短生活,请继续阅读spread/gather

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

来自github

tidyrreshape2对整洁数据框架的重新设计,并与之并肩工作magrittrdplyr建立了可靠的数据分析管道。

正如reshape2不是重塑确实少,tidyr不小于reshape2。它是专门为整理数据而设计的,而不是用于进行常规整形reshape2或进行整形的常规聚合。特别是,内置方法仅适用于数据帧,并且不tidyr提供边距或聚合。


5
只是想将链接添加到[R食谱,讨论因使用这些功能页面tidyrreshape2。它提供了很好的示例和解释。
杰克

71

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

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

或使用 reshape2

library(reshape2)
dcast(dat1, name ~ numbers)

2
可能值得注意的是,如果没有明确的“值”列,则仅使用castdcast将无法很好地工作。尝试dat <- data.frame(id=c(1,1,2,2),blah=c(8,4,7,6),index=c(1,2,1,2)); dcast(dat, id ~ index); cast(dat, id ~ index),您将无法获得期望的结果。您需要明确注明value/value.var- cast(dat, id ~ index, value="blah")dcast(dat, id ~ index, value.var="blah")例如。
thelatemail

44

如果需要考虑性能的另一种选择是使用data.table' reshape2smelt和dcast函数的扩展名

参考:使用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

5
data.table办法是最好的!非常高效...当name30-40列组合时,您会看到区别!
joel.wilson '17

如果我想获得最大收益怎么办?
T.Fung

@ T.Fung我不明白您的要求。最好打开一个新问题?
SymbolixAU

op的问题“名称”和“数字”中的@SymbolixAU是唯一的组合。如果不是,我想在透视后获取每个组合的最大值怎么办?如果太麻烦的话,这不是问题。只是思想的食物。谢谢。
T.Fung

好答案。谢谢。对于多列,我“在.subset2错误(X,I,准确=精确)”了,并且可以通过强制使用data.table dcast的解决这个问题:看stackoverflow.com/a/44271092/190791
TimothéeHENRY

26

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

xtabs(value ~ name + numbers, data = dat1)

2
这是一个很好的方法,但是结果是格式表,它可能不像data.frame或data.table那样难于处理,两者都有大量的程序包
cloudcomputes '17

18

其他两个选项:

基本包装:

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')

1
代替硬编码数字,查询可以这样设置:ValCol <- unique(dat1$numbers);s <- sprintf("MAX(CASE WHEN numbers = %s THEN value ELSE NULL END) `%s`,", ValCol, ValCol);mquerym <- gsub('.{1}$','',paste(s, collapse = "\n"));mquery <- paste("SELECT name,", mquerym, "FROM dat1", "GROUP BY name", sep = "\n");sqldf(mquery)
M--

13

使用基本R aggregate函数:

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

11

在的开发版本中tidyr ‘0.8.3.9000’,存在pivot_widerpivot_longer,它们被普遍用于从1列到多列的重塑(分别为long-> width,wide-> long)。使用OP的数据

-单列长->宽

library(dplyr)
library(tidyr)
dat1 %>% 
    pivot_wider(names_from = numbers, values_from = value)
# A tibble: 2 x 5
#  name          `1`    `2`    `3`    `4`
#  <fct>       <dbl>  <dbl>  <dbl>  <dbl>
#1 firstName   0.341 -0.703 -0.380 -0.746
#2 secondName -0.898 -0.335 -0.501 -0.175

->创建了另一列以显示功能

dat1 %>% 
    mutate(value2 = value * 2) %>% 
    pivot_wider(names_from = numbers, values_from = c("value", "value2"))
# A tibble: 2 x 9
#  name       value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4
#  <fct>        <dbl>   <dbl>   <dbl>   <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
#1 firstName    0.341  -0.703  -0.380  -0.746    0.682   -1.41    -0.759   -1.49 
#2 secondName  -0.898  -0.335  -0.501  -0.175   -1.80    -0.670   -1.00    -0.349

7

有从赢矢量天才科学家的数据(即发人非常强大的新包装vtreatseplyrreplyr调用)cdata。它实现了本文档以及本博客文章中描述的“协调数据”原则。这个想法是,无论您如何组织数据,都应该可以使用“数据坐标”系统来识别单个数据点。这是John Mount最近的博客文章的节选:

整个系统基于两个原语或运算符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

7

基本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参数是在timevar类名之间和v.names输出中使用的分隔符data.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部分很容易。该函数的输出比其他一些函数更可预测,因为所有内容都已明确定义。


1

更简单的方法!

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

如果要从宽到长返回,只需将宽到长更改,而对象没有变化。

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.