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

Update metrics computing for entities

parent 067f000f
Branches
No related tags found
No related merge requests found
......@@ -21,7 +21,7 @@ def main():
scorer = OntoScorer(REFERENCE_ONTOLOGY_PATH, GENERATED_ONTOLOGY_PATH)
# Compare the ontologies
scorer.compare()
scorer.compute_metrics()
# Generate a report
scorer.generate_report()
......
......@@ -25,6 +25,8 @@ class Score:
self.f1 = None
self.total_elements = 0
self.matched_elements = 0
self.y_true = [] # Nouveau champ
self.y_pred = [] # Nouveau champ
#--------------------------------------------------------------------------
......@@ -42,6 +44,8 @@ class Score:
Returns:
None
"""
self.y_true = y_true
self.y_pred = y_pred
self.precision = precision_score(y_true, y_pred)
self.recall = recall_score(y_true, y_pred)
self.f1 = f1_score(y_true, y_pred)
......
......@@ -15,6 +15,7 @@ matches the reference.
from ontoScorer.ontology import Ontology
from ontoScorer.metric_score import Score
class Metrics:
"""
Metrics class provides functionalities to compute scores for ontology
......@@ -26,59 +27,73 @@ class Metrics:
#--------------------------------------------------------------------------
def __init__(self):
"""
Initializes score categories for various ontology elements.
"""
self.scores = {
"class": Score(),
"object_property": Score(),
"data_property": Score(),
"restriction": Score(),
"individual": Score(),
"annotation": Score(),
"overall": Score()
"entities": {
"classes": Score(),
"object_properties": Score(),
"individuals": Score(),
"synthesis": Score() # Synthesis score for entities axis
},
"taxonomic_relations": {
"subclass": Score(),
"instanciation": Score(),
"synthesis": Score() # Synthesis score for taxonomic relations axis
},
"non_taxonomic_relations": {
"object_properties": Score(),
"data_properties": Score(),
"domains": Score(),
"ranges": Score(),
"synthesis": Score() # Synthesis score for non-taxonomic relations axis
},
"axioms": {
"restriction_axioms": Score(),
"synthesis": Score() # Synthesis score for axioms axis
}
}
#--------------------------------------------------------------------------
# Computing Method(s)
#--------------------------------------------------------------------------
def calculate(self, reference_ontology, generated_ontology):
"""
Compute scores (precision, recall, f1) for each ontology element category.
Args:
- reference_ontology: Ontology object representing the reference ontology.
- generated_ontology: Ontology object representing the generated ontology.
"""
methods = [
("class", "get_classes"),
("object_property", "get_object_properties"),
# Additional methods can be uncommented as needed
#("data_property", "get_data_properties"),
#("restriction", "get_restrictions"),
("individual", "get_individuals"),
#("annotation", "get_annotations")
]
@staticmethod
def deduplicate_elements(elements, comparison_function):
unique_elements = []
for elem in elements:
if not any([comparison_function(elem, unique_elem) for unique_elem in unique_elements]):
unique_elements.append(elem)
return unique_elements
def compute_entity_scores(self, reference_ontology, generated_ontology):
entity_methods = {
"classes": ("get_classes", Ontology.compare_entity_names),
"object_properties": ("get_object_properties", Ontology.compare_entity_names),
"individuals": ("get_individuals", Ontology.compare_entity_names)
}
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)()])
for score_name, (method_name, comparison_function) in entity_methods.items():
reference_elements_raw = getattr(reference_ontology, method_name)()
generated_elements_raw = getattr(generated_ontology, method_name)()
reference_elements = Metrics.deduplicate_elements(reference_elements_raw, comparison_function)
generated_elements = Metrics.deduplicate_elements(generated_elements_raw, comparison_function)
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]
all_elements = list(set(reference_elements + generated_elements))
all_elements = Metrics.deduplicate_elements(all_elements, comparison_function)
y_true = [1 if any([comparison_function(elem, ref_elem) for ref_elem in reference_elements]) else 0 for elem in all_elements]
y_pred = [1 if any([comparison_function(elem, gen_elem) for gen_elem in generated_elements]) else 0 for elem in all_elements]
self.scores[score_name].compute(y_true, y_pred)
self.scores["entities"][score_name].compute(y_true, y_pred)
y_true_overall.extend(y_true)
y_pred_overall.extend(y_pred)
self.scores["overall"].compute(y_true_overall, y_pred_overall)
self.scores["entities"]["synthesis"].compute(y_true_overall, y_pred_overall)
......@@ -90,7 +105,8 @@ class Metrics:
"""
Prints the scores (precision, recall, f1) for each ontology element category.
"""
for element, score in self.scores.items():
print(f"Metrics for {element.capitalize()}:")
entity_scores = self.scores["entities"]
for element, score in entity_scores.items():
print(f"Metrics for {element.capitalize()} (Entity axis):")
print(score)
print("----------------------------")
......@@ -122,6 +122,11 @@ class Ontology:
# Comparison Method(s)
#--------------------------------------------------------------------------
@staticmethod
def compare_entity_names(entity1, entity2):
return entity1.name() == entity2.name()
def compare_to(self, other_ontology) -> tuple:
"""Compare classes of the current ontology with another."""
self_classes = {c.name() for c in self.classes}
......
#!/usr/bin/python3.10
# -*-coding:Utf-8 -*
#==============================================================================
# ontoScorer: [brief description of the module]
#------------------------------------------------------------------------------
# Detailed module description, if needed
#==============================================================================
"""
Ontology Report Module
------------------------------------------------------------------------------
This module provides a means to generate detailed reports that compare a
reference ontology with a generated ontology. It uses various metrics to
highlight the similarities and differences between the two ontologies,
facilitating a comprehensive evaluation of ontology generation techniques.
The reports can be used for debugging, optimization, and academic purposes,
helping developers and researchers to better understand the quality and
characteristics of generated ontologies in comparison to a reference.
Classes:
- Report: Main class that represents an ontology comparison report.
"""
class Report:
def __init__(self, reference_ontology, generated_ontology, comparison_result, metrics):
"""
A class used to generate detailed reports on the comparison of a
reference ontology with a generated ontology.
Attributes:
------------
reference_ontology : obj
The reference ontology against which the generated ontology is compared.
generated_ontology : obj
The ontology that has been generated and needs to be evaluated.
metrics : obj
The metrics used to evaluate the generated ontology against the reference.
Methods:
---------
generate() -> str:
Produces a string representation of the report, detailing the comparison
results and associated metrics.
"""
def __init__(self, reference_ontology, generated_ontology, metrics):
"""
Initializes the Report with a reference ontology, generated ontology,
and comparison metrics.
"""
self.reference_ontology = reference_ontology
self.generated_ontology = generated_ontology
self.comparison_result = comparison_result
self.metrics = metrics
def generate(self):
report_str = "=== Ontology Evaluation Report ===\n"
report_str += f"\nReference Ontology: {self.reference_ontology.path}"
report_str += f"\nNumber of classes in Reference Ontology: {len(self.reference_ontology.classes)}"
def _generate_evaluation_metrics(self, category_name):
scores_str = f"\n\n== {category_name.replace('_', ' ').capitalize()} ==\n"
report_str += f"\n\nGenerated Ontology: {self.generated_ontology.path}"
report_str += f"\nNumber of classes in Generated Ontology: {len(self.generated_ontology.classes)}"
category_scores = self.metrics.scores[category_name]
for element, score in category_scores.items():
if element != "synthesis": # We'll print synthesis separately
scores_str += f"\nMetrics for {element.replace('_', ' ').capitalize()}:\n"
scores_str += f'Precision: {score._format_metric(score.precision)}\n'
scores_str += f'Recall: {score._format_metric(score.recall)}\n'
scores_str += f'F1 Score: {score._format_metric(score.f1)}\n'
scores_str += f'Total Elements: {score.total_elements}\n'
scores_str += f'Matched Elements: {score.matched_elements}\n'
# Comparison of the number of classes
report_str += "\n\nComparison Result: "
if len(self.reference_ontology.classes) > len(self.generated_ontology.classes):
report_str += "The generated ontology has fewer classes than the reference ontology."
elif len(self.reference_ontology.classes) < len(self.generated_ontology.classes):
report_str += "The generated ontology has more classes than the reference ontology."
else:
report_str += "The generated ontology and the reference ontology have the same number of classes."
# Print synthesis score at the end for the category
synthesis_score = category_scores["synthesis"]
scores_str += "\nOverall Metrics (Synthesis):\n"
scores_str += f'Precision: {synthesis_score._format_metric(synthesis_score.precision)}\n'
scores_str += f'Recall: {synthesis_score._format_metric(synthesis_score.recall)}\n'
scores_str += f'F1 Score: {synthesis_score._format_metric(synthesis_score.f1)}\n'
report_str += "\n\nEvaluation Metrics:"
report_str += f'\nPrecision: {self.metrics.scores["overall"]["precision"]}'
report_str += f'\nRecall: {self.metrics.scores["overall"]["recall"]}'
report_str += f'\nF1 Score: {self.metrics.scores["overall"]["f1"]}'
return scores_str
def generate(self):
report_str = "=== Ontology Evaluation Report ===\n"
# Introduction
report_str += "\nComparing Reference Ontology with Generated Ontology.\n"
# Detailed Evaluation Metrics
for category in self.metrics.scores.keys():
report_str += self._generate_evaluation_metrics(category)
return report_str
......@@ -17,14 +17,13 @@ class OntoScorer:
self.generated_ontology = Ontology(generated_ontology_path)
self.metrics = Metrics()
def compare(self):
def compute_metrics(self):
self.comparison_result = self.reference_ontology.compare_to(self.generated_ontology)
self.metrics.calculate(self.reference_ontology, self.generated_ontology)
self.metrics.compute_entity_scores(self.reference_ontology, self.generated_ontology)
def generate_report(self):
report = Report(self.reference_ontology,
self.generated_ontology,
self.comparison_result,
self.metrics)
print(report.generate())
......
......@@ -27,8 +27,8 @@ class TestMetrics(unittest.TestCase):
self.metrics = Metrics()
def test_calculate_scores(self):
self.metrics.calculate(self.onto1, self.onto2)
def test_computes_scores(self):
self.metrics.compute_entity_scores(self.onto1, self.onto2)
for element, score in self.metrics.scores.items():
if score.total_elements == 0:
self.assertIsNone(score.precision, f"Precision for {element} should be None when no elements are present")
......@@ -41,7 +41,7 @@ class TestMetrics(unittest.TestCase):
def test_print_scores(self):
self.metrics.calculate(self.onto1, self.onto2)
self.metrics.compute_entity_scores(self.onto1, self.onto2)
print()
self.metrics.print_scores()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment