需要:从宽到长的数据透视,堆叠相应列的组
本质上,我有3组5列,需要将相应列中的每一列堆叠成1列(即,3组中的第一个变量变为1列,每一组中的第二个变量为第二列,等等(。例如,我需要:列#2,7,&12都在1列,3,8,&13在下一列中。。。6,11,&一列共16个。
数据结构:我有一个类似于以下的数据集:
df <- tibble(
pid = c(1, 2, 3, 4),
v1_1 = c(19, NA, NA, NA),
v1_2 = c(12, NA, NA, NA),
v2_1 = c(15, NA, NA, NA),
v2_2 = c(19, NA, NA, NA),
v1_entry_3 = c(11, NA, NA, NA),
v1_1_1 = c(NA, NA, 36, NA),
v1_2_1 = c(NA, NA, 35, NA),
v2_1_1 = c(NA, NA, 31, NA),
v2_2_1 = c(NA, NA, 39, NA),
v1_entry_3_1 = c(NA, NA, 33, NA),
v1_1_2 = c(NA, 26, NA, 41),
v1_2_2 = c(NA, 29, NA, 44),
v2_1_2 = c(NA, 21, NA, 42),
v2_2_2 = c(NA, 20, NA, 45),
v1_entry_3_2 = c(NA, 22, NA, 44),
age = c(19, 21, 33, 47)
)
最后,我需要这样的数据:
df_t <- tibble(
pid = c(1, 2, 3, 4),
v1_1 = c(19, 26, 36, 41),
v1_2 = c(12, 29, 35, 44),
v2_1 = c(15, 21, 31, 42),
v2_2 = c(19, 20, 39, 45),
v1_entry_3 = c(11, 22, 33, 44),
age = c(19, 21, 33, 47)
)
您需要在第二个下划线之前的所有内容上匹配列名:
library(tidyr)
df %>%
pivot_longer(
-c(pid, age),
names_pattern = "([^_]*_[^_]*)",
names_to = ".value",
values_drop_na = TRUE
)
# A tibble: 4 x 7
pid age v1_1 v1_2 v2_1 v2_2 v1_entry
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 19 19 12 15 19 11
2 2 21 26 29 21 20 22
3 3 33 36 35 31 39 33
4 4 47 41 44 42 45 44
在执行pivot_longer
之前,请考虑重命名某些列
library(dplyr)
library(stringr)
library(tidyr)
df %>%
rename_with(~ str_c(., '_0'), matches("^v\d+_\d+$|^v\d+_entry_\d+$")) %>%
pivot_longer(cols = -c(pid, age), names_to = c(".value"),
names_pattern = "(.*)_\d+$", values_drop_na = TRUE)
# A tibble: 4 x 7
pid age v1_1 v1_2 v2_1 v2_2 v1_entry_3
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 19 19 12 15 19 11
2 2 21 26 29 21 20 22
3 3 33 36 35 31 39 33
4 4 47 41 44 42 45 44
这里有一个基本的R解决方案:
colnames <- startsWith(names(df), "v")
cbind(df[!colnames],
do.call(cbind, lapply(split.default(df[colnames], gsub("(v\d_\d|[[:alpha:]]+)_.*", "\1", names(df)[colnames])),
function(x) apply(x, 1, (x) x[!is.na(x)]))))
pid age v1_1 v1_2 v1_entry v2_1 v2_2
1 1 19 19 12 11 15 19
2 2 21 26 29 22 21 20
3 3 33 36 35 33 31 39
4 4 47 41 44 44 42 45
为了完整起见,这里有一种使用melt()
函数的方法:
library(data.table)
cols <- names(df)[2:6]
melt(setDT(df), measure = patterns(cols), value.name = cols, na.rm = TRUE)[order(pid)]
pid age variable v1_1 v1_2 v2_1 v2_2 v1_entry_3 1: 1 19 1 19 12 15 19 11 2: 2 21 3 26 29 21 20 22 3: 3 33 2 36 35 31 39 33 4: 4 47 3 41 44 42 45 44
在这里,我们受益于这样一个事实,即要重新整形的第一组列的列名可以重新用作重新整形输出的列名。