You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何简化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

错误原因分析

  1. 管道符%>%后直接使用names(.) <- ...赋值语法错误,管道符需接函数调用而非赋值语句。
  2. 循环for (i in length(df_list1))仅会执行一次,应使用seq_along(df_list1)遍历所有列表元素。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 08:27:09