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

R语言如何将webchem的PubChem查询结果格式化转为dataframe

webchem包ci_query嵌套结果转dataframe实现方案

ci_query返回结果为三级嵌套结构,按字段类型拆分处理即可,不需要硬转成单张宽表(嵌套的多行tibble硬塞宽表会造成严重数据冗余),以下是可直接复用的代码,兼容3500个CAS号批量查询场景。

单条结果解析逻辑

返回的9个字段分三类处理:

  • 单值标量字段(cas、inchi、inchikey、smiles、source_url):每个CAS号对应唯一值,直接提取作为主表列
  • 多值向量字段(name、synonyms):每个CAS号对应多个名称/同义词,可先用分隔符合并为单字符串存入主表,需要长表时再拆分
  • 嵌套表格字段(toxicity、physprop):本身为多行结构,单独存为带CAS关联键的长表,后续通过CAS号和主表关联即可

先写单条结果解析的容错函数,自动处理查询失败返回空的情况:

library(tidyverse)
library(webchem)

# 单条结果解析辅助函数
parse_ci_result <- function(ci_res, query_id) {
  # 查询为空时返回占位结果
  if(is.null(ci_res) | length(ci_res) == 0) {
    return(list(
      main_info = tibble(cas_query = query_id, query_status = "no_result"),
      toxicity = tibble(cas = query_id),
      physprop = tibble(cas = query_id)
    ))
  }
  
  # 提取单值基础字段
  main_single <- tibble(
    cas_query = query_id,
    cas = ifelse(is.null(ci_res$cas), NA_character_, ci_res$cas[1]),
    inchi = ifelse(is.null(ci_res$inchi), NA_character_, ci_res$inchi[1]),
    inchikey = ifelse(is.null(ci_res$inchikey), NA_character_, ci_res$inchikey[1]),
    smiles = ifelse(is.null(ci_res$smiles), NA_character_, ci_res$smiles[1]),
    source_url = ifelse(is.null(ci_res$source_url), NA_character_, ci_res$source_url[1]),
    query_status = "success"
  )
  
  # 合并多值向量字段,用|做分隔符
  main_multi <- tibble(
    cas_query = query_id,
    common_names = ifelse(is.null(ci_res$name), NA_character_, paste(ci_res$name, collapse = "|")),
    synonyms = ifelse(is.null(ci_res$synonyms), NA_character_, paste(ci_res$synonyms, collapse = "|"))
  )
  
  main_info <- main_single %>% left_join(main_multi, by = "cas_query")
  
  # 处理嵌套毒性表,加CAS关联键
  toxicity_tab <- if(is.null(ci_res$toxicity) | nrow(ci_res$toxicity) == 0) {
    tibble(cas = ifelse(is.null(ci_res$cas), query_id, ci_res$cas[1]))
  } else {
    ci_res$toxicity %>% mutate(cas = ifelse(is.null(ci_res$cas), query_id, ci_res$cas[1]), .before = 1)
  }
  
  # 处理嵌套理化性质表,加CAS关联键
  physprop_tab <- if(is.null(ci_res$physprop) | nrow(ci_res$physprop) == 0) {
    tibble(cas = ifelse(is.null(ci_res$cas), query_id, ci_res$cas[1]))
  } else {
    ci_res$physprop %>% mutate(cas = ifelse(is.null(ci_res$cas), query_id, ci_res$cas[1]), .before = 1)
  }
  
  return(list(
    main_info = main_info,
    toxicity = toxicity_tab,
    physprop = physprop_tab
  ))
}

# 单条测试
y1 <- ci_query('50-00-0', from = 'rn')
parsed_y1 <- parse_ci_result(y1[['50-00-0']], query_id = '50-00-0')

运行后得到三个结构化对象:

  • parsed_y1$main_info:1行的化合物基础信息宽表,每个CAS号对应一行
  • parsed_y1$toxicity:毒性数据长表,每条毒性测试记录对应一行,带CAS号做关联
  • parsed_y1$physprop:理化性质长表,每条理化指标对应一行,带CAS号做关联

批量处理3500个CAS号的实现

直接全量循环请求容易被站点限流、中途报错中断,以下代码加了随机停顿、错误捕获、断点续跑逻辑,中途断网/报错后重新运行不会重复请求已经抓取过的条目:

# 替换为你自己的CAS号向量
# cas_list <- c("50-00-0", "67-64-1", ......)

# 初始化存储列表,支持断点续跑
if(!exists("all_raw_res")) {
  all_raw_res <- list()
}

# 批量循环查询
for(cas in cas_list) {
  # 已查询过的条目直接跳过
  if(cas %in% names(all_raw_res)) next
  tryCatch({
    res <- ci_query(cas, from = "rn", verbose = FALSE)
    all_raw_res[[cas]] <- res[[cas]]
    # 打印进度
    cat("Finished:", cas, "Progress:", round(length(all_raw_res)/length(cas_list)*100, 2), "%\n")
    # 随机停顿0.5-2秒,避免被限流
    Sys.sleep(runif(1, 0.5, 2))
  }, error = function(e) {
    cat("Error querying:", cas, "\n")
    all_raw_res[[cas]] <<- NULL
  })
}

# 批量解析所有结果
all_parsed <- map2(all_raw_res, names(all_raw_res), ~parse_ci_result(.x, .y))

# 合并为最终三个规范dataframe
main_df <- map_dfr(all_parsed, ~.x$main_info)
toxicity_df <- map_dfr(all_parsed, ~.x$toxicity)
physprop_df <- map_dfr(all_parsed, ~.x$physprop)

补充说明

  • ci_query默认抓取ChemIDplus数据库数据,并非PubChem数据源。如果需要PubChem数据,将查询函数替换为同包的pc_query即可,上述解析逻辑完全通用,仅需对应调整字段名匹配返回结构。
  • 如果需要把常用名、同义词拆分为每行一个值的长格式,调用tidyr::separate_longer_delim(main_df, cols = c(common_names, synonyms), delim = "|")即可快速实现。
  • 不要强行将毒性、理化性质两类多行数据合并到基础信息主表,会导致单CAS号对应多行、基础字段大量冗余,大幅提升后续数据处理的出错概率,使用时通过CAS号关联三张表即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:21:29