Select 数据框列基于列名中的数字

Select dataframe columns based on number in column name

我有一组数据框,每个数据框都有很多列。在每个数据框中,一些列被命名为 Test_1Test_2Test_3 等,测试列的总数不同。

一些(但不是全部!)测试有关联的结果。结果在它们自己的列中,命名为 Test_1_ResultTest_2_Result 等。结果总是按顺序排列,没有跳过(所以没有 Test_Result_4 没有 Test_Result_3)。

数据框中可能还有任意数量的其他非 test/result 列。

这是一个玩具示例:

df <- data.frame(
  Name = c("A", "B", "C"),
  Test_1 = c("Standard", "Standard", "Standard"),
  Test_1_Result = c("Pass", "Fail", "Pass"),
  Test_2 = c("Sepcial", "Special", "Special"),
  Test_2_Result = c("Pass", "Fail", "Fail"),
  Test_3 = c("Unknown", "Unknown", "Unknown"),
  Test_4 = c(NA, NA, NA),
  Col_1 = c(1, 2, 3),
  Col_2 = c(1.2, 2.2, 3.2),
  Other_Col = c(1.3, 2.3, 3.3)
)

我想做的是删除没有相应结果列的测试列,最好使用 dplyr::select.

我已经能够像这样识别出最高的结果列,但没有进一步

library(stringr)
numbs <- str_extract(names(df), "\d_")[is.na(str_extract(names(df), "\d_")) == FALSE]
numbs <- as.numeric(str_remove(numbs, "_"))
paste0("Test_", max(numbs), "_Result")
[1] "Test_2_Result"

我想要的是这个(Test_3Test_4 已删除)。

df_target <- data.frame(
  Name = c("A", "B", "C"),
  Test_1 = c("Standard", "Standard", "Standard"),
  Test_1_Result = c("Pass", "Fail", "Pass"),
  Test_2 = c("Sepcial", "Special", "Special"),
  Test_2_Result = c("Pass", "Fail", "Fail"),
  Col_1 = c(1, 2, 3),
  Col_2 = c(1.2, 2.2, 3.2),
  Other_Col = c(1.3, 2.3, 3.3)
)

我知道在这个玩具示例中,使用 dplyr::select(-Test_3, -Test_4) 很简单,但实际数据框将有很多测试列需要删除。我想避免手动输入它们。

忽略列顺序的一个选项可能是:

df %>%
 select(which(duplicated(str_remove(names(.), "_Result"), fromLast = TRUE)),
        matches("^Name|_Result$|^Col|^Other_col"))

    Test_1  Test_2 Name Test_1_Result Test_2_Result Col_1 Col_2 Other_Col
1 Standard Sepcial    A          Pass          Pass     1   1.2       1.3
2 Standard Special    B          Fail          Fail     2   2.2       2.3
3 Standard Special    C          Pass          Fail     3   3.2       3.3

这一点都不优雅,但会删除所需的列。这假设 Test_*Test_*_Result 列一致 naming/structure。

library(stringr)
library(dplyr)

test_cols <- str_remove(str_subset(names(df), "^Test_"), "_Result")
keep_these <- test_cols[duplicated(test_cols)]
drop_these <- test_cols[!test_cols %in% keep_these]
df %>% 
  select(-all_of(drop_these))

  Name   Test_1 Test_1_Result  Test_2 Test_2_Result Col_1 Col_2 Other_Col
1    A Standard          Pass Sepcial          Pass     1   1.2       1.3
2    B Standard          Fail Special          Fail     2   2.2       2.3
3    C Standard          Pass Special          Fail     3   3.2       3.3