当我要携带多个度量变量时,我很难找出最优雅,最灵活的方式将数据从长格式转换为宽格式。长格式相框。 ID是主题,TIME是时间变量,XY是在ID处对TIME进行的测量:

> my.df <- data.frame(ID=rep(c("A","B","C"), 5), TIME=rep(1:5, each=3), X=1:15, Y=16:30)
> my.df

   ID TIME  X  Y
1   A    1  1 16
2   B    1  2 17
3   C    1  3 18
4   A    2  4 19
5   B    2  5 20
6   C    2  6 21
7   A    3  7 22
8   B    3  8 23
9   C    3  9 24
10  A    4 10 25
11  B    4 11 26
12  C    4 12 27
13  A    5 13 28
14  B    5 14 29
15  C    5 15 30


如果我只是想将TIME的值取整进入包含include X的列标题中,我知道我可以使用cast()软件包中的reshape(或dcast()中的reshape2):

> cast(my.df, ID ~ TIME, value="X")
  ID 1 2 3  4  5
1  A 1 4 7 10 13
2  B 2 5 8 11 14
3  C 3 6 9 12 15


但是我真正想做的是还带来了Y作为另一个度量变量,并让列名同时反映了度量变量名称和时间值:

  ID X_1 X_2 X_3  X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7   10  13  16  19  22  25  28
2  B   2   5   8   11  14  17  20  23  26  29
3  C   3   6   9   12  15  18  21  24  27  30


(FWIW,我不是真的请注意是否所有X都紧随其后,或者是否与YX_1Y_1X_2等交错插入)

我可以通过强制转换接近将长数据两次并合并结果,尽管列名需要一些工作,并且如果需要添加第3个或第4个变量,则需要对其进行调整除了Y_2X之外,还可以:

merge(
cast(my.df, ID ~ TIME, value="X"),
cast(my.df, ID ~ TIME, value="Y"),
by="ID", suffixes=c("_X","_Y")
)


Y和/或reshape2中的某些功能组合似乎应该能够比我的尝试更优雅地完成此操作以及更干净地处理多个度量变量。诸如plyr之类的无效。但是我还没弄清楚。

#1 楼

为了根据需要处理多个变量,需要先对数据进行melt转换。

library("reshape2")

dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME)


给出




根据评论进行编辑:

数据框

  ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7  10  13  16  19  22  25  28
2  B   2   5   8  11  14  17  20  23  26  29
3  C   3   6   9  12  15  18  21  24  27  30


给出不同的结果(所有条目均为2),因为ID / TIME组合不表示唯一行。实际上,每个ID / TIME组合有两行。 reshape2为变量的每种可能组合假定一个值,并且在存在多个条目的情况下将应用汇总函数来创建一个变量。这就是为什么发出警告的原因。
num.id = 10 
num.time=10 
my.df <- data.frame(ID=rep(LETTERS[1:num.id], num.time), 
                    TIME=rep(1:num.time, each=num.id), 
                    X=1:(num.id*num.time), 
                    Y=(num.id*num.time)+1:(2*length(1:(num.id*num.time))))


如果添加另一个破坏冗余的变量,则可以得到一些有用的信息。

Aggregation function missing: defaulting to length


之所以有效,是因为cycle / ID / time现在在my.df中唯一地定义了一行。

评论


我试图评估哪种解决方案最快,但是如果数据帧如下,则会发现代码存在问题:num.id = 10 num.time = 10 my.df <-data.frame(ID = rep(LETTERS [ 1:num.id],num.time),TIME = rep(1:num.time,each = num.id),X = 1:(num.idnum.time),Y =(num.idnum.time) +1:(2 *长度(1 :(数字ID *数字时间))))

–盖尔迪诺(Manoel Galdino)
2012年5月14日20:21

太好了,谢谢Brian。由于Cast似乎可以正常工作,所以我没有意识到仍然需要融化。

–colonel.triq
2012年5月15日19:02

#2 楼

   reshape(my.df,
           idvar = "ID",
           timevar = "TIME",
           direction = "wide")


给予

  ID X.1 Y.1 X.2 Y.2 X.3 Y.3 X.4 Y.4 X.5 Y.5
1  A   1  16   4  19   7  22  10  25  13  28
2  B   2  17   5  20   8  23  11  26  14  29
3  C   3  18   6  21   9  24  12  27  15  30


#3 楼

使用data.table_1.9.5,无需melt即可完成,因为它可以处理多个value.var列。您可以从here

 library(data.table)
 dcast(setDT(my.df), ID~TIME, value.var=c('X', 'Y'))
 #   ID 1_X 2_X 3_X 4_X 5_X 1_Y 2_Y 3_Y 4_Y 5_Y
 #1:  A   1   4   7  10  13  16  19  22  25  28
 #2:  B   2   5   8  11  14  17  20  23  26  29
 #3:  C   3   6   9  12  15  18  21  24  27  30

安装它

#4 楼

注意-2019年9月:在tidyr中,gather() + spread()方法(在此答案中描述)或多或少已被pivot_wider()方法(在此新的tidyr答案中描述)取代。有关过渡的最新信息,请参见枢轴装饰插图。


这是tidyr软件包的一种解决方案,该软件包已基本替换了reshape和reshape2。与这两个软件包一样,该策略首先使数据集变长,然后再变宽。

library(magrittr); requireNamespace("tidyr"); requireNamespace("dplyr")
my.df %>%
  tidyr::gather(key=variable, value=value, c(X, Y)) %>%   # Make it even longer.
  dplyr::mutate(                                          # Create the spread key.
    time_by_variable   = paste0(variable, "_", TIME)
  ) %>%
  dplyr::select(ID, time_by_variable, value) %>%          # Retain these three.
  tidyr::spread(key=time_by_variable, value=value)        # Spread/widen.


tidyr::gather()调用后,中间数据集为:

ID TIME variable value
1   A    1        X     1
2   B    1        X     2
3   C    1        X     3
...
28  A    5        Y    28
29  B    5        Y    29
30  C    5        Y    30


最终结果是:

  ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1  A   1   4   7  10  13  16  19  22  25  28
2  B   2   5   8  11  14  17  20  23  26  29
3  C   3   6   9  12  15  18  21  24  27  30


tidyr::unite()是@JWilliman建议的替代方法。当dplyr::mutate()参数为true(默认设置)时,此功能在功能上等同于上述dplyr::select()remove组合。

如果您不习惯这种类型的操作,则tidyr::unite()可能会成为一个小障碍,因为它是您必须学习并记住的另一项功能。但是,这样做的好处包括(a)更简洁的代码(即,四行替换为一行)和(b)减少重复变量名称的位置(即,不必在dplyr::select()子句中重复/修改变量)。

my.df %>%
  tidyr::gather(key=variable, value=value, c(X, Y)) %>%           # Make it even longer.
  tidyr::unite("time_by_variable", variable, TIME, remove=T) %>%  # Create the spread key `time_by_variable` while simultaneously dropping `variable` and `TIME`.
  tidyr::spread(key=time_by_variable, value=value)                # Spread/widen.


评论


dplyr :: mutate(time_by_variable = paste0(variable,“ _”,TIME))行可以替换为tidyr :: unite(“ time_by_variable”,variable,TIME)。

– JWilliman
18-3-21在23:42



我同意,@ JWilliman。我认为显式的mutate()和paste0()调用使意图更清晰,并且避免引入新功能。但是我看到tidyr :: unite()在过去的一年中使用了更多。我将附上回复以反映您的建议。

–wibeasley
18年5月8日在16:59

#5 楼

pivot_wider()函数是tidyr的第二代方法(在tidyr 1.0.0中发布)。

 library(magrittr); requireNamespace("tidyr");

my.df %>%
  tidyr::pivot_wider(
    names_from  = c(TIME), # Can accommodate more variables, if needed.
    values_from = c(X, Y)
  )
 


结果:

 # A tibble: 3 x 11
  ID      X_1   X_2   X_3   X_4   X_5   Y_1   Y_2   Y_3   Y_4   Y_5
  <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 A         1     4     7    10    13    16    19    22    25    28
2 B         2     5     8    11    14    17    20    23    26    29
3 C         3     6     9    12    15    18    21    24    27    30
 


这比以前的tidyr方法(使用gather()spread()的组合)更可取。 />枢轴装饰插图中描述了更多功能。
此示例特别简洁,因为您所需的规格与id_colsnames_sep的默认设置匹配。