我正在根据列名将data.frame拆分成一个列表。我想要的是将id列(id
(不仅包括在一个项目中,还包括在结果列表的所有元素中。
目前,我正在通过map
和bind_cols
(通过Map
/do.call
/mapply
等的替代方案,我自己也可以这样做(将id
列后续绑定到列表的所有项目。我想知道的是,是否有任何规范的方法可以直接执行,可能使用split.default
的函数参数,也可能直接通过其他函数,从而节省两到三个额外的步骤
可复制示例
df <- data.frame(
stringsAsFactors = FALSE,
id = c("A", "B", "C"),
nm1_a = c(928L, 476L, 928L),
nm1_b = c(61L, 362L, 398L),
nm2_a = c(965L, 466L, 369L),
nm2_b = c(240L, 375L, 904L),
nm3_a = c(429L, 730L, 788L),
nm3_b = c(99L, 896L, 540L),
nm3_c = c(463L, 143L, 870L)
)
df
#> id nm1_a nm1_b nm2_a nm2_b nm3_a nm3_b nm3_c
#> 1 A 928 61 965 240 429 99 463
#> 2 B 476 362 466 375 730 896 143
#> 3 C 928 398 369 904 788 540 870
我目前在做什么
library(tidyverse)
split.default(df[-1], gsub('^(nm\d+).*', '\1', names(df)[-1])) %>%
map(~ .x %>% bind_cols('id' = df$id, .))
#> $nm1
#> id nm1_a nm1_b
#> 1 A 928 61
#> 2 B 476 362
#> 3 C 928 398
#>
#> $nm2
#> id nm2_a nm2_b
#> 1 A 965 240
#> 2 B 466 375
#> 3 C 369 904
#>
#> $nm3
#> id nm3_a nm3_b nm3_c
#> 1 A 429 99 463
#> 2 B 730 896 143
#> 3 C 788 540 870
我想要的是完全相同的输出,但有没有直接或更规范的方法?
为了获得多种选择,以下是您所说的不想做的。pivot/split/ppivot方法可以帮助更好地扩展和适应,而不仅仅是根据列位置来保持ID。它还利用ID来进行整形,因此如果在中间步骤中有其他操作要做,并且不确定行顺序是否会保持不变,它可能会更灵活——这也是我有时避免绑定列的原因之一。基于某个变量而不是按列组划分数据(至少对我来说(也是有意义的。
library(tidyr)
df %>%
pivot_longer(-id) %>%
split(stringr::str_extract(.$name, "^nm\d+")) %>%
purrr::map(pivot_wider, id_cols = id, names_from = name)
#> $nm1
#> # A tibble: 3 x 3
#> id nm1_a nm1_b
#> <chr> <int> <int>
#> 1 A 928 61
#> 2 B 476 362
#> 3 C 928 398
#>
#> $nm2
#> # A tibble: 3 x 3
#> id nm2_a nm2_b
#> <chr> <int> <int>
#> 1 A 965 240
#> 2 B 466 375
#> 3 C 369 904
#>
#> $nm3
#> # A tibble: 3 x 4
#> id nm3_a nm3_b nm3_c
#> <chr> <int> <int> <int>
#> 1 A 429 99 463
#> 2 B 730 896 143
#> 3 C 788 540 870
您可以使用一个临时变量,这样代码就更简洁易懂了。
common_cols <- 1
tmp <- df[-common_cols]
lapply(split.default(tmp, sub('^(nm\d+).*', '\1', names(tmp))),
function(x) cbind(df[common_cols], x))
#$nm1
# id nm1_a nm1_b
#1 A 928 61
#2 B 476 362
#3 C 928 398
#$nm2
# id nm2_a nm2_b
#1 A 965 240
#2 B 466 375
#3 C 369 904
#$nm3
# id nm3_a nm3_b nm3_c
#1 A 429 99 463
#2 B 730 896 143
#3 C 788 540 870
这应该只是两个步骤,拆分和替换。
Map(`[<-`, split.default(df[-1], substr(names(df)[-1], 1, 3)), 'id', value=df[1])
# $nm1
# nm1_a nm1_b id
# 1 928 61 A
# 2 476 362 B
# 3 928 398 C
#
# $nm2
# nm2_a nm2_b id
# 1 965 240 A
# 2 466 375 B
# 3 369 904 C
#
# $nm3
# nm3_a nm3_b nm3_c id
# 1 429 99 463 A
# 2 730 896 143 B
# 3 788 540 870 C