首页 > 解决方案 > 有效地将数据从宽格式转换为长格式,并将多列组合成新变量

问题描述

我必须重塑一个由观察(obs)和与观察相关联的元素(p)组成的数据集。元素的数据(特征)位于附加到观测值的新列中。

MWE 如下所示:

set.seed(1)

data <- data.frame(obs_id = c(1:3),
                   char1 = sample(1:10, 3),
                   p1 = 1,
                   p1_char = sample(11:20, 3),
                   p2 = 2,
                   p2_char = sample(11:20, 3),
                   p3 = 3,
                   p3_char = sample(11:20, 3))

这会产生如下所示的数据:

> data
  obs_id p1 p1_char1 p2 p2_char1 p3 p3_char1
1      1  1       20  2       12  3       16
2      2  1       16  2       20  3       11
3      3  1       18  2       16  3       13

obs_id是观察。pX表示各种元素和pX_charX特征。

现在,我必须创建带有两个新列的长格式数据。第一个应命名p并包含所有元素编号。所以,太好了。例如,这可以很容易地gathertidyr包中实现:

library(magrittr)
library(tidyr)

data_long1 <- gather(data, key = p_variable, value = p,
                     p1, p2, p3)

过滤掉第一个观察结果,一切都应该是:

> data_long1 %>% filter(obs_id == 1)
  obs_id p1_char1 p2_char1 p3_char1 p_variable p
1      1       20       12       16         p1 1
2      1       20       12       16         p2 2
3      1       20       12       16         p3 3

现在,应该命名第二个新列char并填充元素的特征。我也可以用gather独立堆叠它们。

data_long2 <- gather(data, key = char_variable, value = char,
                     p1_char1, p2_char1, p3_char1)

> data_long2 %>% filter(obs_id == 1)
  obs_id p1 p2 p3 char_variable char
1      1  1  2  3      p1_char1   20
2      1  1  2  3      p2_char1   12
3      1  1  2  3      p3_char1   16

现在,我可以将两者结合起来bind_cols()得到我想要的

data_long <- bind_cols(data_long1, data_long2)

> data_long %>% 
+   select(obs_id, p, char) %>% 
+   filter(obs_id == 1)
  obs_id p char
1      1 1   20
2      1 2   12
3      1 3   16

问题是我需要对要堆叠的元素的每个新变量执行此操作。

我的问题是:当我将数据从宽格式化为长时,是否有更有效的方法来创建两列或多列?如果我pX_char2char2在最终数据中的变量中转换原始数据中的变量怎么办?

标签: rdplyrreshapetidyr

解决方案


正如@domaeg 在评论中指出的那样,这可以通过以下新pivot_longer功能来完成tidyr 1.0.0

library(tidyverse)

data %>% pivot_longer(-obs_id, 
                      names_to = "p", 
                      names_pattern = "p([0-9])", 
                      values_to = "char")

产生:

# A tibble: 9 x 3
  obs_id p      char
   <int> <chr> <int>
1      1 1        20
2      1 2        12
3      1 3        16
4      2 1        16
5      2 2        20
6      2 3        11
7      3 1        18
8      3 2        16
9      3 3        13

为了更好的衡量,我无法用种子复制你的数据,所以我直接这样设置它,如果其他人想要一个镜头:

txt <- "obs_id p1 p1_char1 p2 p2_char1 p3 p3_char1
1      1  1       20  2       12  3       16
2      2  1       16  2       20  3       11
3      3  1       18  2       16  3       13"

data <- read.table(text = txt, header = TRUE)

推荐阅读