如何用case_when和for循环重组列表中的tibble?
问题:按分组处理列表中的tibble时出现列不存在报错
我有一个包含15个tibble的列表,这些tibble分属3种不同列数的格式。我想通过for循环遍历所有tibble,根据它们的分组(已按tibble名称分到不同向量里)执行对应的列选择和重命名操作,但运行代码时报错了。
示例代码
library(tidyverse) # 我的数据示例 df.1 <- tibble(x = 1:5, y = 1, z = 4:8) df.2 <- tibble(x= 1:8, y = 2, z = 5, r = 7, d = 9) df.3 <- tibble(x = 1:4, y = 3, z = 8, r = 2, d = 8) df.4 <- tibble(x=1:3, y = 1, z = 0, r = 4) df.list <- list(df.1, df.2, df.3, df.4) names(df.list) <- c("a", "b", "c", "d") # 按tibble名称划分的分组 group1 <- c("a") group2 <- c("b", "c") group3 <- c("d") # 我尝试运行的for循环 for(i in seq_along(df.list)){ df.list[[i]] <- case_when( names(df.list[i])%in%group1 ~ df.list[[i]] %>% dplyr::select(c(3, 2)) %>% rename("yy" = "y", "zz" = "z"), names(df.list[1])%in%group2 ~ df.list[[i]] %>% dplyr::select(c(3,4)) %>% rename("zz" = "z", "rr" = "r"), names(df.list[i])%in%group3 ~ df.list[[i]] %>% dplyr::select(c(1, 4, 3)) %>% rename("zz"= "z") ) }
报错信息
Error in `dplyr::select()`: ! Can't subset columns past the end. ℹ Location 4 doesn't exist. ℹ There are only 3 columns.
问题原因与解决方案
错误根源
- case_when执行逻辑问题:case_when会尝试计算所有分支的表达式,哪怕条件不满足。比如处理group1的tibble(只有3列)时,group2分支里的
select(c(3,4))会试图选择第4列,直接触发报错。 - 代码笔误:第二个条件里错误地写了
names(df.list[1]),应该是names(df.list[i]),否则永远只会判断第一个元素的名称是否在group2里。
修正方案1:用if-else替代case_when(适合新手理解)
if-else是分支执行,满足哪个条件就只运行对应的代码块,不会触发其他分支的操作,避免列不存在的错误:
library(tidyverse) # 示例数据(同上) df.1 <- tibble(x = 1:5, y = 1, z = 4:8) df.2 <- tibble(x= 1:8, y = 2, z = 5, r = 7, d = 9) df.3 <- tibble(x = 1:4, y = 3, z = 8, r = 2, d = 8) df.4 <- tibble(x=1:3, y = 1, z = 0, r = 4) df.list <- list(df.1, df.2, df.3, df.4) names(df.list) <- c("a", "b", "c", "d") group1 <- c("a") group2 <- c("b", "c") group3 <- c("d") # 修正后的for循环 for(i in seq_along(df.list)){ current_name <- names(df.list)[i] current_df <- df.list[[i]] if(current_name %in% group1){ df.list[[i]] <- current_df %>% select(3, 2) %>% rename(yy = y, zz = z) } else if(current_name %in% group2){ df.list[[i]] <- current_df %>% select(3,4) %>% rename(zz = z, rr = r) } else if(current_name %in% group3){ df.list[[i]] <- current_df %>% select(1, 4, 3) %>% rename(zz = z) } } # 查看处理后的结果 df.list
修正方案2:用purrr::map2实现(tidyverse风格)
如果习惯tidyverse的函数式编程,可以用map2同时遍历列表元素和它们的名称,这里的case_when会在每个元素的上下文里执行,不会触发无关分支:
library(tidyverse) # 示例数据与分组(同上) df.1 <- tibble(x = 1:5, y = 1, z = 4:8) df.2 <- tibble(x= 1:8, y = 2, z = 5, r = 7, d = 9) df.3 <- tibble(x = 1:4, y = 3, z = 8, r = 2, d = 8) df.4 <- tibble(x=1:3, y = 1, z = 0, r = 4) df.list <- list(df.1, df.2, df.3, df.4) names(df.list) <- c("a", "b", "c", "d") group1 <- c("a") group2 <- c("b", "c") group3 <- c("d") # 用map2处理列表 df.list_processed <- map2(df.list, names(df.list), function(df, name){ case_when( name %in% group1 ~ df %>% select(3,2) %>% rename(yy=y, zz=z), name %in% group2 ~ df %>% select(3,4) %>% rename(zz=z, rr=r), name %in% group3 ~ df %>% select(1,4,3) %>% rename(zz=z), TRUE ~ df # 可选:处理不属于任何分组的tibble ) }) # 保留原名称 names(df.list_processed) <- names(df.list) # 查看结果 df.list_processed
内容的提问来源于stack exchange,提问作者Bryce Cook
相关产品推荐
相关产品推荐

