Skip to content
Snippets Groups Projects
Commit a92f647f authored by Aurélien Lamercerie's avatar Aurélien Lamercerie
Browse files

Update module Metrics to refine metrics calculation

parent 02292710
Branches
No related tags found
No related merge requests found
File deleted
File deleted
File deleted
File deleted
File deleted
...@@ -2,28 +2,84 @@ ...@@ -2,28 +2,84 @@
# -*-coding:Utf-8 -* # -*-coding:Utf-8 -*
#============================================================================== #==============================================================================
# ontoScorer: [brief description of the module] # ontoScorer: Ontology Scoring Module
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Detailed module description # This module provides metrics to evaluate and compare different ontologies.
# It calculates precision, recall, and F1 score for various ontology elements
# such as classes, object properties, data properties, restrictions, individuals,
# and annotations. It also computes an overall score taking into account all
# the ontology elements. The comparison is performed between a reference ontology
# and a generated ontology, allowing users to evaluate how well the generated
# ontology matches the reference.
#============================================================================== #==============================================================================
from sklearn.metrics import precision_score, recall_score, f1_score from sklearn.metrics import precision_score, recall_score, f1_score
from ontoScorer.ontology import Ontology from ontoScorer.ontology import Ontology
class Metrics: class Metrics:
#--------------------------------------------------------------------------
# Constructor(s)
#--------------------------------------------------------------------------
def __init__(self): def __init__(self):
self.precision = 0 self.scores = {
self.recall = 0 "class": {"precision": 0, "recall": 0, "f1": 0},
self.f1 = 0 "object_property": {"precision": 0, "recall": 0, "f1": 0},
"data_property": {"precision": 0, "recall": 0, "f1": 0},
"restriction": {"precision": 0, "recall": 0, "f1": 0},
"individual": {"precision": 0, "recall": 0, "f1": 0},
"annotation": {"precision": 0, "recall": 0, "f1": 0},
"overall": {"precision": 0, "recall": 0, "f1": 0}
}
#--------------------------------------------------------------------------
# Computing Method(s)
#--------------------------------------------------------------------------
def calculate(self, reference_ontology, generated_ontology): def calculate(self, reference_ontology, generated_ontology):
reference_classes = set([cls.name() for cls in reference_ontology.get_classes()]) methods = [
generated_classes = set([cls.name() for cls in generated_ontology.get_classes()]) ("class", "get_classes"),
("object_property", "get_object_properties"),
("data_property", "get_data_properties"),
("restriction", "get_restrictions"),
("individual", "get_individuals"),
#("annotation", "get_annotations")
]
y_true_overall = []
y_pred_overall = []
for score_name, method_name in methods:
reference_elements = set([elem.name() for elem in getattr(reference_ontology, method_name)()])
generated_elements = set([elem.name() for elem in getattr(generated_ontology, method_name)()])
all_elements = reference_elements.union(generated_elements)
y_true = [1 if elem in reference_elements else 0 for elem in all_elements]
y_pred = [1 if elem in generated_elements else 0 for elem in all_elements]
self.scores[score_name]["precision"] = precision_score(y_true, y_pred)
self.scores[score_name]["recall"] = recall_score(y_true, y_pred)
self.scores[score_name]["f1"] = f1_score(y_true, y_pred)
y_true_overall.extend(y_true)
y_pred_overall.extend(y_pred)
self.scores["overall"]["precision"] = precision_score(y_true_overall, y_pred_overall)
self.scores["overall"]["recall"] = recall_score(y_true_overall, y_pred_overall)
self.scores["overall"]["f1"] = f1_score(y_true_overall, y_pred_overall)
all_classes = reference_classes.union(generated_classes) #--------------------------------------------------------------------------
y_true = [1 if cls in reference_classes else 0 for cls in all_classes] # Printing Method(s)
y_pred = [1 if cls in generated_classes else 0 for cls in all_classes] #--------------------------------------------------------------------------
self.precision = precision_score(y_true, y_pred) def print_scores(self):
self.recall = recall_score(y_true, y_pred) for element, scores in self.scores.items():
self.f1 = f1_score(y_true, y_pred) print(f"Metrics for {element.capitalize()}:")
\ No newline at end of file print(f"\tPrecision: {scores['precision']:.4f}")
print(f"\tRecall: {scores['recall']:.4f}")
print(f"\tF1 Score: {scores['f1']:.4f}")
print("----------------------------")
\ No newline at end of file
...@@ -114,17 +114,21 @@ class Ontology: ...@@ -114,17 +114,21 @@ class Ontology:
return self._get_elements_of_type(OWL.Restriction) return self._get_elements_of_type(OWL.Restriction)
def get_individuals(self) -> list: def get_individuals(self) -> list:
"""Extract all individuals from the ontology.""" """Extract all individuals from the ontology, including elements explicitly typed as owl:Individual,
all_types = set(self.graph.subjects(RDF.type)) owl:NamedIndividual or any class."""
non_individuals = {element.reference for element in
self.get_classes() +
self.get_object_properties() +
self.get_data_properties()}
individuals = all_types - non_individuals # Getting elements of type owl:NamedIndividual and owl:Individual
individuals = self._get_elements_of_type(OWL.NamedIndividual)
individuals += self._get_elements_of_type(URIRef("http://www.w3.org/2002/07/owl#Individual"))
# Getting all elements typed as one of the classes
all_classes = {cls.reference for cls in self.get_classes()}
for cls in all_classes:
individuals += [NamedElement(s, self.graph) if isinstance(s, URIRef) else BlankElement(s, self.graph)
for s, _, o in self.graph.triples((None, RDF.type, cls))]
return list(set(individuals)) # Ensuring uniqueness
return [NamedElement(i, self.graph) if isinstance(i, URIRef) else BlankElement(i, self.graph)
for i in individuals]
def get_annotations(self): def get_annotations(self):
"""Extract all annotation comments from the ontology.""" """Extract all annotation comments from the ontology."""
......
...@@ -33,9 +33,9 @@ class Report: ...@@ -33,9 +33,9 @@ class Report:
report_str += "The generated ontology and the reference ontology have the same number of classes." report_str += "The generated ontology and the reference ontology have the same number of classes."
report_str += "\n\nEvaluation Metrics:" report_str += "\n\nEvaluation Metrics:"
report_str += f"\nPrecision: {self.metrics.precision}" report_str += f'\nPrecision: {self.metrics.scores["overall"]["precision"]}'
report_str += f"\nRecall: {self.metrics.recall}" report_str += f'\nRecall: {self.metrics.scores["overall"]["recall"]}'
report_str += f"\nF1 Score: {self.metrics.f1}" report_str += f'\nF1 Score: {self.metrics.scores["overall"]["f1"]}'
return report_str return report_str
......
...@@ -2,9 +2,39 @@ ...@@ -2,9 +2,39 @@
# -*-coding:Utf-8 -* # -*-coding:Utf-8 -*
#============================================================================== #==============================================================================
# ontoScorer: [brief description of the module] # test_metrics: Metrics Testing Module
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Detailed module description, if needed # Contains tests for verifying functionality of the Metrics class.
#============================================================================== #==============================================================================
# TODO import unittest
import os
from context import ontoScorer
from ontoScorer.ontology import Ontology
from ontoScorer.metrics import Metrics
class TestMetrics(unittest.TestCase):
def setUp(self):
DATA_FOLDER_PATH = f'{os.path.dirname(os.path.abspath(__file__))}/test_data'
self.ontology1_path = f"{DATA_FOLDER_PATH}/ontology_a.ttl"
self.ontology2_path = f"{DATA_FOLDER_PATH}/ontology_b.ttl"
self.onto1 = Ontology(self.ontology1_path)
self.onto2 = Ontology(self.ontology2_path)
self.metrics = Metrics()
def test_calculate_scores(self):
self.metrics.calculate(self.onto1, self.onto2)
for key in self.metrics.scores:
self.assertTrue(0 <= self.metrics.scores[key]["precision"] <= 1)
self.assertTrue(0 <= self.metrics.scores[key]["recall"] <= 1)
self.assertTrue(0 <= self.metrics.scores[key]["f1"] <= 1)
def test_print_scores(self):
self.metrics.calculate(self.onto1, self.onto2)
self.metrics.print_scores()
if __name__ == "__main__":
unittest.main()
...@@ -68,7 +68,7 @@ class TestOntology(unittest.TestCase): ...@@ -68,7 +68,7 @@ class TestOntology(unittest.TestCase):
def test_get_individuals(self): def test_get_individuals(self):
individuals_names = {ind.name() for ind in self.onto1.get_individuals()} individuals_names = {ind.name() for ind in self.onto1.get_individuals()}
self.assertEqual(len(individuals_names), 1)
self.assertIn("SolarSystem", individuals_names) self.assertIn("SolarSystem", individuals_names)
self.assertNotIn("gravitation", individuals_names) self.assertNotIn("gravitation", individuals_names)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment