过滤掉数据框列表中没有 Z 列的所有数据框?

Filter out all data frames which don't have the column Z in a list of data frames?

我有一个包含六个数据框的列表,其中 5/6 个数据框有一列“Z”。要继续我的脚本,我需要删除没有 Z 列的数据框,所以我尝试了以下代码:

for(i in 1:length(df)){
  if(!("Z" %in% colnames(df[[i]])))
  {
    df[[i]] = NULL
  }
}

这似乎真的可以完成这项工作(它从列表中删除了一个数据框,它没有列 Z),但是我仍然收到一条消息“df [[i] 中的错误] :下标越界”。为什么会这样,我该如何解决这个错误?

如果 df 是您的 6 个数据帧列表,您可以这样做:

df <- df[sapply(df, \(i) "Z" %in% colnames(i))]

你得到错误的原因是你的循环将减少 df 的长度,这样 i 最终将超过 df 的(新)长度。如果 df 中唯一没有列 Z 的帧是最后一帧,则不会出现错误。

基本 Filter 函数在这里运行良好:

df <- Filter(\(x) "Z" %in% names(x), df)

至于为什么您的方法不起作用,for(i in 1:length(df)) 遍历了原始 length(df) 中的每个项目。一旦 df[[i]] = NULL 发生一次,那么 df 就会比循环开始时短,因此最后一次迭代将越界。而且你还会跳过一些项目:如果 df[[2]] 被删除,那么原来的 df[[3]] 现在是 df[[2]],而当前的 df[[3]] 原来是 df[[4]],所以你跳过原来的 df[[3]] 而没有检查它。经验教训:不要在遍历对象的过程中更改对象的长度。

使用discard

list_df <- list(df1, df2)
purrr::discard(list_df, ~any(colnames(.x) == "Z"))

输出:

[[1]]
  A B
1 1 3
2 3 4

如您所见,它删除了第一个包含 Z 列的数据框。

数据

df1 <- data.frame(A = c(1,2),
                  Z = c(1,4))

df2 <- data.frame(A = c(1,3),
                  B = c(3,4))