Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici

Mixer Python et OWL

Introduction

Dans ce chapitre, nous verrons comment mélanger méthodes Python et constructeurs logiques OWL au sein d’une même classe.

Ajouter des méthodes Python aux classes OWL

Avec Owlready, les classes OWL sont des classes Python (presque) comme les autres. Il est donc possible d’y inclure des méthodes. Voici un exemple simple qui permet de calculer le prix par comprimé d’un médicament à partir de son prix unitaire (par boîte) et du nombre de comprimés dans la boîte :

>>> from owlready2 import * 
>>> onto = get_ontology("http://test.org/medicament.owl#")  
>>> with onto:  
...     class Médicament(Thing): pass 
... 
...     class prix (Médicament >> float, FunctionalProperty): pass
...     class nb_comprimé(Médicament >> int , FunctionalProperty): pass 
...  
...     class Médicament(Thing):  
...         def get_prix_par_comprimé(self):  
...             return self.prix / self.nb_comprimé 

Notez que la classe Médicament est définie deux fois : il s’agit d’une déclaration anticipée pour pouvoir l’utiliser dans les définitions des propriétés (voir chapitre Créer et modifier des ontologies en Python, section Définitions multiples et déclarations anticipées).

La méthode peut ensuite être appelée...

Associer un module Python à une ontologie

Lorsque les ontologies ne sont pas créées entièrement en Python (comme dans l’exemple ci-dessus) mais chargées à partir de fichier OWL, les méthodes Python peuvent être définies dans un fichier Python .py séparé. Celui-ci peut être importé manuellement ou bien relié à l’ontologie via une annotation; Owlready importera alors automatiquement le module Python lorsque l’ontologie sera chargée.

Par exemple, le fichier bacterie.py suivant ajoute une méthode dans les classes Bactérie et Staphylocoque de l’ontologie des bactéries :

# Fichier bacterie.py  
from owlready2 import *  
 
onto = get_ontology("http://lesfleursdunormal.fr/static/ \ 
                    _downloads/bacterie.owl#") 
 
with onto:  
   class Bactérie(Thing):  
       def méthode(self):  
           print("C'est une bactérie !")  
 
   class Staphylocoque(Thing):  
       def méthode(self):  
           print("C'est un staphylocoque !") 

Notez que nous n’avons pas chargé l’ontologie des bactéries...

Polymorphisme sur inférence de type

Nous avons vu au chapitre Raisonnement automatique, section Raisonnement en monde ouvert que, lors du raisonnement, les classes des individus et les superclasses des classes pouvaient être modifiées. Dans ce cas, les méthodes disponibles peuvent changer. De plus, en cas de polymorphisme, c’est-à-dire lorsque plusieurs classes exploitent la même méthode de manière différente, l’implémentation de la méthode pour un individu ou une classe peut changer. C’est le « polymorphisme sur inférence de type ».

Voici un exemple simple :

>>> ma_bacterie = onto.Bactérie(gram_positif = True,  
...     a_pour_forme = onto.Ronde(),  
...     a_pour_regroupement = [onto.EnAmas()] )  
>>> ma_bacterie.méthode()  
C'est une bactérie ! 

Nous avons créé une bactérie. Lorsque nous exécutons la méthode, c’est l’implémentation de la classe Bactérie qui est donc appelée. Nous allons maintenant appeler le raisonneur.

>>> sync_reasoner() 

Le raisonneur a déduit que la bactérie est en fait un Staphylocoque (à partir de ses relations). À présent, si nous appelons la méthode, c’est l’implémentation du Staphylocoque qui est appelée :...

Introspection

L’introspection est une technique avancée de programmation objet qui consiste à analyser un objet sans le connaître, par exemple afin d’obtenir la liste de ses attributs et leurs valeurs.

Pour l’introspection des individus, la méthode get_properties() permet d’obtenir la liste des propriétés pour lesquelles l’individu possède au moins une relation.

>>> onto.bactérie_inconnue.get_properties()  
{bacterie.a_pour_forme,  
 bacterie.a_pour_regroupement,  
 bacterie.gram_positif,  
 bacterie.nb_colonies} 

Il est ensuite possible d’obtenir et/ou de modifier ces relations. Les fonctions getattr(objet, attribut) et setattr(objet, attribut, valeur) de Python permettent de lire ou d’écrire un attribut d’un objet Python, lorsque le nom de l’attribut est connu dans une variable (voir chapitre Le langage Python : adoptez un serpent !, section Fonctions et opérateurs pour la programmation objet), par exemple :

>>> for prop in onto.bactérie_inconnue.get_properties():  
...     print(prop.name, "=", getattr(onto.bactérie_inconnue, prop.python_name)) 
a_pour_regroupement = [bacterie.en_amas1]  
a_pour_forme = bacterie.ronde1  
gram_positif = True  
nb_colonies = 6 

Les valeurs retournées sont les mêmes qu’avec la syntaxe...

Inverser les restrictions

Les restrictions permettent de définir des relations au niveau des classes de l’ontologie, par exemple « Pseudomonas a_pour_forme some Allongée ». Owlready permet d’accéder facilement à ces relations avec la syntaxe « Classe.propriété » :

>>> onto.Pseudomonas.a_pour_forme  
bacterie.Allongée 

Mais comment lire cette restriction existentielle « à l’envers », c’est-à-dire, à partir de la classe Allongée, remonter à la classe Pseudomonas ? Même si nous avions défini la propriété inverse, que nous pourrions appeler « est_forme_de », elle ne permettrait pas de répondre à notre question, comme le montre l’exemple suivant :

>>> with onto:  
...     class est_forme_de(ObjectProperty):  
...         inverse = onto.a_pour_forme  
 
>>> onto.Allongée.est_forme_de  
[] 

En effet, d’un point de vue logique, les deux propositions suivantes sont différentes :

  • « Pseudomonas a_pour_forme some Allongée »

  • « Allongée est_forme_de some Pseudomonas »

La première indique que tout Pseudomonas a une forme Allongée, ce qui est vrai. La seconde indique que toute forme Allongée...

Exemple : utiliser Gene Ontology et gérer les relations « partie-de »

Gene Ontology (GO) est une ontologie très utilisée en bioinformatique (voir chapitre Accéder aux ontologies en Python, section Ontologie volumineuse et cache disque). GO se compose de trois parties : les processus biologiques, les fonctions moléculaires et les composants de la cellule. Cette troisième partie décrit les différents éléments d’une cellule : membranes, noyau, organites (tels que les mitochondries)... Elle est particulièrement complexe à gérer, car elle comprend à la fois une hiérarchie d’héritage « classique » avec des relations « est-un », mais aussi une hiérarchie de relation « partie-de ». Dans cette dernière, appelée méronymie, il s’agit de décomposer la cellule en sous-parties, puis en sous-sous-parties... La racine de cette hiérarchie est donc la cellule entière, et les feuilles, les parties indivisibles.

OWL et Owlready possèdent des relations et des méthodes pour gérer la hiérarchie d’héritage (subclasses(), descendants(), ancestors()... voir chapitre Accéder aux ontologies en Python, section Classes). En revanche, il n’existe pas de relation standard OWL pour la méronymie, ni de méthodes spécifiques dans Owlready. Nous allons voir ici comment ajouter aux classes GO des méthodes pour accéder aux sous-parties et aux superparties, en prenant en compte à la fois les relations « partie-de » et les relations « est-un ».

GO étant assez volumineuse (près de 200 Mo), le chargement...

Exemple : un « site de rencontre » pour les protéines

À présent, nous allons utiliser les fonctionnalités du module go_partie_de.py pour réaliser un « site de rencontre » pour les protéines. Ce site permet d’entrer deux noms de protéines, et de déterminer dans quels compartiments de la cellule elles peuvent se rencontrer (si c’est possible !). D’un point de vue biologique, cela est important, car deux protéines qui n’ont pas de « site de rencontre » commun ne peuvent pas interagir ensemble.

Pour cela, nous utiliserons :

  • Le module Python Flask, pour faire un site web dynamique (voir chapitre Accéder aux ontologies en Python, section Exemple : créer un site web dynamique à partir d’une ontologie).

  • Le module Python MyGene, pour effectuer des recherches sur le serveur MyGene et récupérer les concepts GO associés à chacune des deux protéines. Ce module permet de faire des recherches sur les gènes (et les protéines qu’ils codent). MyGene s’utilise de la manière suivante :

import mygene  
mg = mygene.MyGeneInfo()  
dico = mg.query('name:"<nom_de_gene>"',  
               fields = "<champs recherchés>",  
               species = "<espèce>",  
               size = <nombre de gènes à rechercher>) 

L’appel à MyGene retourne un dictionnaire contenant lui-même des listes et d’autres dictionnaires. Par exemple, nous pouvons rechercher l’ensemble des termes GO associés à l’insuline de la manière suivante :

>>> import mygene  
>>> mg = mygene.MyGeneInfo()  
>>> dico = mg.query('name:"insulin"',  
...                  fields = "go.CC.id,go.MF.id,go.BP.id", 
...                  species = "human", size = 1)  
>>> dico  
{'max_score': 13.233688, 'took': 17, 'total': 57,  
'hits': [{'_id':...