将几块列动态移动到另一个位置

Move several chunks of columns dynamically to another position

我的数据是:

df <- data.frame(a   = 1:2,
                 x   = 1:2,
                 b   = 1:2,
                 y   = 3:4,
                 x_2 = 1:2,
                 y_2 = 3:4,
                 c   = 1:2,
                 x_3 = 5:6,
                 y_3 = 1:2)

我现在想将 x 变量和 y 变量放在一起,以便列的顺序为:

a, x, x_2, x_3, b, y, y_2, y_3, c

我想,我可以将 tidyverse 的 relocate 函数与 lapply 或 map 或 reduce (?) 结合使用,但没有成功。

例如如果我这样做:

move_names <- c("x", "y")

library(tidyverse)
moved_data <- lapply(as.list(move_names), function(x)
{
  df <- df |> 
    relocate(!!!syms(paste0(x, "_", 2:3)),
             .after = all_of(x))
}
)

它分别移动 x 和 y,但它创建了单独的列表,但我只想拥有带有重定位列的原始 df。

更新:

我应该清楚我的真实数据框有大约 500 列,其中到处都是要移动的列。因此,提供所需列名顺序的完整向量是不可行的。

我拥有的是:我有原始列的名称,即 x 和 y,我有要移动的列的名称,即 x_2、x_3 , y_2, y_3.

不确定这是否是您想要的。

具有列名顺序的向量

假设您有一个包含列顺序的向量 relocate_name

library(tidyverse)

relocate_name <- c("a", "x", "x_2", "x_3", "b", "y", "y_2", "y_3", "c")

df %>% relocate(any_of(relocate_name))

带有列名前缀的向量

或者如果你只有订单的前缀,我们就称它为relocate_name2:

relocate_name2 <- c("a", "x", "b", "y", "c")

df %>% relocate(starts_with(relocate_name2))

将 x 和 y 组合在一起

或者如果您只想将 xy“组合”在一起:

df %>% 
  relocate(starts_with("x"), .after = "x") %>% 
  relocate(starts_with("y"), .after = "y")

输出

以上输出都是一样的

  a x x_2 x_3 b y y_2 y_3 c
1 1 1   1   5 1 3   3   1 1
2 2 2   2   6 2 4   4   2 2
library(rlist)
# split based in colname-part before _
L <- split.default(df, f = gsub("(.*)_.*", "\1", names(df)))

# remove names with an underscore
# this is the new order, it should match the names of list L !!
neworder <- names(df)[!grepl("_", names(df))]
# [1] "a" "x" "b" "y" "c"

# cbind list elements together
ans <- rlist::list.cbind(L[neworder])
# a x.x x.x_2 x.x_3 b y.y y.y_2 y.y_3 c
# 1 1   1     1     5 1   3     3     1 1
# 2 2   2     2     6 2   4     4     2 2

# create tidy names again
names(ans) <- gsub(".*\.(.*)", "\1", names(ans))
#   a x x_2 x_3 b y y_2 y_3 c
# 1 1 1   1   5 1 3   3   1 1
# 2 2 2   2   6 2 4   4   2 2

在基数 R 中:

df[match(c('a', 'x', 'x_2', 'x_3', 'b', 'y', 'y_2', 'y_3', 'c'), names(df))]
#>   a x x_2 x_3 b y y_2 y_3 c
#> 1 1 1   1   5 1 3   3   1 1
#> 2 2 2   2   6 2 4   4   2 2

好吧,这可能是有史以来最糟糕的解决方法,我真的不明白我到底在做什么(尤其是 <<-),但它确实有效。

在你们这里​​的帮助下更多地意识到问题后,我的一般想法是“循环”我的 x 和 y 名称,从列名称向量中删除这些新的 _2 和 _3 列,然后re-append 他们在他们的“基础”x 和 y 列之后。

search_names <- c("x", "y")

df_names <- names(df)

new_names <- lapply(search_names, function(x)
{
  start <- which(df_names == x)

  without_new_names <- setdiff(df_names, paste0(x, "_", 2:3))
  
  df_names <<- append(without_new_names, values = paste0(x, "_", 2:3), after = start)
})[[length(search_names)]]

df |> 
  relocate(any_of(new_names))

  a x x_2 x_3 b y y_2 y_3 c
1 1 1   1   5 1 3   3   1 1
2 2 2   2   6 2 4   4   2 2