如何简化R语言中利用子串为数据框命名的操作?
批量读取CSV并简化数据框名称的优化方案
问题背景
有两个文件夹data/ct1和data/ct2,每个文件夹内有14-15个CSV文件(示例:ct1_01_examinee.csv、ct1_02_disease.csv等)。需要将数据框命名简化为ct1_01、ct1_02这类前缀,同时处理同前缀多文件的情况(如ct1_13_ffq.csv对应ct1_13,ct1_13_ffqnutri.csv对应ct1_13_1)。原手动逐个赋值的代码过于冗余,尝试优化时出现Error: attempt to apply non-function错误。
原冗余代码
library(plyr); library(dplyr) library(magrittr) library(ExclusionTable) library(lubridate) library(tidyverse) # the HEXA cohort (1 of 3) ## ct1 flnames1<-list.files(path = "C:/Users/aaa/Documents/data/ct1", pattern = "\\.csv", full.names=TRUE) df_list1<-lapply(flnames1, read.csv) ## ct2 flnames2<-list.files(path = "C:/Users/aaa/Documents/data/ct2", pattern = "\\.csv", full.names=TRUE) df_list2<-lapply(flnames2, read.csv) names(df_list1) <- gsub(".csv","",list.files("C:/Users/aaa/Documents/data/ct1",full.names = FALSE),fixed = TRUE) ct1_01 <- as.data.frame(df_list1$ct1_01_examinee) ct1_02 <- as.data.frame(df_list1$ct1_02_disease) ct1_03 <- as.data.frame(df_list1$ct1_03_oper) ct1_04 <- as.data.frame(df_list1$ct1_04_drug) ct1_08 <- as.data.frame(df_list1$ct1_08_habit1) ct1_09 <- as.data.frame(df_list1$ct1_09_habit2) ct1_11 <- as.data.frame(df_list1$ct1_11_femd) ct1_13 <- as.data.frame(df_list1$ct1_13_ffq) ct1_13_1 <- as.data.frame(df_list1$ct1_13_ffqnutri) ct1_14 <- as.data.frame(df_list1$ct1_14_anthropometry) ct1_15 <- as.data.frame(df_list1$ct1_15_biochem) names(df_list2) <- gsub(".csv","",list.files("C:/Users/aaa/Documents/data/ct2",full.names = FALSE),fixed = TRUE) ct2_01 <- as.data.frame(df_list2$ct2_01_examinee) ct2_02 <- as.data.frame(df_list2$ct2_02_disease) ct2_04 <- as.data.frame(df_list2$ct2_04_drug) ct2_08 <- as.data.frame(df_list2$ct2_07_habit1) ct2_09 <- as.data.frame(df_list2$ct2_08_habit2) ct2_11 <- as.data.frame(df_list2$ct2_10_femd) ct2_12 <- as.data.frame(df_list2$ct2_12_ffq) ct2_12_1 <- as.data.frame(df_list2$ct2_12_ffqnutri) ct2_13 <- as.data.frame(df_list2$ct2_13_anthropometry) ct2_14 <- as.data.frame(df_list2$ct2_14_biochem) ct1_list <- list( ct1_01, ct1_02, ct1_03, ct1_04, ct1_08, ct1_09, ct1_11, ct1_13, ct1_13_1, ct1_14, ct1_15 ) ct2_list <- list( ct2_01, ct2_02, ct2_04, ct2_08, ct2_09, ct2_11, ct2_12, ct2_12_1, ct2_13, ct2_14 ) ct_list <- append(ct1_list, ct2_list) ct <- join_all(ct_list, by = "RID", type = "inner", match = "first")# length(unique(ct$RID)) #Inclusion and Exclusion criteria fu <- join_all(list(ct1_01, ct2_01), by = c('RID'), type = "inner", ) fu_loss <- ct1_01 %>% anti_join(ct2_01, by = ('RID'))
错误优化代码及报错
尝试用str_sub优化时的错误代码:
## ct1 flnames1<-list.files(path = "C:/Users/aaa/Documents/data/ct1", pattern = "\\.csv", full.names=TRUE) df_list1<-lapply(flnames1, read.csv) %>% names(.) <- gsub(".csv","",list.files("C:/Users/aaa/Documents/data/ct1",full.names = FALSE),fixed = TRUE) for (i in length(df_list1)){ str_sub(names(df_list1)[i], 0, 6) <- as.data.frame(df_list1$names(df_list1)[i]) }
报错信息:
Error: attempt to apply non-function
错误原因分析
- 管道符
%>%后直接使用names(.) <- ...赋值语法错误,管道符需接函数调用而非赋值语句。 - 循环
for (i in length(df_list1))仅会执行一次,应使用seq_along(df_list1)遍历所有列表元素。 str_sub用于提取字符串,不能用来赋值数据框;且df_list1$names(df_list1)[i]写法错误,需用df_list1[[names(df_list1)[i]]]访问列表元素。
优化方案
使用自定义函数批量处理文件读取、名称简化,代码简洁且符合需求:
完整优化代码
# 加载所需包 library(plyr) library(dplyr) library(magrittr) library(ExclusionTable) library(lubridate) library(tidyverse) # 自定义函数:读取指定文件夹的CSV并生成简化名称 read_and_rename <- function(folder_path) { # 获取文件完整路径和基础文件名 filenames <- list.files(path = folder_path, pattern = "\\.csv$", full.names = TRUE) base_names <- list.files(path = folder_path, pattern = "\\.csv$", full.names = FALSE) # 提取文件名前缀(如ct1_01、ct1_13) prefixes <- gsub("^(ct\\d+_\\d+).*\\.csv$", "\\1", base_names) # 对同前缀文件自动添加序号(如ct1_13、ct1_13_1) simplified_names <- ave(prefixes, prefixes, FUN = function(x) { if (length(x) == 1) x else paste0(x, "_", seq_along(x)) }) # 读取文件到列表并设置名称 df_list <- lapply(filenames, read.csv) names(df_list) <- simplified_names return(df_list) } # 处理两个文件夹 ct1_list <- read_and_rename("C:/Users/aaa/Documents/data/ct1") ct2_list <- read_and_rename("C:/Users/aaa/Documents/data/ct2") # 可选:将列表中的数据框导出到全局环境(可直接调用ct1_01等对象) list2env(ct1_list, envir = .GlobalEnv) list2env(ct2_list, envir = .GlobalEnv) # 合并所有数据框(沿用原逻辑) ct_list <- c(ct1_list, ct2_list) ct <- join_all(ct_list, by = "RID", type = "inner", match = "first") # 纳入排除标准处理(沿用原逻辑) fu <- join_all(list(ct1_01, ct2_01), by = "RID", type = "inner") fu_loss <- ct1_01 %>% anti_join(ct2_01, by = "RID")
代码说明
- 自定义函数
read_and_rename:一次性完成文件读取、名称生成、列表命名,避免重复代码。 - 正则表达式匹配:
^(ct\\d+_\\d+).*\\.csv$精准提取ctX_XX格式的前缀,忽略后续描述和.csv后缀。 - 自动序号生成:
ave函数对相同前缀的文件自动添加序号,匹配你手动命名的规则(如ct1_13_ffq.csv→ct1_13,ct1_13_ffqnutri.csv→ct1_13_1)。 list2env函数:将列表中的数据框导出到全局环境,可直接像之前一样调用ct1_01等对象,无需手动逐个赋值。- 后续合并、纳入排除处理完全沿用原有逻辑,保证业务流程不变。
内容的提问来源于stack exchange,提问作者HJ WHY
相关产品推荐
相关产品推荐

