library(ontologics)
library(dplyr, warn.conflicts = FALSE)

When an ontology exists already, it can be used either by looking up which concepts exist in it, which relations the concepts have among each other, by adding and linking new concepts to the already existing concepts or by extending the harmonised concepts based on external ontologies.

Relate an external dataset to harmonized concepts

When adding (or mapping) new concepts, we first have to define the source of the new concepts.

# already existing ontology for some project about crops
crops <- load_ontology(path = system.file("extdata", "crops.rds", package = "ontologics"))

# where we have to set the external dataset as new source
crops <- new_source(name = "externalDataset",
                    version = "0.0.1",
                    description = "a vocabulary",
                    homepage = "https://www.something.net",
                    license = "CC-BY-4.0",
                    ontology = crops)

# new concepts that occur in the external dataset, which should be harmonised with the ontology
externalConcepts <- c("Wheat", "NUTS", "Avocado")

The new concepts are from different conceptual levels, both ‘Wheat’ and ‘Avocado’ are the crop itself, while ‘NUTS’ is an aggregate of various crops (such as walnut, hazelnut, etc). Let’s first find out whether these concepts are in fact new concepts because they are missing from the ontology.

missingConcepts <- get_concept(label = externalConcepts, ontology = crops)
missingConcepts %>% 
  select(1:5) %>% 
  kable()
id label description class has_broader
.02.09 Wheat NA class .02
.10 NUTS NA group NA

This tells us that that both ‘NUTS’ and ‘Wheat’ don’t seem to be missing from the ontology. Moreover, we see that ‘Wheat’ is a class and not a crop and ‘NUTS’ also isn’t crop and doesn’t have any broader concept. While Avocado is missing from the ontology entirely, the crop Wheat is also missing so that both have to be defined as new concept.

By studying the ontology (not shown here), we can identify the semantic relation between the new concepts and some of the already harmonized concepts. First of all, to assign new concepts from an external ontology, a harmonised concept must already exist in the harmonised ontology. For the new harmonized concepts, we chose the lower capital letter words to show the difference between those and the external concepts and we assign them as narrower concepts to the respective already existing, harmonized (broader) concepts.

broaderConcepts <- get_concept(label = c("Wheat", "Tropical and subtropical Fruit"), 
                               ontology = crops)

crops <- new_concept(new = c("wheat", "avocado"),
                     broader = broaderConcepts,
                     class = "crop",
                     ontology = crops)

Eventually, all new (external) concepts can be mapped to already harmonized concepts. Even though ‘NUTS’ already exists, this also applies to this new concept, because the already existing concept ‘NUTS’ doesn’t necessarily have to be the same as the new concept ‘NUTS’. This all depends on the respective description of the harmonized and external concepts. When setting a new mapping, the type and the certainty of the match have to be defined. As we have defined the closely related concepts as new concepts, we can chose a close match for all external concepts. Certainty has the value 3, because it is clear, assigned according to a given definition.

toMap <- get_concept(label = c("wheat", "NUTS", "avocado"),
                     ontology = crops)

crops <- new_mapping(new = externalConcepts,
                     target = toMap,
                     match = c("close", "close", "close"),
                     source = "externalDataset",
                     certainty = 3,
                     ontology = crops)

Extend the classes

It may be, moreover, that we want to (or have to) add new concepts, that do not have a class defined yet. This can be the case when we have to nest new concepts, out of a range of concepts that do have a valid class, into concepts that are already at the lowest possible hierarchical level. These concepts are, when specified with class = NA, class = c(bla, blubb, NA), or class = NULL, assigned the actual class undefined and you are informed about how to proceed.

broaderConcepts <- get_concept(label = c("wheat", "wheat"),
                               ontology = crops)

# for (some of) these concepts we do not know the class ...
crops <- new_concept(new = c("wheat1", "wheat2"),
                     broader = broaderConcepts,
                     class = NA_character_,
                     ontology = crops)
#> Warning: some new concepts (wheat1, wheat2) don't have a class; please define
#> this with 'new_class()' and re-run 'new_concept()' with these concepts and the
#> new class.

make_tree(label = "Wheat", ontology = crops) %>%
  select(1:5) %>%
  kable()
id label class description has_broader
.02.09 Wheat class NA .02
.02.09.01 wheat crop NA .02.09
.02.09.01.01 wheat1 undefined NA .02.09.01
.02.09.01.02 wheat2 undefined NA .02.09.01

# ... ok, then let's specify that class and re-run new_concept
crops <- new_class(new = "cultivar", target = "crop", 
                   description = "type of plant that people have bred for desired traits", 
                   ontology = crops)

crops <- new_concept(new = c("wheat1", "wheat2"),
                     broader = broaderConcepts,
                     class = "cultivar",
                     ontology = crops)

Now we can check whether the updated ontology is as we’d expect, for example by looking at the tree of the respective items again. We should expect that the new harmonized concepts now appear in the ontology and that they have some link to an external concept defined.

make_tree(label = "Wheat", ontology = crops) %>%
  select(1:5) %>%
  kable()
id label class description has_broader
.02.09 Wheat class NA .02
.02.09.01 wheat crop NA .02.09
.02.09.01.03 wheat1 cultivar NA .02.09.01
.02.09.01.04 wheat2 cultivar NA .02.09.01
make_tree(label = "NUTS", ontology = crops) %>%
  select(1:5) %>%
  kable()
id label class description has_broader
.10 NUTS group NA NA
.10.01 Treenuts class NA .10
.10.02 Other nuts class NA .10
make_tree(label = "FRUIT", ontology = crops) %>%
  select(1:5) %>%
  kable()
id label class description has_broader
.06 FRUIT group NA NA
.06.01 Berries class NA .06
.06.02 Citrus Fruit class NA .06
.06.03 Grapes class NA .06
.06.04 Pome Fruit class NA .06
.06.05 Stone Fruit class NA .06
.06.06 Tropical and subtropical Fruit class NA .06
.06.06.01 avocado crop NA .06.06
.06.07 Other fruit class NA .06
# and finally a list of all external concepts
get_concept(external = TRUE, ontology = crops) %>% 
  kable()
id label description has_source has_broader
externalDataset_1 Wheat NA 2 .02.09
externalDataset_2 NUTS NA 2 .06.06
externalDataset_3 Avocado NA 2 NA