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

R语言循环脚本优化及起始列调整技术咨询

问题

我有一段可正常运行的R脚本,功能如下:遍历包含分类单元名称的dataframe,为每个分类单元名称查找对应的数值ID;若ID为NA,则保留原dataframe中的名称,最终将结果写入新的dataframe。该脚本可正常工作,但写法较为繁琐,希望获取优化或简化的建议(不强制使用taxize包)。同时想咨询:若使用该脚本(或其他方案),如何将循环的起始列从第2列改为第4列?


示例数据

数据表格

KingdomPhylumClassOrderFamilyGenus
BacteriaFirmicutesClostridiaEubacterialesLachnospiraceaeDorea
BacteriaFirmicutesClostridiaEubacterialesOscillospiraceaeGGB9634
BacteriaFirmicutesClostridiaEubacterialesClostridiaceaeClostridiaceae_unclassified

数据代码

taxa <- structure(list(Kingdom = c("Bacteria", "Bacteria", "Bacteria"
), Phylum = c("Firmicutes", "Firmicutes", "Firmicutes"), Class = c("Clostridia", 
"Clostridia", "Clostridia"), Order = c("Eubacteriales", "Eubacteriales", 
"Eubacteriales"), Family = c("Lachnospiraceae", "Oscillospiraceae", 
"Clostridiaceae"), Genus = c("Dorea", "GGB9634", "Clostridiaceae_unclassified"
)), row.names = c(NA, -3L), class = "data.frame")

现有脚本

sad <- taxa[1:3,] # 取上述dataframe的前3行
numID <- data.frame(sad$Kingdom) # 存储ID的dataframe
taxize::taxize_options(ncbi_sleep = 0.9) # 调整HTTP请求频率

for(r in 1:length(sad[,1])){
  for(c in 2:length(sad[1,])){
    sadID <- taxize::get_uid(sad[r,c], ask=F)[1]
    if(is.na(sadID)){
      numID[r,c] <- sad[r,c]
    } else {
      numID[r,c] <- sadID
    }
  }}
names(numID) <- names(sad)

# 期望输出
structure(list(Kingdom = c("Bacteria", "Bacteria", "Bacteria"),
Phylum = c("Firmicutes", "Firmicutes", "Firmicutes"), 
Class = c("186801","186801", "186801"),
Order = c("186802", "186802", "186802"), 
Family = c("186803", "216572", "31979"), 
Genus = c("189330","GGB9634", "Clostridiaceae_unclassified")), row.names = c(NA,3L), class = "data.frame")

解决方案

一、脚本优化建议

1. 使用dplyr + purrr替代嵌套循环

用mutate(across(...))批量处理指定列,结合map函数实现单值查询,代码更简洁易读:

library(dplyr)
library(purrr)
library(taxize)

taxize_options(ncbi_sleep = 0.9)

# 定义查询函数:获取ID,NA则返回原名称
get_taxid_or_name <- function(taxon_name) {
  taxid <- get_uid(taxon_name, ask = F)[1]
  ifelse(is.na(taxid), taxon_name, as.character(taxid))
}

# 批量处理列(示例默认从第2列开始)
numID <- sad %>%
  mutate(across(2:ncol(.), ~ map_chr(., get_taxid_or_name)))

2. 缓存查询结果减少重复API请求

如果数据中有大量重复的分类单元名称,用memoise包缓存查询结果,避免重复调用API,大幅提升效率:

library(memoise)

# 缓存查询函数
memoised_get_taxid <- memoise(get_uid)

get_taxid_or_name_cached <- function(taxon_name) {
  taxid <- memoised_get_taxid(taxon_name, ask = F)[1]
  ifelse(is.na(taxid), taxon_name, as.character(taxid))
}

# 使用缓存函数处理
numID <- sad %>%
  mutate(across(2:ncol(.), ~ map_chr(., get_taxid_or_name_cached)))

3. 基础R版本优化(不依赖tidyverse)

如果不想用tidyverse包,可以用apply系列函数替代嵌套循环:

library(taxize)
taxize_options(ncbi_sleep = 0.9)

get_taxid_or_name <- function(x) {
  taxid <- get_uid(x, ask = F)[1]
  ifelse(is.na(taxid), x, as.character(taxid))
}

# 处理指定列,返回矩阵后转成dataframe
processed_cols <- apply(sad[,2:ncol(sad)], c(1,2), get_taxid_or_name)
numID <- cbind(sad[,1, drop = FALSE], processed_cols)
names(numID) <- names(sad)

二、修改循环起始列为第4列的方法

1. 原脚本修改方式

直接调整内层循环的起始值,同时初始化numID时保留前3列的原始值:

sad <- taxa[1:3,]
# 初始化时保留前3列原始数据
numID <- sad[,1:3, drop = FALSE]
taxize::taxize_options(ncbi_sleep = 0.9)

for(r in 1:nrow(sad)){
  # 循环从第4列开始到最后一列
  for(c in 4:ncol(sad)){
    sadID <- taxize::get_uid(sad[r,c], ask=F)[1]
    numID[r,c] <- ifelse(is.na(sadID), sad[r,c], sadID)
  }}

2. 优化后脚本修改方式

不管是tidyverse还是基础R版本,只需修改处理列的范围:

tidyverse版本:

numID <- sad %>%
  # 处理第4列到最后一列
  mutate(across(4:ncol(.), ~ map_chr(., get_taxid_or_name_cached)))

基础R版本:

processed_cols <- apply(sad[,4:ncol(sad)], c(1,2), get_taxid_or_name)
numID <- cbind(sad[,1:3, drop = FALSE], processed_cols)
names(numID) <- names(sad)

内容的提问来源于stack exchange,提问作者Z U

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:14:54