R:解析 JSON/XML 从 Pubchem 导出的化合物属性

R: parse JSON/XML exported compound properties from Pubchem

我想使用 JSON(或 XML)导出工具解析 R 中 Pubchem 中给定化合物的所有化学性质。

示例:ALPHA-IONONE,pubchem 化合物 ID 5282108

https://pubchem.ncbi.nlm.nih.gov/compound/5282108

library("rjson")
data <- rjson::fromJSON(file="https://pubchem.ncbi.nlm.nih.gov/rest/pug_view/data/compound/5282108/JSON/?response_type=display")

library("RJSONIO")
data <- RJSONIO::fromJSON("https://pubchem.ncbi.nlm.nih.gov/rest/pug_view/data/compound/5282108/JSON/?response_type=display")

会给我一棵嵌套列表树,但是我如何从这个相当复杂的嵌套列表列表变成一个漂亮的数据框或数据框列表?

这样的话,我追求的就是

下的一切

3.1 计算描述符

3.2 其他标识符

3.3 同义词

4.1 计算属性

在数据框的一行中,每个元素在一个单独的命名列中,每个元素有多个项目(例如多个同义词)与“|”粘贴在一起作为分隔符。例如。在这种情况下,类似于

pubchemid      IUPAC_Name    InChI       InChI_Key     Canonical SMILES      Isomeric SMILES     CAS     EC Number     Wikipedia      MeSH Synonyms     Depositor-Supplied Synonyms   Molecular_Weight    Molecular_Formula    XLogP3   Hydrogen_Bond_Donor_Count ... 
5282108        (E)-4-(2,6,6-trimethylcyclohex-2-en-1-yl)but-3-en-2-one       InChI=1S/C13H20O/c1-10-6-5-9-13(3,4)12(10)8-7-11(2)14/h6-8,12H,5,9H2,1-4H3/b8-7+ ....

具有多个项目的字段,例如 Depositor-Supplied Synonyms 可以用“|”粘贴在一起,例如值可以是 ALPHA-IONONE|Iraldeine|...

二、我也想导入section 4.2.2 科瓦茨保留指数 作为数据框

pubchemid      column_class            kovats_ri
5282108        Standard non-polar      1413
5282108        Standard non-polar      1417
...
5282108        Semi-standard non-polar 1427
...

(第 4.3.1 节 GC-MS 也不错,但由于它只显示 3 个顶部峰,所以现在有点无用,所以我将跳过)

有人知道如何以优雅的方式实现这一目标吗?

PS 请注意,对于任何给定的查询,并非所有这些字段都必须存在。

二维结构和一些属性也可以从

中获得

https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/5282108/record/SDF/?record_type=2d&response_type=display

和来自

的 3D 结构

https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/5282108/record/SDF/?record_type=3d&response_type=display

数据也可以导出为 XML,使用

https://pubchem.ncbi.nlm.nih.gov/rest/pug_view/data/compound/5282108/XML/?response_type=display

如果这样会更容易一些

注意:也尝试使用 R 包 rpubchem,但那个似乎只导入了少量可用信息:

library("rpubchem")
get.cid(5282108)
CID  IUPACName CanonicalSmile MolecularFormula MolecularWeight TotalFormalCharge XLogP HydrogenBondDonorCount HydrogenBondAcceptorCount HeavyAtomCount    TPSA
2 5282108 (E)-4-(2,6,6-trimethylcyclohex-2-en-1-yl)but-3-en-2-one        C13H20O       192.297300               0                 3     0                      1                        14             17 5282108

我的建议适用于 XML 个文件,因为(感谢 XPath)我发现它们更便于遍历 select 个节点。

请注意,这既不快(测试时花了几秒钟)也不是最优的(我对每个文件解析两次 - 一次用于名称等,一次用于 Kovats Retention Index)。但我猜你会想要解析一些文件集并继续你的真正业务,过早的优化是万恶之源。

我已将主要任务放入不同的功能中。如果您想获取某个特定 pubchem 记录的数据,它们随时可用。但是如果你想一次从几条 pubchem 记录中获取数据,你可以定义指向数据的指针向量,并使用底部的示例将结果合并在一起。就我而言,矢量包含本地磁盘上文件的路径。也支持 URL,尽管我不鼓励它们(请记住,每个站点将被请求两次,如果有更多的记录,您可能希望以某种方式处理故障网络)。

您链接到的化合物在 "EC Number" 上有多个条目。它们确实相差 ReferenceNumber,但相差 Name。我不确定为什么会这样以及我应该如何处理它(您的样本输出仅包含一个 EC 编号条目),所以我将其留给了 R。R 为重复值添加了后缀并创建了 EC.Number.1 , EC.Number.2 等。这些后缀 与文件中的 ReferenceNumber 匹配,并且主数据框中的同一列可能会引用不同的 ReferenceNumber对于不同的化合物。

pubchem 似乎使用以下标签格式 <type>Value[List]。在少数地方,我硬编码了 StringValue,但也许某些化合物在同一字段中具有不同的类型。我通常不会考虑列表,除非有要求。因此,随着更多数据被抛入此代码,可能需要进一步修改。

如果您有任何问题,请 post 在评论中提出。我不确定是否应该解释该代码还是什么。

library("xml2")
library("data.table")

compound.attributes <- function(file=NULL) {
  compound <- read_xml(file)
  ns <- xml_ns(compound)
  information <- xml_find_all(compound, paste0(
    "//d1:TOCHeading[text()='Computed Descriptors'",
    " or text()='Other Identifiers'",
    " or text()='Synonyms'",
    " or text()='Computed Properties']",
    "/following-sibling::d1:Section/d1:Information"
  ), ns)

  properties <- sapply(information, function(x) {
    name <- xml_text(xml_find_one(x, "./d1:Name", ns))
    value <- ifelse(length(xml_find_all(x, "./d1:StringValueList", ns)) > 0,
                    paste(sapply(
                      xml_find_all(x, "./d1:StringValueList", ns),
                      xml_text, trim=TRUE), sep="", collapse="|"),
                    xml_text(
                      xml_find_one(x, "./*[contains(name(),'Value')]", ns),
                      trim=TRUE)
    )
    names(value) <- name
    return(value)
  })
  rm(compound, information)
  properties <- as.list(properties)
  properties$pubchemid <- sub(".*/([0-9]+)/?.*", "\1", file)
  return(data.frame(properties))
}

compound.retention.index <- function(file=NULL) {
  pubchemid <- sub(".*/([0-9]+)/?.*", "\1", file)
  compound <- read_xml(file)
  ns <- xml_ns(compound)
  information <- xml_find_all(compound, paste0(
    "//d1:TOCHeading[text()='Kovats Retention Index']",
    "/following-sibling::d1:Information"
  ), ns)
  indexes <- lapply(information, function(x) {
    name <- xml_text(xml_find_one(x, "./d1:Name", ns))
    values <- as.numeric(sapply(
      xml_find_all(x, "./*[contains(name(), 'NumValue')]", ns), 
      xml_text))

    data.frame(pubchemid=pubchemid,
               column_class=name,
               kovats_ri=values)
  })

  return( do.call("rbind", indexes) )
}

compounds <- c("./5282108.xml", "./5282148.xml", "./91754124.xml")

cd <- rbindlist(
  lapply(compounds, compound.attributes),
  fill=TRUE
)

rti <- do.call("rbind",
               lapply(compounds, compound.retention.index))