计算R中列表的每个父节点下的xmlchildren数
Calculating number of xmlchildren under each parent node for a list in R
我正在使用 R 查询带有一长串 PMID 的 PubMED。因为 entrez_fetch 一次只能做一定数量的事情,所以我将我的 ~2000 个 PMID 分解为一个包含多个向量的列表(每个约 500 长)。当我查询 PubMED 时,我从每个出版物的 XML 文件中提取信息。最后我想要的是这样的:
Original.PMID Publication.type
26956987 Journal.article
26956987 Meta.analysis
26956987 Multicenter.study
26402000 Journal.article
25404043 Journal.article
25404043 Meta.analysis
每个出版物都有一个唯一的 PMID,但可能有多个出版物类型与每个 PMID 关联(如上所示)。我可以从 XML 文件中查询 PMID 号,我可以得到每个 PMID 的发布类型。我遇到的问题是重复 PMID x 次,以便每个 PMID 都与其拥有的每个发布类型相关联。如果我的数据不在包含多个子列表的列表中(例如,如果我有 14 个批次,每个批次作为其自己的数据框),我可以通过从父 PublicationType 节点获取子节点的数量来执行此操作。但我似乎无法弄清楚如何在列表中执行此操作。
到目前为止我的代码是这样的:
library(rvest)
library(tidyverse)
library(stringr)
library(regexr)
library(rentrez)
library(XML)
pubmed<-my.data.frame
into.batches<-function(x,n) split(x,cut(seq_along(x),n,labels=FALSE))
batches<-into.batches(pubmed.fwd$PMID, 14)
headings<-lapply(1:14, function(x) {paste0("Batch",x)})
names(batches)<-headings
fwd<-sapply(batches, function(x) entrez_fetch(db="pubmed", id=x, rettype="xml", parsed=TRUE))
trial1<-lapply(fwd, function(x)
list(pub.type = xpathSApply(x, "//PublicationTypeList/PublicationType", xmlValue),
or.pmid = xpathSApply(x, "//ArticleId[@IdType='pubmed']", xmlValue)))
trial1 是我遇到的问题。这给了我一个列表,在每个批次中,我有一个 pub.type 的向量和一个 or.pmid 的向量,但它们的长度不同。
我想弄清楚每个出版物有多少子出版物类型,所以我可以重复 PMID 很多次。我目前正在使用以下代码,但它不符合我的要求:
trial1<-lapply(fwd, function(x)
list(childnodes = xpathSApply(xmlRoot(x), "count(.//PublicationTypeList/PublicationType)", xmlChildren)))
不幸的是,这只是告诉我每个批次的子节点总数,而不是每个发布(或 pmid)的子节点总数。
由于每篇文章的 ArticleId 可能是唯一的,并且每篇文章的 PublicationType 可能不止一个,请考虑迭代创建数据框而不是单独创建数据框载体。
具体来说,在 XML 文档的每个 PubmedArticle 节点上使用节点索引 [#]
,因为这是 id 和类型的共享祖先,然后所需后代的 xpath。下面创建了一个长度等于 fwd
:
的数据帧列表
trial1 <- lapply(fwd, function(doc) {
# RETRIEVE NUMBER OF ARTICLES PER EACH XML
num_of_articles <- length(xpathSApply(doc, "//PubmedArticle"))
# LOOP THROUGH EACH ARTICLE AND BIND XML VALUES TO DATAFRAME
dfList <- lapply(seq(num_of_articles), function(i)
data.frame(
Original.PMID = xpathSApply(doc, paste0("//PubmedArticle[",i,"]/descendant::ArticleId[@IdType='pubmed']"), xmlValue),
Publication.type = xpathSApply(doc, paste0("//PubmedArticle[",i,"]/descendant::PublicationTypeList/PublicationType"), xmlValue)
))
# ROW BIND ALL DFS INTO ONE
df <- do.call(rbind, dfList)
})
对于所有批次的最终主数据帧,运行 do.call(rbind, ...)
再次退出循环:
finaldf <- do.call(rbind, trial1)
我会将 XML 结果拆分为单独的 Article 节点并应用 xpath 函数来获取 pmid 和 pubtypes。
pmids <- c(11677608, 22328765 ,11337471)
res <- entrez_fetch(db="pubmed", rettype="xml", id = pmids)
doc <- xmlParse(res)
x <- getNodeSet(doc, "//PubmedArticle")
x1 <- sapply(x, xpathSApply, ".//ArticleId[@IdType='pubmed']", xmlValue)
x2 <- sapply(x, xpathSApply, ".//PublicationType", xmlValue)
data.frame( pmid= rep(x1, sapply(x2, length) ), pubtype = unlist(x2) )
pmid pubtype
1 11677608 Journal Article
2 11677608 Research Support, Non-U.S. Gov't
3 22328765 Journal Article
4 22328765 Research Support, Non-U.S. Gov't
5 11337471 Journal Article
此外,NCBI 表示如果使用超过 200 个 UID,则使用 HTTP POST 方法。 rentrez
不支持 POSTing,但您可以 运行 使用几行代码。
首先,您需要一个具有 1000 个 Pubmed ID 的载体(来自微生物基因组的 6171 个 table)
library(readr)
x <- read_tsv( "ftp://ftp.ncbi.nih.gov/genomes/GENOME_REPORTS/prokaryotes.txt",
na = "-", quote = "")
ids <- unique( x$`Pubmed ID` )
ids <- ids[ids < 1e9 & !is.na(ids)]
Post 使用 httr POST
.
的 NCBI ID
uri = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/epost.fcgi?"
response <- httr::POST(uri, body= list(id = paste(ids, collapse=","), db = "pubmed"))
按照entrez_post
中的代码解析结果,获取网络历史。
doc <- xmlParse( httr::content(response, as="text", encoding="UTF-8") )
result <- xpathApply(doc, "/ePostResult/*", xmlValue)
names(result) <- c("QueryKey", "WebEnv")
class(result) <- c("web_history", "list")
最后,获取最多 10K 条记录(如果超过 10K,则使用 retstart
选项循环)
res <- entrez_fetch(db="pubmed", rettype="xml", web_history=result)
doc <- xmlParse(res)
在我的笔记本电脑上 运行 这些只需要一秒钟。
articles <- getNodeSet(doc, "//PubmedArticle")
x1 <- sapply(articles, xpathSApply, ".//ArticleId[@IdType='pubmed']", xmlValue)
x2 <- sapply(articles, xpathSApply, ".//PublicationType", xmlValue)
data_frame( pmid= rep(x1, sapply(x2, length) ), pubtype = unlist(x2) )
# A tibble: 9,885 × 2
pmid pubtype
<chr> <chr>
1 11677608 Journal Article
2 11677608 Research Support, Non-U.S. Gov't
3 12950922 Journal Article
4 12950922 Research Support, Non-U.S. Gov't
5 22328765 Journal Article
...
还有最后一条评论。如果你想每篇文章一行,我通常会创建一个函数,将多个标签组合成一个分隔列表,并为缺失的节点添加 NA。
xpath2 <-function(x, ...){
y <- xpathSApply(x, ...)
ifelse(length(y) == 0, NA, paste(y, collapse="; "))
}
data_frame( pmid = sapply(articles, xpath2, ".//ArticleId[@IdType='pubmed']", xmlValue),
journal = sapply(articles, xpath2, ".//Journal/Title", xmlValue),
pubtypes = sapply(articles, xpath2, ".//PublicationType", xmlValue))
# A tibble: 6,172 × 3
pmid journal pubtypes
<chr> <chr> <chr>
1 11677608 Nature Journal Article; Research Support, Non-U.S. Gov't
2 12950922 Molecular microbiology Journal Article; Research Support, Non-U.S. Gov't
3 22328765 Journal of bacteriology Journal Article; Research Support, Non-U.S. Gov't
4 11337471 Genome research Journal Article
...
我正在使用 R 查询带有一长串 PMID 的 PubMED。因为 entrez_fetch 一次只能做一定数量的事情,所以我将我的 ~2000 个 PMID 分解为一个包含多个向量的列表(每个约 500 长)。当我查询 PubMED 时,我从每个出版物的 XML 文件中提取信息。最后我想要的是这样的:
Original.PMID Publication.type
26956987 Journal.article
26956987 Meta.analysis
26956987 Multicenter.study
26402000 Journal.article
25404043 Journal.article
25404043 Meta.analysis
每个出版物都有一个唯一的 PMID,但可能有多个出版物类型与每个 PMID 关联(如上所示)。我可以从 XML 文件中查询 PMID 号,我可以得到每个 PMID 的发布类型。我遇到的问题是重复 PMID x 次,以便每个 PMID 都与其拥有的每个发布类型相关联。如果我的数据不在包含多个子列表的列表中(例如,如果我有 14 个批次,每个批次作为其自己的数据框),我可以通过从父 PublicationType 节点获取子节点的数量来执行此操作。但我似乎无法弄清楚如何在列表中执行此操作。
到目前为止我的代码是这样的:
library(rvest)
library(tidyverse)
library(stringr)
library(regexr)
library(rentrez)
library(XML)
pubmed<-my.data.frame
into.batches<-function(x,n) split(x,cut(seq_along(x),n,labels=FALSE))
batches<-into.batches(pubmed.fwd$PMID, 14)
headings<-lapply(1:14, function(x) {paste0("Batch",x)})
names(batches)<-headings
fwd<-sapply(batches, function(x) entrez_fetch(db="pubmed", id=x, rettype="xml", parsed=TRUE))
trial1<-lapply(fwd, function(x)
list(pub.type = xpathSApply(x, "//PublicationTypeList/PublicationType", xmlValue),
or.pmid = xpathSApply(x, "//ArticleId[@IdType='pubmed']", xmlValue)))
trial1 是我遇到的问题。这给了我一个列表,在每个批次中,我有一个 pub.type 的向量和一个 or.pmid 的向量,但它们的长度不同。
我想弄清楚每个出版物有多少子出版物类型,所以我可以重复 PMID 很多次。我目前正在使用以下代码,但它不符合我的要求:
trial1<-lapply(fwd, function(x)
list(childnodes = xpathSApply(xmlRoot(x), "count(.//PublicationTypeList/PublicationType)", xmlChildren)))
不幸的是,这只是告诉我每个批次的子节点总数,而不是每个发布(或 pmid)的子节点总数。
由于每篇文章的 ArticleId 可能是唯一的,并且每篇文章的 PublicationType 可能不止一个,请考虑迭代创建数据框而不是单独创建数据框载体。
具体来说,在 XML 文档的每个 PubmedArticle 节点上使用节点索引 [#]
,因为这是 id 和类型的共享祖先,然后所需后代的 xpath。下面创建了一个长度等于 fwd
:
trial1 <- lapply(fwd, function(doc) {
# RETRIEVE NUMBER OF ARTICLES PER EACH XML
num_of_articles <- length(xpathSApply(doc, "//PubmedArticle"))
# LOOP THROUGH EACH ARTICLE AND BIND XML VALUES TO DATAFRAME
dfList <- lapply(seq(num_of_articles), function(i)
data.frame(
Original.PMID = xpathSApply(doc, paste0("//PubmedArticle[",i,"]/descendant::ArticleId[@IdType='pubmed']"), xmlValue),
Publication.type = xpathSApply(doc, paste0("//PubmedArticle[",i,"]/descendant::PublicationTypeList/PublicationType"), xmlValue)
))
# ROW BIND ALL DFS INTO ONE
df <- do.call(rbind, dfList)
})
对于所有批次的最终主数据帧,运行 do.call(rbind, ...)
再次退出循环:
finaldf <- do.call(rbind, trial1)
我会将 XML 结果拆分为单独的 Article 节点并应用 xpath 函数来获取 pmid 和 pubtypes。
pmids <- c(11677608, 22328765 ,11337471)
res <- entrez_fetch(db="pubmed", rettype="xml", id = pmids)
doc <- xmlParse(res)
x <- getNodeSet(doc, "//PubmedArticle")
x1 <- sapply(x, xpathSApply, ".//ArticleId[@IdType='pubmed']", xmlValue)
x2 <- sapply(x, xpathSApply, ".//PublicationType", xmlValue)
data.frame( pmid= rep(x1, sapply(x2, length) ), pubtype = unlist(x2) )
pmid pubtype
1 11677608 Journal Article
2 11677608 Research Support, Non-U.S. Gov't
3 22328765 Journal Article
4 22328765 Research Support, Non-U.S. Gov't
5 11337471 Journal Article
此外,NCBI 表示如果使用超过 200 个 UID,则使用 HTTP POST 方法。 rentrez
不支持 POSTing,但您可以 运行 使用几行代码。
首先,您需要一个具有 1000 个 Pubmed ID 的载体(来自微生物基因组的 6171 个 table)
library(readr)
x <- read_tsv( "ftp://ftp.ncbi.nih.gov/genomes/GENOME_REPORTS/prokaryotes.txt",
na = "-", quote = "")
ids <- unique( x$`Pubmed ID` )
ids <- ids[ids < 1e9 & !is.na(ids)]
Post 使用 httr POST
.
uri = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/epost.fcgi?"
response <- httr::POST(uri, body= list(id = paste(ids, collapse=","), db = "pubmed"))
按照entrez_post
中的代码解析结果,获取网络历史。
doc <- xmlParse( httr::content(response, as="text", encoding="UTF-8") )
result <- xpathApply(doc, "/ePostResult/*", xmlValue)
names(result) <- c("QueryKey", "WebEnv")
class(result) <- c("web_history", "list")
最后,获取最多 10K 条记录(如果超过 10K,则使用 retstart
选项循环)
res <- entrez_fetch(db="pubmed", rettype="xml", web_history=result)
doc <- xmlParse(res)
在我的笔记本电脑上 运行 这些只需要一秒钟。
articles <- getNodeSet(doc, "//PubmedArticle")
x1 <- sapply(articles, xpathSApply, ".//ArticleId[@IdType='pubmed']", xmlValue)
x2 <- sapply(articles, xpathSApply, ".//PublicationType", xmlValue)
data_frame( pmid= rep(x1, sapply(x2, length) ), pubtype = unlist(x2) )
# A tibble: 9,885 × 2
pmid pubtype
<chr> <chr>
1 11677608 Journal Article
2 11677608 Research Support, Non-U.S. Gov't
3 12950922 Journal Article
4 12950922 Research Support, Non-U.S. Gov't
5 22328765 Journal Article
...
还有最后一条评论。如果你想每篇文章一行,我通常会创建一个函数,将多个标签组合成一个分隔列表,并为缺失的节点添加 NA。
xpath2 <-function(x, ...){
y <- xpathSApply(x, ...)
ifelse(length(y) == 0, NA, paste(y, collapse="; "))
}
data_frame( pmid = sapply(articles, xpath2, ".//ArticleId[@IdType='pubmed']", xmlValue),
journal = sapply(articles, xpath2, ".//Journal/Title", xmlValue),
pubtypes = sapply(articles, xpath2, ".//PublicationType", xmlValue))
# A tibble: 6,172 × 3
pmid journal pubtypes
<chr> <chr> <chr>
1 11677608 Nature Journal Article; Research Support, Non-U.S. Gov't
2 12950922 Molecular microbiology Journal Article; Research Support, Non-U.S. Gov't
3 22328765 Journal of bacteriology Journal Article; Research Support, Non-U.S. Gov't
4 11337471 Genome research Journal Article
...