puremoe retrieves PubTator3 entity mentions and relation
pairs with one call, then reshapes them locally into sentence context,
entity co-occurrence counts, relation networks, and edge-level evidence.
Everything after the initial fetch is a local transform on the retrieved
tables and makes no further API calls.
We use a biomedical corpus with rich entity and relation annotation throughout.
pmids <- search_pubmed('"doxorubicin"[TiAb] AND "cardiotoxicity"[TiAb]')
pt <- get_records(head(pmids, 25L), endpoint = "pubtator", cores = 1L)get_records(endpoint = "pubtator") returns a list with
entities (one row per mention) and relations
(one row per typed relation pair).
pubtator_context() preserves PubTator entity spans, adds
sentence IDs and sentence-relative spans to entities, adds readable
entity labels and sentence anchors to relations, and returns a sentence
lookup table.
ctx <- pubtator_context(pt)pubtator_cooccurrence() counts entity pairs within a
sentence window. window = 0 counts co-mentions in the same
sentence; larger windows reach across adjacent sentences within the same
passage. Title and abstract sentences are never compared to each other.
Set by = "entity" to resolve specific mentions;
by = "type" aggregates by entity-type pair.
pubtator_cooccurrence(ctx, window = 1, by = "entity") |>
select(type_x, text_x, type_y, text_y, n, n_pmids) |>
head(10) |>
DT::datatable(rownames = FALSE, options = list(scrollX = TRUE))pubtator_network() turns contextualized PubTator
relations into a directed entity network plus a lean evidence table.
Nodes are specific entities keyed on PubTator identifier
when present, falling back to type:text. Edges are directed
relation assertions aggregated across the corpus.
rel_net <- pubtator_network(ctx)
evidence <- rel_net[["evidence"]]
if (is.null(evidence)) {
evidence <- data.frame(
from = character(), to = character(), relation_type = character(),
pmid = character(), same_sentence = logical(), sentence = character()
)
}
as.data.frame(evidence) |>
dplyr::select(dplyr::any_of(c(
"from", "to", "relation_type", "pmid", "same_sentence", "sentence"
))) |>
dplyr::filter(.data$same_sentence %in% TRUE, !is.na(.data$sentence)) |>
dplyr::slice_head(n = 15) |>
DT::datatable(
rownames = FALSE,
options = list(scrollX = TRUE, pageLength = 5)
)