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

时间:2012-05-14 18:33:14

标签: r dataframe plyr

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

例如,这是一个长格式的简单数据框。 ID是主题,TIME是时间变量,XYID TIME的{​​{1}}测量结果:

> 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的值转换为包含包含X的列标题,我知道我可以使用cast()包中的reshape(或{来自dcast())的{1}}:

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

(FWIW,我并不关心 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 的所有X是否首先跟Y',或者它们是否被X_1交错,{{1 }},Y_1X_2等。)

我可以通过强制转换来接近这个 - 长数据两次并合并结果,虽然列名需要一些工作,如果我需要添加一个,我需要调整它除Y_2X之外的第3或第4个变量:

Y

似乎merge( cast(my.df, ID ~ TIME, value="X"), cast(my.df, ID ~ TIME, value="Y"), by="ID", suffixes=c("_X","_Y") ) 和/或reshape2中的某些功能组合应该能够更优雅地完成我的尝试,以及更干净地处理多个度量变量。像plyr这样的东西是无效的。但我无法弄明白。

5 个答案:

答案 0 :(得分:20)

   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

答案 1 :(得分:15)

为了处理您想要的多个变量,您需要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

根据评论编辑:

数据框

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

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

的原因
Aggregation function missing: defaulting to length

如果你添加另一个打破冗余的变量,你可以得到一些有用的东西。

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)

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

答案 2 :(得分: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

答案 3 :(得分: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.

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建议。当remove参数为true(默认值)时,这在功能上等同于上面的dplyr::mutate()dplyr::select()组合。

如果你不习惯这种类型的操纵,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.

答案 4 :(得分:0)

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

这可能比previous tidyr approach更可取 (使用gather()spread()的组合)。

pivoting vignette中介绍了更多功能。 此示例非常简洁,因为您所需的规格与id_colsnames_sep的默认值匹配。