使用多个度量列将数据从长格式转换为宽格式

col*_*riq 34 r plyr dataframe

当我想要携带多个度量变量时,我无法找出将数据从长格式转换为宽格式的最优雅和灵活的方法.

例如,这是一个长格式的简单数据框. ID是主体,TIME是时间可变的,并且X和Y是由测量XY:

> 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
Run Code Online (Sandbox Code Playgroud)

如果我只想将TIME的值转换为包含包含X的列标题,我知道我可以使用reshape包中的强制转换(或来自reshape2的dcast):

> 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
Run Code Online (Sandbox Code Playgroud)

但我真正想做的是将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
Run Code Online (Sandbox Code Playgroud)

(FWIW,我真的不关心,如果所有X的首先遵循由Y的,或者如果它们交错为ID,TIME,TIME,X,等)

我可以通过cast()两次长数据并合并结果来接近这个,虽然列名需要一些工作,如果我需要添加除X和Y之外的第3或第4个变量,我需要调整它:

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

看起来像某些功能组合reshape和/或dcast()应该能够更优雅地完成我的尝试,以及更干净地处理多个测量变量.有点像reshape2,这是无效的.但我无法弄明白.

任何R-Wizards可以帮助我吗?谢谢.

Set*_*eth 20

   reshape(my.df,
           idvar = "ID",
           timevar = "TIME",
           direction = "wide")
Run Code Online (Sandbox Code Playgroud)

  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
Run Code Online (Sandbox Code Playgroud)


Bri*_*ggs 16

为了处理您想要的多个变量,您需要melt在投射之前获得数据.

library("reshape2")

dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME)
Run Code Online (Sandbox Code Playgroud)

这使

  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
Run Code Online (Sandbox Code Playgroud)

根据评论编辑:

数据框

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))))
Run Code Online (Sandbox Code Playgroud)

给出不同的结果(所有条目都是2),因为ID/ TIME组合不表示唯一的行.实际上,每个ID/ TIME组合有两行.reshape2假定每个可能的变量组合都有一个值,并且如果有多个条目,则应用汇总函数来创建单个变量.这就是警告的原因

Aggregation function missing: defaulting to length
Run Code Online (Sandbox Code Playgroud)

如果添加另一个打破冗余的变量,您可以获得一些有用的东西.

my.df$cycle <- rep(1:2, each=num.id*num.time)
dcast(melt(my.df, id.vars=c("cycle", "ID", "TIME")), cycle+ID~variable+TIME)
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为cycle/ ID/ time现在唯一地定义了一行my.df.


akr*_*run 14

使用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
Run Code Online (Sandbox Code Playgroud)


wib*_*ley 7

这是tidyr包的解决方案,它基本上取代了reshapereshape2.与这两个软件包一样,它使数据集首先变长,然后变宽.

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.
Run Code Online (Sandbox Code Playgroud)

在之后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
Run Code Online (Sandbox Code Playgroud)

最终的结果是:

  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
Run Code Online (Sandbox Code Playgroud)

spread()是@JWilliman提出的替代方案.这在功能上等同于pivot_wider()tidyr::gather()上述组合中,当tidyr::unite()参数为true(这是默认值).

如果你不习惯这种类型的操纵,dplyr::mutate()可能是一个小障碍,因为它是你必须学习和记忆的另一个功能.但是,它的好处包括(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.
Run Code Online (Sandbox Code Playgroud)


wib*_*ley 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)
  )
Run Code Online (Sandbox Code Playgroud)

结果

# 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
Run Code Online (Sandbox Code Playgroud)

这可能是优选的前tidyr方法 (使用的组合gather()spread())。

旋转小插图中描述了更多功能。这个例子是特别简洁,因为您想要的规格相匹配的默认值id_colsnames_sep

  • 令人惊讶的是,这个没有得到更多的支持。如果您正在处理一个非常大的数据框和/或很多列,这是唯一可行的方法。所有其他解决方案都耗尽了内存。不太确定阈值是多少,但我正在处理一个包含 6M 记录和 24 列的数据帧。 (2认同)