Source code for skosify.infer

# -*- coding: utf-8 -*-
"""Inference rules are bundled in one namespace."""

import logging
from rdflib import Namespace, RDF, RDFS

SKOS = Namespace("")

[docs]def skos_topConcept(rdf): """Infer skos:topConceptOf/skos:hasTopConcept (S8) and skos:inScheme (S7).""" for s, o in rdf.subject_objects(SKOS.hasTopConcept): rdf.add((o, SKOS.topConceptOf, s)) for s, o in rdf.subject_objects(SKOS.topConceptOf): rdf.add((o, SKOS.hasTopConcept, s)) for s, o in rdf.subject_objects(SKOS.topConceptOf): rdf.add((s, SKOS.inScheme, o))
[docs]def skos_hierarchical(rdf, narrower=True): """Infer skos:broader/skos:narrower (S25) but only keep skos:narrower on request. :param bool narrower: If set to False, skos:narrower will not be added, but rather removed. """ if narrower: for s, o in rdf.subject_objects(SKOS.broader): rdf.add((o, SKOS.narrower, s)) for s, o in rdf.subject_objects(SKOS.narrower): rdf.add((o, SKOS.broader, s)) if not narrower: rdf.remove((s, SKOS.narrower, o))
[docs]def skos_transitive(rdf, narrower=True): """Perform transitive closure inference (S22, S24).""" for conc in rdf.subjects(RDF.type, SKOS.Concept): for bt in rdf.transitive_objects(conc, SKOS.broader): if bt == conc: continue rdf.add((conc, SKOS.broaderTransitive, bt)) if narrower: rdf.add((bt, SKOS.narrowerTransitive, conc))
[docs]def skos_symmetric_mappings(rdf, related=True): """Ensure that the symmetric mapping properties (skos:relatedMatch, skos:closeMatch and skos:exactMatch) are stated in both directions (S44). :param bool related: Add the skos:related super-property for all skos:relatedMatch relations (S41). """ for s, o in rdf.subject_objects(SKOS.relatedMatch): rdf.add((o, SKOS.relatedMatch, s)) if related: rdf.add((s, SKOS.related, o)) rdf.add((o, SKOS.related, s)) for s, o in rdf.subject_objects(SKOS.closeMatch): rdf.add((o, SKOS.closeMatch, s)) for s, o in rdf.subject_objects(SKOS.exactMatch): rdf.add((o, SKOS.exactMatch, s))
[docs]def skos_hierarchical_mappings(rdf, narrower=True): """Infer skos:broadMatch/skos:narrowMatch (S43) and add the super-properties skos:broader/skos:narrower (S41). :param bool narrower: If set to False, skos:narrowMatch will not be added, but rather removed. """ for s, o in rdf.subject_objects(SKOS.broadMatch): rdf.add((s, SKOS.broader, o)) if narrower: rdf.add((o, SKOS.narrowMatch, s)) rdf.add((o, SKOS.narrower, s)) for s, o in rdf.subject_objects(SKOS.narrowMatch): rdf.add((o, SKOS.broadMatch, s)) rdf.add((o, SKOS.broader, s)) if narrower: rdf.add((s, SKOS.narrower, o)) else: rdf.remove((s, SKOS.narrowMatch, o))
[docs]def rdfs_classes(rdf): """Perform RDFS subclass inference. Mark all resources with a subclass type with the upper class.""" # find out the subclass mappings upperclasses = {} # key: class val: set([superclass1, superclass2..]) for s, o in rdf.subject_objects(RDFS.subClassOf): upperclasses.setdefault(s, set()) for uc in rdf.transitive_objects(s, RDFS.subClassOf): if uc != s: upperclasses[s].add(uc) # set the superclass type information for subclass instances for s, ucs in upperclasses.items(): logging.debug("setting superclass types: %s -> %s", s, str(ucs)) for res in rdf.subjects(RDF.type, s): for uc in ucs: rdf.add((res, RDF.type, uc))
[docs]def rdfs_properties(rdf): """Perform RDFS subproperty inference. Add superproperties where subproperties have been used.""" # find out the subproperty mappings superprops = {} # key: property val: set([superprop1, superprop2..]) for s, o in rdf.subject_objects(RDFS.subPropertyOf): superprops.setdefault(s, set()) for sp in rdf.transitive_objects(s, RDFS.subPropertyOf): if sp != s: superprops[s].add(sp) # add the superproperty relationships for p, sps in sorted(superprops.items()): logging.debug("setting superproperties: %s -> %s", p, str(sps)) for s, o in rdf.subject_objects(p): for sp in sps: rdf.add((s, sp, o))