ripple-down-rules 0.0.0__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/PKG-INFO +4 -3
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/README.md +3 -2
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/pyproject.toml +1 -1
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datasets.py +13 -6
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/callable_expression.py +2 -2
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/table.py +8 -3
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/rdr.py +9 -7
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/rules.py +4 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/utils.py +19 -1
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/PKG-INFO +4 -3
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/test/test_json_serialization.py +11 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/setup.cfg +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/__init__.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/__init__.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/dataclasses.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/enums.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/generated/__init__.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/generated/column/__init__.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/generated/row/__init__.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/experts.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/failures.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/prompt.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/requires.txt +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/test/test_rdr.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/test/test_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/test/test_relational_rdr.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/test/test_relational_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/test/test_sql_model.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.2
|
4
4
|
Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
|
5
5
|
Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
|
6
6
|
Project-URL: Homepage, https://github.com/AbdelrhmanBassiouny/ripple_down_rules
|
@@ -34,6 +34,7 @@ Fit the SCRDR to the data, then classify one of the data cases to check if its c
|
|
34
34
|
and render the tree to a file:
|
35
35
|
|
36
36
|
```Python
|
37
|
+
from ripple_down_rules.datastructures import CaseQuery
|
37
38
|
from ripple_down_rules.rdr import SingleClassRDR
|
38
39
|
from ripple_down_rules.datasets import load_zoo_dataset
|
39
40
|
from ripple_down_rules.utils import render_tree
|
@@ -43,8 +44,8 @@ all_cases, targets = load_zoo_dataset()
|
|
43
44
|
scrdr = SingleClassRDR()
|
44
45
|
|
45
46
|
# Fit the SCRDR to the data
|
46
|
-
|
47
|
-
|
47
|
+
case_queries = [CaseQuery(case, target=target) for case, target in zip(all_cases, targets)]
|
48
|
+
scrdr.fit(case_queries, animate_tree=True)
|
48
49
|
|
49
50
|
# Render the tree to a file
|
50
51
|
render_tree(scrdr.start_rule, use_dot_exporter=True, filename="scrdr")
|
@@ -22,6 +22,7 @@ Fit the SCRDR to the data, then classify one of the data cases to check if its c
|
|
22
22
|
and render the tree to a file:
|
23
23
|
|
24
24
|
```Python
|
25
|
+
from ripple_down_rules.datastructures import CaseQuery
|
25
26
|
from ripple_down_rules.rdr import SingleClassRDR
|
26
27
|
from ripple_down_rules.datasets import load_zoo_dataset
|
27
28
|
from ripple_down_rules.utils import render_tree
|
@@ -31,8 +32,8 @@ all_cases, targets = load_zoo_dataset()
|
|
31
32
|
scrdr = SingleClassRDR()
|
32
33
|
|
33
34
|
# Fit the SCRDR to the data
|
34
|
-
|
35
|
-
|
35
|
+
case_queries = [CaseQuery(case, target=target) for case, target in zip(all_cases, targets)]
|
36
|
+
scrdr.fit(case_queries, animate_tree=True)
|
36
37
|
|
37
38
|
# Render the tree to a file
|
38
39
|
render_tree(scrdr.start_rule, use_dot_exporter=True, filename="scrdr")
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "ripple_down_rules"
|
9
|
-
version = "0.0.
|
9
|
+
version = "0.0.2"
|
10
10
|
description = "Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning."
|
11
11
|
readme = "README.md"
|
12
12
|
authors = [{ name = "Abdelrhman Bassiouny", email = "abassiou@uni-bremen.de" }]
|
@@ -6,7 +6,7 @@ import pickle
|
|
6
6
|
import sqlalchemy
|
7
7
|
from sqlalchemy import ForeignKey
|
8
8
|
from sqlalchemy.orm import MappedAsDataclass, Mapped, mapped_column, relationship
|
9
|
-
from typing_extensions import Tuple, List, Set
|
9
|
+
from typing_extensions import Tuple, List, Set, Optional
|
10
10
|
from ucimlrepo import fetch_ucirepo
|
11
11
|
|
12
12
|
from .datastructures import Case, create_rows_from_dataframe, Category, Column
|
@@ -38,9 +38,9 @@ def save_dataset_to_cache(dataset, cache_file):
|
|
38
38
|
print("Dataset cached successfully.")
|
39
39
|
|
40
40
|
|
41
|
-
def get_dataset(dataset_id, cache_file):
|
41
|
+
def get_dataset(dataset_id, cache_file: Optional[str] = None):
|
42
42
|
"""Fetches dataset from cache or downloads it if not available."""
|
43
|
-
dataset = load_cached_dataset(cache_file)
|
43
|
+
dataset = load_cached_dataset(cache_file) if cache_file else None
|
44
44
|
if dataset is None:
|
45
45
|
print("Downloading dataset...")
|
46
46
|
dataset = fetch_ucirepo(id=dataset_id)
|
@@ -50,16 +50,23 @@ def get_dataset(dataset_id, cache_file):
|
|
50
50
|
print("Error: Failed to fetch dataset.")
|
51
51
|
return None
|
52
52
|
|
53
|
-
|
53
|
+
if cache_file:
|
54
|
+
save_dataset_to_cache(dataset, cache_file)
|
55
|
+
|
56
|
+
dataset = {
|
57
|
+
"features": dataset.data.features,
|
58
|
+
"targets": dataset.data.targets,
|
59
|
+
"ids": dataset.data.ids,
|
60
|
+
}
|
54
61
|
|
55
62
|
return dataset
|
56
63
|
|
57
64
|
|
58
|
-
def load_zoo_dataset(cache_file: str) -> Tuple[List[Case], List[Species]]:
|
65
|
+
def load_zoo_dataset(cache_file: Optional[str] = None) -> Tuple[List[Case], List[Species]]:
|
59
66
|
"""
|
60
67
|
Load the zoo dataset.
|
61
68
|
|
62
|
-
:param cache_file: the cache file.
|
69
|
+
:param cache_file: the cache file to store the dataset or load it from.
|
63
70
|
:return: all cases and targets.
|
64
71
|
"""
|
65
72
|
# fetch dataset
|
@@ -8,7 +8,7 @@ from sqlalchemy.orm import Session
|
|
8
8
|
from typing_extensions import Type, Optional, Any, List, Union, Tuple, Dict, Set
|
9
9
|
|
10
10
|
from .table import create_row, Row
|
11
|
-
from ..utils import SubclassJSONSerializer, get_full_class_name
|
11
|
+
from ..utils import SubclassJSONSerializer, get_full_class_name, get_type_from_string
|
12
12
|
|
13
13
|
|
14
14
|
class VariableVisitor(ast.NodeVisitor):
|
@@ -168,7 +168,7 @@ class CallableExpression(SubclassJSONSerializer):
|
|
168
168
|
|
169
169
|
@classmethod
|
170
170
|
def _from_json(cls, data: Dict[str, Any]) -> CallableExpression:
|
171
|
-
return cls(user_input=data["user_input"], conclusion_type=data["conclusion_type"])
|
171
|
+
return cls(user_input=data["user_input"], conclusion_type=get_type_from_string(data["conclusion_type"]))
|
172
172
|
|
173
173
|
|
174
174
|
def compile_expression_to_code(expression_tree: AST) -> Any:
|
{ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/table.py
RENAMED
@@ -4,6 +4,7 @@ import os
|
|
4
4
|
import time
|
5
5
|
from abc import ABC
|
6
6
|
from collections import UserDict
|
7
|
+
from copy import deepcopy
|
7
8
|
from dataclasses import dataclass
|
8
9
|
from enum import Enum
|
9
10
|
|
@@ -77,6 +78,7 @@ class SubClassFactory:
|
|
77
78
|
parent_class_alias = cls.__name__ + "_"
|
78
79
|
imports = f"from {cls.__module__} import {cls.__name__} as {parent_class_alias}\n"
|
79
80
|
class_code = f"class {name}({parent_class_alias}):\n"
|
81
|
+
class_attributes = deepcopy(class_attributes) if class_attributes else {}
|
80
82
|
class_attributes.update({"_value_range": range_})
|
81
83
|
for key, value in class_attributes.items():
|
82
84
|
if value is not None:
|
@@ -245,12 +247,15 @@ class Row(UserDict, SubClassFactory, SubclassJSONSerializer):
|
|
245
247
|
return isinstance(instance, (dict, UserDict, Row)) or super().__instancecheck__(instance)
|
246
248
|
|
247
249
|
def to_json(self) -> Dict[str, Any]:
|
250
|
+
serializable = {k: v for k, v in self.items() if not k.startswith("_")}
|
251
|
+
serializable["_id"] = self.id
|
248
252
|
return {**SubclassJSONSerializer.to_json(self),
|
249
|
-
**{k: v.to_json() if isinstance(v, SubclassJSONSerializer) else v for k, v in
|
253
|
+
**{k: v.to_json() if isinstance(v, SubclassJSONSerializer) else v for k, v in serializable.items()}}
|
250
254
|
|
251
255
|
@classmethod
|
252
256
|
def _from_json(cls, data: Dict[str, Any]) -> Row:
|
253
|
-
|
257
|
+
id_ = data.pop("_id")
|
258
|
+
return cls(id_=id_, **data)
|
254
259
|
|
255
260
|
|
256
261
|
@dataclass
|
@@ -377,7 +382,7 @@ class Column(set, SubClassFactory, SubclassJSONSerializer):
|
|
377
382
|
|
378
383
|
@classmethod
|
379
384
|
def _from_json(cls, data: Dict[str, Any]) -> Column:
|
380
|
-
return cls({ColumnValue(
|
385
|
+
return cls({ColumnValue.from_json(v) for id_, v in data.items()})
|
381
386
|
|
382
387
|
|
383
388
|
def create_rows_from_dataframe(df: DataFrame, name: Optional[str] = None) -> List[Row]:
|
@@ -111,15 +111,17 @@ class RippleDownRules(ABC):
|
|
111
111
|
if animate_tree and self.start_rule.size > num_rules:
|
112
112
|
num_rules = self.start_rule.size
|
113
113
|
self.update_figures()
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
114
|
+
i += 1
|
115
|
+
all_pred = [1 if p == t else 0
|
116
|
+
for case, target in zip(cases, targets) for p, t in zip(self.classify(case), target)]
|
117
|
+
all_predicted = targets and sum(all_pred) == len(targets)
|
118
|
+
num_iter_reached = n_iter and i >= n_iter
|
119
|
+
stop_iterating = all_predicted or num_iter_reached
|
120
|
+
if stop_iterating:
|
121
|
+
break
|
120
122
|
print(f"Recall: {sum(all_recall) / len(all_recall)}")
|
121
123
|
print(f"Precision: {sum(all_precision) / len(all_precision)}")
|
122
|
-
print(f"Accuracy: {all_pred}/{
|
124
|
+
print(f"Accuracy: {all_pred}/{len(targets)}")
|
123
125
|
print(f"Finished training in {i} iterations")
|
124
126
|
if animate_tree:
|
125
127
|
plt.ioff()
|
@@ -114,6 +114,8 @@ class HasAlternativeRule:
|
|
114
114
|
Set the alternative rule of the rule. It is important that no rules should be retracted or changed,
|
115
115
|
only new rules should be added.
|
116
116
|
"""
|
117
|
+
if new_rule is None:
|
118
|
+
return
|
117
119
|
if self.furthest_alternative:
|
118
120
|
self.furthest_alternative[-1].alternative = new_rule
|
119
121
|
else:
|
@@ -139,6 +141,8 @@ class HasRefinementRule:
|
|
139
141
|
Set the refinement rule of the rule. It is important that no rules should be retracted or changed,
|
140
142
|
only new rules should be added.
|
141
143
|
"""
|
144
|
+
if new_rule is None:
|
145
|
+
return
|
142
146
|
new_rule.top_rule = self
|
143
147
|
if self.refinement:
|
144
148
|
self.refinement.alternative = new_rule
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import importlib
|
3
4
|
import logging
|
4
5
|
from abc import abstractmethod
|
5
6
|
from collections import UserDict
|
@@ -23,6 +24,17 @@ if TYPE_CHECKING:
|
|
23
24
|
matplotlib.use("Qt5Agg") # or "Qt5Agg", depending on availability
|
24
25
|
|
25
26
|
|
27
|
+
def get_type_from_string(type_path: str):
|
28
|
+
"""
|
29
|
+
Get a type from a string describing its path using the format "module_path.ClassName".
|
30
|
+
|
31
|
+
:param type_path: The path to the type.
|
32
|
+
"""
|
33
|
+
module_path, class_name = type_path.rsplit(".", 1)
|
34
|
+
module = importlib.import_module(module_path)
|
35
|
+
return getattr(module, class_name)
|
36
|
+
|
37
|
+
|
26
38
|
def get_full_class_name(cls):
|
27
39
|
"""
|
28
40
|
Returns the full name of a class, including the module name.
|
@@ -75,9 +87,15 @@ class SubclassJSONSerializer:
|
|
75
87
|
:param data: The json dict
|
76
88
|
:return: The correct instance of the subclass
|
77
89
|
"""
|
90
|
+
if data is None:
|
91
|
+
return None
|
92
|
+
if get_full_class_name(cls) == data["_type"]:
|
93
|
+
return cls._from_json(data)
|
78
94
|
for subclass in recursive_subclasses(SubclassJSONSerializer):
|
79
95
|
if get_full_class_name(subclass) == data["_type"]:
|
80
|
-
|
96
|
+
subclass_data = deepcopy(data)
|
97
|
+
subclass_data.pop("_type")
|
98
|
+
return subclass._from_json(subclass_data)
|
81
99
|
|
82
100
|
raise ValueError("Unknown type {}".format(data["_type"]))
|
83
101
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.2
|
4
4
|
Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
|
5
5
|
Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
|
6
6
|
Project-URL: Homepage, https://github.com/AbdelrhmanBassiouny/ripple_down_rules
|
@@ -34,6 +34,7 @@ Fit the SCRDR to the data, then classify one of the data cases to check if its c
|
|
34
34
|
and render the tree to a file:
|
35
35
|
|
36
36
|
```Python
|
37
|
+
from ripple_down_rules.datastructures import CaseQuery
|
37
38
|
from ripple_down_rules.rdr import SingleClassRDR
|
38
39
|
from ripple_down_rules.datasets import load_zoo_dataset
|
39
40
|
from ripple_down_rules.utils import render_tree
|
@@ -43,8 +44,8 @@ all_cases, targets = load_zoo_dataset()
|
|
43
44
|
scrdr = SingleClassRDR()
|
44
45
|
|
45
46
|
# Fit the SCRDR to the data
|
46
|
-
|
47
|
-
|
47
|
+
case_queries = [CaseQuery(case, target=target) for case, target in zip(all_cases, targets)]
|
48
|
+
scrdr.fit(case_queries, animate_tree=True)
|
48
49
|
|
49
50
|
# Render the tree to a file
|
50
51
|
render_tree(scrdr.start_rule, use_dot_exporter=True, filename="scrdr")
|
@@ -26,6 +26,14 @@ class TestJSONSerialization(TestCase):
|
|
26
26
|
with open(f"{self.cache_dir}/scrdr.json", "w") as f:
|
27
27
|
json.dump(scrdr_json, f, indent=4)
|
28
28
|
|
29
|
+
# load the json from the file
|
30
|
+
with open(f"{self.cache_dir}/scrdr.json", "r") as f:
|
31
|
+
scrdr_json = json.load(f)
|
32
|
+
scrdr = SingleClassRDR.from_json(scrdr_json)
|
33
|
+
for case, target in zip(self.all_cases, self.targets):
|
34
|
+
cat = scrdr.classify(case)
|
35
|
+
self.assertEqual(cat, target)
|
36
|
+
|
29
37
|
def get_fit_scrdr(self, draw_tree=False) -> SingleClassRDR:
|
30
38
|
filename = self.expert_answers_dir + "/scrdr_expert_answers_fit"
|
31
39
|
expert = Human(use_loaded_answers=True)
|
@@ -35,4 +43,7 @@ class TestJSONSerialization(TestCase):
|
|
35
43
|
case_queries = [CaseQuery(case, target=target) for case, target in zip(self.all_cases, self.targets)]
|
36
44
|
scrdr.fit(case_queries, expert=expert,
|
37
45
|
animate_tree=draw_tree)
|
46
|
+
for case, target in zip(self.all_cases, self.targets):
|
47
|
+
cat = scrdr.classify(case)
|
48
|
+
self.assertEqual(cat, target)
|
38
49
|
return scrdr
|
File without changes
|
File without changes
|
{ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/__init__.py
RENAMED
File without changes
|
File without changes
|
{ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules/datastructures/enums.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
{ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/requires.txt
RENAMED
File without changes
|
{ripple_down_rules-0.0.0 → ripple_down_rules-0.0.2}/src/ripple_down_rules.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|