diff --git a/README.md b/README.md index d295f3e217e81ce747e53d45effec114b79934e8..8e9545592c9614df72ec7a2688e5ab949cee3b57 100644 --- a/README.md +++ b/README.md @@ -21,27 +21,27 @@ All dependencies are listed in **requirements.txt**. We recommend creating a virtual environment before proceeding: -1. Create a virtual environment: -> python3 -m venv env +1. Create a virtual environment: `python3 -m venv env` -2. Activate the virtual environment: -> source env/bin/activate +2. Activate the virtual environment: `source env/bin/activate` ## Usage Here's an example of how to use ontoScorer: -> from ontoScorer import OntoScorer -> -> # Initialize the scorer -> scorer = OntoScorer("path/to/reference_ontology.ttl", "path/to/generated_ontology.ttl") -> -> # Compare the ontologies -> scorer.compare() -> -> # Print the scores -> scorer.print_scores() -> -> # Generate a report -> scorer.generate_report("path/to/report.txt") +```python +from ontoScorer import OntoScorer + +# Initialize the scorer +scorer = OntoScorer("path/to/reference_ontology.ttl", "path/to/generated_ontology.ttl") + +# Compare the ontologies +scorer.compare() + +# Print the scores +scorer.print_scores() + +# Generate a report +scorer.generate_report("path/to/report.txt") +``` diff --git a/data/generated_ontology.ttl b/data/generated_ontology.ttl new file mode 100644 index 0000000000000000000000000000000000000000..2b5ee473b9aaafcdab815b06788bd4a9aa435474 --- /dev/null +++ b/data/generated_ontology.ttl @@ -0,0 +1,104 @@ +@prefix ns1: <https://tenet.tetras-libre.fr/base-ontology#> . +@prefix owl: <http://www.w3.org/2002/07/owl#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +<https://tenet.tetras-libre.fr/extract-result#SolarSystem> a owl:Individual, + <https://tenet.tetras-libre.fr/extract-result#system>, + <https://tenet.tetras-libre.fr/extract-result#system-hasPart-object>, + <https://tenet.tetras-libre.fr/extract-result#system-hasPart-sun> ; + rdfs:label "SolarSystem" ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#direct> a owl:ObjectProperty ; + rdfs:label "direct" ; + rdfs:subPropertyOf ns1:Out_ObjectProperty ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#gravitation-bind-system> a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty <https://tenet.tetras-libre.fr/extract-result#bind> ; + owl:someValuesFrom <https://tenet.tetras-libre.fr/extract-result#system> ], + <https://tenet.tetras-libre.fr/extract-result#gravitation> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#hasManner> a owl:ObjectProperty ; + rdfs:label "hasManner" ; + rdfs:subPropertyOf ns1:Out_ObjectProperty ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#not-direct> a owl:ObjectProperty ; + rdfs:subPropertyOf ns1:Out_ObjectProperty ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#object-orbit-hasManner-direct-sun> a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty <https://tenet.tetras-libre.fr/extract-result#orbit-hasManner-direct> ; + owl:someValuesFrom <https://tenet.tetras-libre.fr/extract-result#sun> ], + <https://tenet.tetras-libre.fr/extract-result#object> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#object-orbit-hasManner-not-direct-sun> a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty <https://tenet.tetras-libre.fr/extract-result#orbit-hasManner-not-direct> ; + owl:someValuesFrom <https://tenet.tetras-libre.fr/extract-result#sun> ], + <https://tenet.tetras-libre.fr/extract-result#object> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#bind> a owl:ObjectProperty ; + rdfs:label "bind" ; + rdfs:subPropertyOf ns1:Out_ObjectProperty ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#gravitation> a owl:Class ; + rdfs:label "gravitation" ; + rdfs:subClassOf ns1:Entity ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#orbit-hasManner-direct> a owl:ObjectProperty ; + rdfs:subPropertyOf <https://tenet.tetras-libre.fr/extract-result#orbit> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#orbit-hasManner-not-direct> a owl:ObjectProperty ; + rdfs:subPropertyOf <https://tenet.tetras-libre.fr/extract-result#orbit> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#system-hasPart-object> a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty <https://tenet.tetras-libre.fr/extract-result#hasPart> ; + owl:someValuesFrom <https://tenet.tetras-libre.fr/extract-result#object> ], + <https://tenet.tetras-libre.fr/extract-result#system> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#system-hasPart-sun> a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty <https://tenet.tetras-libre.fr/extract-result#hasPart> ; + owl:someValuesFrom <https://tenet.tetras-libre.fr/extract-result#sun> ], + <https://tenet.tetras-libre.fr/extract-result#system> ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#hasPart> a owl:ObjectProperty ; + rdfs:label "hasPart" ; + rdfs:subPropertyOf ns1:Out_ObjectProperty ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#orbit> a owl:ObjectProperty ; + rdfs:label "orbit" ; + rdfs:subPropertyOf ns1:Out_ObjectProperty ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#object> a owl:Class ; + rdfs:label "object" ; + rdfs:subClassOf ns1:Entity ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#sun> a owl:Class ; + rdfs:label "sun" ; + rdfs:subClassOf ns1:Entity ; + ns1:fromStructure "unknown" . + +<https://tenet.tetras-libre.fr/extract-result#system> a owl:Class ; + rdfs:label "system" ; + rdfs:subClassOf ns1:Entity, + ns1:Undetermined_Thing ; + ns1:fromStructure "unknown" . + diff --git a/data/reference_ontology.ttl b/data/reference_ontology.ttl new file mode 100644 index 0000000000000000000000000000000000000000..2b9713297e6616b251f740fc10991dc2f265c5c4 --- /dev/null +++ b/data/reference_ontology.ttl @@ -0,0 +1,109 @@ +@prefix base: <https://reference.tetras-libre.fr/base-ontology#> . +@prefix owl: <http://www.w3.org/2002/07/owl#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix result: <https://reference.tetras-libre.fr/expected-result#> . + +result:SolarSystem a owl:Individual, + result:system, + result:system-isBindBy-gravitation, + result:system-hasPart-object-orbit-sun, + result:system-hasPart-sun ; + rdfs:label "SolarSystem" . + +result:direct a owl:ObjectProperty ; + rdfs:label "direct" ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:gravitation-bind-system a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:bind ; + owl:someValuesFrom result:system ], + result:gravitation . + +result:hasManner a owl:ObjectProperty ; + rdfs:label "hasManner" ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:not-direct a owl:ObjectProperty ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:object-orbit-sun a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:orbit ; + owl:someValuesFrom result:sun ], + result:object . + +result:object-orbit-hasManner-direct-sun a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:orbit-hasManner-direct ; + owl:someValuesFrom result:sun ], + result:object-orbit-sun . + +result:object-orbit-hasManner-not-direct-sun a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:orbit-hasManner-not-direct ; + owl:someValuesFrom result:sun ], + result:object-orbit-sun . + +result:gravitation a owl:Class ; + rdfs:label "gravitation" ; + rdfs:subClassOf base:Entity . + +result:system-isBindBy-gravitation a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:isBindBy ; + owl:someValuesFrom result:gravitation ], + result:system . + +result:system-hasPart-object a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:hasPart ; + owl:someValuesFrom result:object ], + result:system . + +result:system-hasPart-object-orbit-sun a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:hasPart ; + owl:someValuesFrom result:object-orbit-sun ], + result:system-hasPart-object . + +result:system-hasPart-sun a owl:Class ; + rdfs:subClassOf [ a owl:Restriction ; + owl:onProperty result:hasPart ; + owl:someValuesFrom result:sun ], + result:system . + +result:bind a owl:ObjectProperty ; + rdfs:label "bind" ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:isBindBy owl:inverseOf result:bind ; + rdfs:label "isBindBy" ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:orbit a owl:ObjectProperty ; + rdfs:label "orbit" ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:orbit-hasManner-direct a owl:ObjectProperty ; + rdfs:subPropertyOf result:orbit . + +result:orbit-hasManner-not-direct a owl:ObjectProperty ; + rdfs:subPropertyOf result:orbit . + +result:hasPart a owl:ObjectProperty ; + rdfs:label "hasPart" ; + rdfs:subPropertyOf base:Out_ObjectProperty . + +result:object a owl:Class ; + rdfs:label "object" ; + rdfs:subClassOf base:Entity . + +result:system a owl:Class ; + rdfs:label "system" ; + rdfs:subClassOf base:Entity. + +result:sun a owl:Class ; + rdfs:label "sun" ; + rdfs:subClassOf base:Entity . + diff --git a/main.py b/main.py new file mode 100644 index 0000000000000000000000000000000000000000..fe59030619a4a1b234199529091cbfa65cd38e39 --- /dev/null +++ b/main.py @@ -0,0 +1,30 @@ +#!/usr/bin/python3.10 +# -*-coding:Utf-8 -* + +#============================================================================== +# ontoScorer: [brief description of the module] +#------------------------------------------------------------------------------ +# Detailed module description, if needed +#============================================================================== + +from ontoScorer.scorer import OntoScorer + +def main(): + # Define the path to the data folder + DATA_FOLDER_PATH = "data" + + # Define paths to the ontology files + REFERENCE_ONTOLOGY_PATH = f"{DATA_FOLDER_PATH}/reference_ontology.ttl" + GENERATED_ONTOLOGY_PATH = f"{DATA_FOLDER_PATH}/generated_ontology.ttl" + + # Initialize the scorer + scorer = OntoScorer(REFERENCE_ONTOLOGY_PATH, GENERATED_ONTOLOGY_PATH) + + # Compare the ontologies + scorer.compare() + + # Generate a report + scorer.generate_report() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ontoScorer/__init__.py b/ontoScorer/__init__.py index 4415d78eaa2ad835966f1b350fdefbdf3e9d9d61..162f65309d18697fde9e913052b0a634a00fcc09 100644 --- a/ontoScorer/__init__.py +++ b/ontoScorer/__init__.py @@ -1,10 +1,5 @@ -#!/usr/bin/python3.10 -# -*-coding:Utf-8 -* - -#============================================================================== -# ontoScorer: [brief description of the module] -#------------------------------------------------------------------------------ -# Detailed module description, if needed -#============================================================================== - -# TODO +# -- Update System Path +import os, sys +LIB_PATH = os.path.dirname(os.path.abspath(__file__)) + '/' +os.chdir(LIB_PATH) +sys.path.insert(0, os.path.abspath(LIB_PATH)) diff --git a/ontoScorer/__pycache__/__init__.cpython-311.pyc b/ontoScorer/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15c9d4b5e58309a39ad2614703ebff6ad3759740 Binary files /dev/null and b/ontoScorer/__pycache__/__init__.cpython-311.pyc differ diff --git a/ontoScorer/__pycache__/metrics.cpython-311.pyc b/ontoScorer/__pycache__/metrics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3d7b0f5b4273d827168b62f4a05c4bc64426e93 Binary files /dev/null and b/ontoScorer/__pycache__/metrics.cpython-311.pyc differ diff --git a/ontoScorer/__pycache__/ontology.cpython-311.pyc b/ontoScorer/__pycache__/ontology.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aafe00e11d0aad998e8c002a193da51608e8d62d Binary files /dev/null and b/ontoScorer/__pycache__/ontology.cpython-311.pyc differ diff --git a/ontoScorer/__pycache__/report.cpython-311.pyc b/ontoScorer/__pycache__/report.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ac64f032d3c2903f9d0664b49ed1fe36c6d71ac Binary files /dev/null and b/ontoScorer/__pycache__/report.cpython-311.pyc differ diff --git a/ontoScorer/__pycache__/scorer.cpython-311.pyc b/ontoScorer/__pycache__/scorer.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15863905c230a177ffba8eb4a80f491d1fd94ecb Binary files /dev/null and b/ontoScorer/__pycache__/scorer.cpython-311.pyc differ diff --git a/ontoScorer/metrics.py b/ontoScorer/metrics.py index 4415d78eaa2ad835966f1b350fdefbdf3e9d9d61..e905353156d991bc58e573834c279ae3a8f3a470 100644 --- a/ontoScorer/metrics.py +++ b/ontoScorer/metrics.py @@ -4,7 +4,23 @@ #============================================================================== # ontoScorer: [brief description of the module] #------------------------------------------------------------------------------ -# Detailed module description, if needed +# Detailed module description #============================================================================== -# TODO +from sklearn.metrics import precision_score, recall_score, f1_score + +class Metrics: + def __init__(self): + self.precision = 0 + self.recall = 0 + self.f1 = 0 + + def calculate(self, reference_classes, generated_classes): + all_classes = reference_classes.union(generated_classes) + y_true = [1 if cls in reference_classes else 0 for cls in all_classes] + y_pred = [1 if cls in generated_classes else 0 for cls in all_classes] + + self.precision = precision_score(y_true, y_pred) + self.recall = recall_score(y_true, y_pred) + self.f1 = f1_score(y_true, y_pred) + diff --git a/ontoScorer/ontology.py b/ontoScorer/ontology.py index 4415d78eaa2ad835966f1b350fdefbdf3e9d9d61..f9403cbbe97ad455713da15ea5d7b27795c759dc 100644 --- a/ontoScorer/ontology.py +++ b/ontoScorer/ontology.py @@ -7,4 +7,29 @@ # Detailed module description, if needed #============================================================================== -# TODO +from rdflib import Graph, OWL +from rdflib.namespace import split_uri + +class Ontology: + def __init__(self, ontology_path): + self.path = ontology_path + self.graph = self.load_ontology(ontology_path) + self.classes = self.get_classes() + + def load_ontology(self, path): + g = Graph() + g.parse(path, format="ttl") + return g + + def get_classes(self): + classes = set() + triplets_count = 0 + for s, p, o in self.graph.triples((None, None, None)): + triplets_count += 1 + if o == OWL.Class: + _, class_name = split_uri(s) + classes.add(class_name) + return classes + + def compare_to(self, other_ontology): + return self.classes, other_ontology.classes \ No newline at end of file diff --git a/ontoScorer/report.py b/ontoScorer/report.py index 4415d78eaa2ad835966f1b350fdefbdf3e9d9d61..30f7659bb493a623975d29a418ae5f42f48bf32a 100644 --- a/ontoScorer/report.py +++ b/ontoScorer/report.py @@ -7,4 +7,36 @@ # Detailed module description, if needed #============================================================================== -# TODO +class Report: + def __init__(self, reference_ontology, generated_ontology, comparison_result, 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)}" + + report_str += f"\n\nGenerated Ontology: {self.generated_ontology.path}" + report_str += f"\nNumber of classes in Generated Ontology: {len(self.generated_ontology.classes)}" + + # 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." + + report_str += "\n\nEvaluation Metrics:" + report_str += f"\nPrecision: {self.metrics.precision}" + report_str += f"\nRecall: {self.metrics.recall}" + report_str += f"\nF1 Score: {self.metrics.f1}" + + return report_str + + diff --git a/ontoScorer/scorer.py b/ontoScorer/scorer.py index 4415d78eaa2ad835966f1b350fdefbdf3e9d9d61..2ed808558b8a67385d0897e8010149eb4086bcf2 100644 --- a/ontoScorer/scorer.py +++ b/ontoScorer/scorer.py @@ -7,4 +7,28 @@ # Detailed module description, if needed #============================================================================== -# TODO +from ontology import Ontology +from report import Report +from metrics import Metrics + +class OntoScorer: + def __init__(self, reference_ontology_path, generated_ontology_path): + self.reference_ontology = Ontology(reference_ontology_path) + self.generated_ontology = Ontology(generated_ontology_path) + self.metrics = Metrics() + + def compare(self): + self.comparison_result = self.reference_ontology.compare_to(self.generated_ontology) + self.metrics.calculate(*self.comparison_result) + + def generate_report(self): + report = Report(self.reference_ontology, + self.generated_ontology, + self.comparison_result, + self.metrics) + print(report.generate()) + + + # def generate_report(self, report_path): + # report = Report(self.comparison_result, self.metrics) + # report.generate(report_path) diff --git a/tests/__init__.py b/tests/__init__.py index 4415d78eaa2ad835966f1b350fdefbdf3e9d9d61..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,10 +0,0 @@ -#!/usr/bin/python3.10 -# -*-coding:Utf-8 -* - -#============================================================================== -# ontoScorer: [brief description of the module] -#------------------------------------------------------------------------------ -# Detailed module description, if needed -#============================================================================== - -# TODO