Author: Carl Boettiger

# SPARQL with RNeXML

#### Carl Boettiger

#### Scott Chamberlain

#### Rutger Vos

#### Hilmar Lapp

## SPARQL Queries

Rich, semantically meaningful metadata lies at the heart of the NeXML standard. R provides a rich environment to unlock this information. While our previous examples have relied on the user knowing exactly what metadata they intend to extract (title, publication date, citation information, and so forth), semantic metadata has meaning that a computer can make use of, allowing us to make much more conceptually rich queries than those simple examples. The SPARQL query language is a powerful way to make use of such semantic information in making complex queries.

While users should consult a formal introduction to SPARQL for further background, here we illustrate how SPARQL can be used in combination with R functions in ways that would be much more tedious to assemble with only traditional/non-semantic queries. The SPARQL query language is provided for the R environment through the `rrdf` package [@Willighagen_2014], so we start by loading that package. We will also make use of functions from `phytools` and `RNeXML`.

    library("rdflib")
    library("xml2")
    library("phytools")
    library("RNeXML")

We read in an example file that contains semantic metadata annotations describing the taxonomic units (OTUs) used in the tree.

    nexml <- nexml_read(system.file("examples/primates.xml", package="RNeXML"))

In particular, this example declares the taxon rank, NCBI identifier and parent taxon for each OTU, such as:

    <otu about="#ou541" id="ou541" label="Alouatta guariba">
          <meta href="http://ncbi.nlm.nih.gov/taxonomy/182256"
                id="ma20"
                rel="concept:toTaxon"
                xsi:type="nex:ResourceMeta"/>
          <meta href="http://rs.tdwg.org/ontology/voc/TaxonRank#Species"
                id="ma21"
                rel="concept:rank"
                xsi:type="nex:ResourceMeta"/>
          <meta href="http://ncbi.nlm.nih.gov/taxonomy/9499"
                id="ma22"
                rel="rdfs:subClassOf"
                xsi:type="nex:ResourceMeta"/>
        </otu>

In this example, we will construct a cladogram by using this information to identify the taxonomic rank of each OTU, and its shared parent taxonomic rank. (If this example looks complex, try writing down the steps to do this without the aid of the SPARQL queries). These examples show the manipulation of semantic triples, Unique Resource Identifiers (URIs) and use of the SPARQL “Join” operator.

Note that this example can be run using `demo("sparql", "RNeXML")` to see the code displayed in the R terminal and to avoid character errors that can occur in having to copy and paste from PDF files.

We begin by extracting the RDF graph from the NeXML,

    rdf <- get_rdf(system.file("examples/primates.xml", package="RNeXML"))
    tmp <- tempfile()  # so we must write the XML out first
    xml2::write_xml(rdf, tmp)

    graph <- rdf_parse(tmp)

We then fetch the NCBI URI for the taxon that has rank ‘Order’, i.e. the root of the primates phylogeny. The dot operator `.` between clauses implies a join, in this case

    root <- rdf_query(graph,
    "SELECT ?uri WHERE {
        ?id <http://rs.tdwg.org/ontology/voc/TaxonConcept#rank> <http://rs.tdwg.org/ontology/voc/TaxonRank#Order> .
        ?id <http://rs.tdwg.org/ontology/voc/TaxonConcept#toTaxon> ?uri
    }")

This makes use of the SPARQL query language provided by the `rrdf` package. We will also define some helper functions that use SPARQL queries. Here we define a function to get the name

    get_name <- function(id) {
      max <- length(nexml@otus[[1]]@otu)
      for(i in 1:max) {
        if ( nexml@otus[[1]]@otu[[i]]@id == id ) {
          label <- nexml@otus[[1]]@otu[[i]]@label
          label <- gsub(" ","_",label)
          return(label)
        }
      }
    }

Next, we define a recursive function to build a newick tree from the taxonomic rank information.

    recurse <- function(node){

        # fetch the taxonomic rank and id string
        rank_query <- paste0(
            "SELECT ?rank ?id WHERE {
                ?id <http://rs.tdwg.org/ontology/voc/TaxonConcept#toTaxon> <",node,"> .
                ?id <http://rs.tdwg.org/ontology/voc/TaxonConcept#rank> ?rank
              }")
        result <- rdf_query(graph, rank_query)

        # get the local ID, strip URI part
        id <- result$id
        id <- gsub("^.+#", "", id, perl = TRUE)

        # if rank is terminal, return the name
        if (result$rank == "http://rs.tdwg.org/ontology/voc/TaxonRank#Species") {
            return(get_name(id))
        }

        # recurse deeper
        else {
            child_query <- paste0(
                "SELECT ?uri WHERE {
                    ?id <http://www.w3.org/2000/01/rdf-schema#subClassOf> <",node,"> .
                    ?id <http://rs.tdwg.org/ontology/voc/TaxonConcept#toTaxon> ?uri
                }")
            children <- rdf_query(graph, child_query)

            return(paste("(",
                         paste(sapply(children$uri, recurse),
                               sep = ",", collapse = "," ),
                         ")",
                         get_name(id), # label interior nodes
                         sep = "", collapse = ""))
        }
    }

With these functions in place, it is straight forward to build the tree from the semantic RDFa data and then visualize it

    newick <- paste(recurse(root), ";", sep = "", collapse = "")
    tree <- read.newick(text = newick)
    collapsed <- collapse.singles(tree)
    plot(collapsed,
         type='cladogram',
         show.tip.label=FALSE,
         show.node.label=TRUE,
         cex=0.75,
         edge.color='grey60',
         label.offset=-9)

plot of chunk unnamed-chunk-8