rulelearn-ripper-interactive 0.2.3__py3-none-any.whl
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.
- rulelearn_ri/__init__.py +6 -0
- rulelearn_ri/algorithms/__init__.py +0 -0
- rulelearn_ri/algorithms/r2n/__init__.py +0 -0
- rulelearn_ri/algorithms/r2n/base.py +28 -0
- rulelearn_ri/algorithms/r2n/functions.py +25 -0
- rulelearn_ri/algorithms/r2n/mask.py +58 -0
- rulelearn_ri/algorithms/r2n/preprocessing_layers.py +192 -0
- rulelearn_ri/algorithms/r2n/r2n_algo.py +287 -0
- rulelearn_ri/algorithms/r2n/simple_rulenet.py +75 -0
- rulelearn_ri/algorithms/r2n/training.py +34 -0
- rulelearn_ri/algorithms/r2n/utilities.py +117 -0
- rulelearn_ri/algorithms/rbm/__init__.py +0 -0
- rulelearn_ri/algorithms/rbm/boolean_rule_cg.py +65 -0
- rulelearn_ri/algorithms/rbm/rbm/BRCG.py +69 -0
- rulelearn_ri/algorithms/rbm/rbm/GLRM.py +98 -0
- rulelearn_ri/algorithms/rbm/rbm/__init__.py +6 -0
- rulelearn_ri/algorithms/rbm/rbm/beam_search.py +302 -0
- rulelearn_ri/algorithms/rbm/rbm/boolean_rule_cg.py +245 -0
- rulelearn_ri/algorithms/rbm/rbm/features.py +544 -0
- rulelearn_ri/algorithms/rbm/rbm/linear_regression.py +393 -0
- rulelearn_ri/algorithms/rbm/rbm/logistic_regression.py +447 -0
- rulelearn_ri/algorithms/rbm/utils.py +13 -0
- rulelearn_ri/algorithms/ripper/__init__.py +10 -0
- rulelearn_ri/algorithms/ripper/base.py +81 -0
- rulelearn_ri/algorithms/ripper/binding.py +541 -0
- rulelearn_ri/algorithms/ripper/mdl.py +97 -0
- rulelearn_ri/algorithms/ripper/pruning.py +212 -0
- rulelearn_ri/algorithms/ripper/ripper.py +600 -0
- rulelearn_ri/algorithms/ripper/ripper_ruleset_generator.py +26 -0
- rulelearn_ri/algorithms/ripper/single_rule.py +425 -0
- rulelearn_ri/dise.py +64 -0
- rulelearn_ri/trxf/__init__.py +0 -0
- rulelearn_ri/trxf/classifier/__init__.py +0 -0
- rulelearn_ri/trxf/classifier/ruleset_classifier.py +153 -0
- rulelearn_ri/trxf/core/__init__.py +5 -0
- rulelearn_ri/trxf/core/boolean_evaluator.py +15 -0
- rulelearn_ri/trxf/core/conjunction.py +50 -0
- rulelearn_ri/trxf/core/dnf_ruleset.py +64 -0
- rulelearn_ri/trxf/core/feature.py +155 -0
- rulelearn_ri/trxf/core/predicate.py +98 -0
- rulelearn_ri/trxf/core/ruleset_generator.py +25 -0
- rulelearn_ri/trxf/core/utils.py +40 -0
- rulelearn_ri/trxf/metrics.py +98 -0
- rulelearn_ri/trxf/pmml_export/__init__.py +4 -0
- rulelearn_ri/trxf/pmml_export/__version__.py +1 -0
- rulelearn_ri/trxf/pmml_export/models/__init__.py +24 -0
- rulelearn_ri/trxf/pmml_export/models/attribute.py +24 -0
- rulelearn_ri/trxf/pmml_export/models/characteristics.py +20 -0
- rulelearn_ri/trxf/pmml_export/models/data_dictionary.py +53 -0
- rulelearn_ri/trxf/pmml_export/models/mining_schema.py +25 -0
- rulelearn_ri/trxf/pmml_export/models/pmml_ruleset_model.py +16 -0
- rulelearn_ri/trxf/pmml_export/models/predicate.py +42 -0
- rulelearn_ri/trxf/pmml_export/models/rule.py +26 -0
- rulelearn_ri/trxf/pmml_export/models/ruleset.py +28 -0
- rulelearn_ri/trxf/pmml_export/models/ruleset_model.py +16 -0
- rulelearn_ri/trxf/pmml_export/models/scorecard.py +35 -0
- rulelearn_ri/trxf/pmml_export/pmml_exporter.py +18 -0
- rulelearn_ri/trxf/pmml_export/reader/__init__.py +2 -0
- rulelearn_ri/trxf/pmml_export/reader/abstract_reader.py +16 -0
- rulelearn_ri/trxf/pmml_export/reader/trxf_reader.py +113 -0
- rulelearn_ri/trxf/pmml_export/serializer/__init__.py +2 -0
- rulelearn_ri/trxf/pmml_export/serializer/abstract_serializer.py +11 -0
- rulelearn_ri/trxf/pmml_export/serializer/nyoka_serializer.py +162 -0
- rulelearn_ri/trxf/scorecard/__init__.py +3 -0
- rulelearn_ri/trxf/scorecard/bins.py +253 -0
- rulelearn_ri/trxf/scorecard/partition.py +71 -0
- rulelearn_ri/trxf/scorecard/scorecard.py +78 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/METADATA +164 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/RECORD +73 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/WHEEL +5 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/licenses/LICENSE +201 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/licenses/NOTICE +11 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
try:
|
|
2
|
+
# python >= 3.7
|
|
3
|
+
from dataclasses import dataclass as dataclass
|
|
4
|
+
from dataclasses import field as field
|
|
5
|
+
except ImportError:
|
|
6
|
+
from attr import s as dataclass
|
|
7
|
+
from attr import ib as field
|
|
8
|
+
import enum
|
|
9
|
+
import typing
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MiningFieldUsageType(enum.Enum):
|
|
13
|
+
active = 0
|
|
14
|
+
target = 1
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class MiningField:
|
|
19
|
+
name: str = field()
|
|
20
|
+
usageType: MiningFieldUsageType = field(default=MiningFieldUsageType.active)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class MiningSchema:
|
|
25
|
+
miningFields: typing.List[MiningField] = field()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
try:
|
|
2
|
+
# python >= 3.7
|
|
3
|
+
from dataclasses import dataclass as dataclass
|
|
4
|
+
from dataclasses import field as field
|
|
5
|
+
except ImportError:
|
|
6
|
+
from attr import s as dataclass
|
|
7
|
+
from attr import ib as field
|
|
8
|
+
|
|
9
|
+
from .data_dictionary import DataDictionary
|
|
10
|
+
from .ruleset_model import RuleSetModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class SimplePMMLRuleSetModel:
|
|
15
|
+
dataDictionary: DataDictionary = field()
|
|
16
|
+
ruleSetModel: RuleSetModel = field()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
try:
|
|
2
|
+
# python >= 3.7
|
|
3
|
+
from dataclasses import dataclass as dataclass
|
|
4
|
+
from dataclasses import field as field
|
|
5
|
+
except ImportError:
|
|
6
|
+
from attr import s as dataclass
|
|
7
|
+
from attr import ib as field
|
|
8
|
+
import enum
|
|
9
|
+
import typing
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Operator(enum.Enum):
|
|
13
|
+
equal = 0
|
|
14
|
+
notEqual = 1
|
|
15
|
+
lessThan = 2
|
|
16
|
+
lessOrEqual = 3
|
|
17
|
+
greaterThan = 4
|
|
18
|
+
greaterOrEqual = 5
|
|
19
|
+
isMissing = 6
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class SimplePredicate:
|
|
24
|
+
operator: Operator = field()
|
|
25
|
+
field: str = field()
|
|
26
|
+
value: typing.Optional[str] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class TruePredicate:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Use functional api to add aliases for `or` and `and`
|
|
35
|
+
BooleanOperator = enum.Enum('BooleanOperator',
|
|
36
|
+
[('or', 0), ('or_', 0), ('and', 1), ('and_', 1), ('xor', 2), ('surrogate', 3)])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class CompoundPredicate:
|
|
41
|
+
simplePredicates: typing.List[SimplePredicate] = field()
|
|
42
|
+
booleanOperator: BooleanOperator = field()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
try:
|
|
2
|
+
# python >= 3.7
|
|
3
|
+
from dataclasses import dataclass as dataclass
|
|
4
|
+
from dataclasses import field as field
|
|
5
|
+
except ImportError:
|
|
6
|
+
from attr import s as dataclass
|
|
7
|
+
from attr import ib as field
|
|
8
|
+
import typing
|
|
9
|
+
|
|
10
|
+
from .predicate import CompoundPredicate
|
|
11
|
+
from .predicate import SimplePredicate
|
|
12
|
+
|
|
13
|
+
DEFAULT_WEIGHT = 1.0
|
|
14
|
+
DEFAULT_CONFIDENCE = 1.0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class SimpleRule:
|
|
19
|
+
predicate: typing.Union[SimplePredicate, CompoundPredicate] = field()
|
|
20
|
+
score: str = field()
|
|
21
|
+
|
|
22
|
+
id: typing.Optional[str] = field(default=None)
|
|
23
|
+
recordCount: typing.Optional[int] = field(default=None)
|
|
24
|
+
nbCorrect: typing.Optional[int] = field(default=None)
|
|
25
|
+
confidence: typing.Optional[float] = field(default=DEFAULT_CONFIDENCE)
|
|
26
|
+
weight: typing.Optional[float] = field(default=DEFAULT_WEIGHT)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
try:
|
|
2
|
+
# python >= 3.7
|
|
3
|
+
from dataclasses import dataclass as dataclass
|
|
4
|
+
from dataclasses import field as field
|
|
5
|
+
except ImportError:
|
|
6
|
+
from attr import s as dataclass
|
|
7
|
+
from attr import ib as field
|
|
8
|
+
import enum
|
|
9
|
+
import typing
|
|
10
|
+
|
|
11
|
+
from .rule import SimpleRule
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RuleSelectionMethod(enum.Enum):
|
|
15
|
+
firstHit = 0
|
|
16
|
+
weightedMax = 1
|
|
17
|
+
weightedSum = 2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class RuleSet:
|
|
22
|
+
ruleSelectionMethod: typing.List[RuleSelectionMethod] = field()
|
|
23
|
+
rules: typing.List[SimpleRule] = field()
|
|
24
|
+
|
|
25
|
+
recordCount: typing.Optional[int] = field(default=None)
|
|
26
|
+
nbCorrect: typing.Optional[int] = field(default=None)
|
|
27
|
+
defaultScore: typing.Optional[str] = field(default=None)
|
|
28
|
+
defaultConfidence: typing.Optional[float] = field(default=None)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
try:
|
|
2
|
+
# python >= 3.7
|
|
3
|
+
from dataclasses import dataclass as dataclass
|
|
4
|
+
from dataclasses import field as field
|
|
5
|
+
except ImportError:
|
|
6
|
+
from attr import s as dataclass
|
|
7
|
+
from attr import ib as field
|
|
8
|
+
|
|
9
|
+
from .mining_schema import MiningSchema
|
|
10
|
+
from .ruleset import RuleSet
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class RuleSetModel:
|
|
15
|
+
miningSchema: MiningSchema = field()
|
|
16
|
+
ruleSet: RuleSet = field()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from . import characteristics
|
|
4
|
+
from . import data_dictionary
|
|
5
|
+
from . import mining_schema
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
# python >= 3.7
|
|
9
|
+
from dataclasses import dataclass as dataclass
|
|
10
|
+
from dataclasses import field as field
|
|
11
|
+
except ImportError:
|
|
12
|
+
from attr import s as dataclass
|
|
13
|
+
from attr import ib as field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class OutputField:
|
|
18
|
+
name: str
|
|
19
|
+
feature: str
|
|
20
|
+
dataType: data_dictionary.DataType
|
|
21
|
+
optype: data_dictionary.OpType
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class Output:
|
|
26
|
+
outputFields: typing.List[OutputField]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class Scorecard:
|
|
31
|
+
dataDictionary: data_dictionary.DataDictionary
|
|
32
|
+
miningSchema: mining_schema.MiningSchema
|
|
33
|
+
output: Output
|
|
34
|
+
characteristics: characteristics.Characteristics
|
|
35
|
+
initialScore: str = field(default="0")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from rulelearn_ri.trxf.classifier.ruleset_classifier import RuleSetClassifier
|
|
2
|
+
from rulelearn_ri.trxf.pmml_export import AbstractSerializer, AbstractReader
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PmmlExporter:
|
|
6
|
+
def __init__(self, reader: AbstractReader, serializer: AbstractSerializer):
|
|
7
|
+
self._serializer = serializer
|
|
8
|
+
self._reader = reader
|
|
9
|
+
|
|
10
|
+
def export(self, trxf_classifier: RuleSetClassifier):
|
|
11
|
+
"""
|
|
12
|
+
Translate a given TRXF RuleSetClassifier to a PMML string
|
|
13
|
+
@param trxf_classifier: A TRXF RuleSetClassifier
|
|
14
|
+
@return: The corresponding PMML string
|
|
15
|
+
"""
|
|
16
|
+
if self._reader.data_dictionary is None:
|
|
17
|
+
raise AssertionError("Missing data dictionary in reader object")
|
|
18
|
+
return self._serializer.serialize(self._reader.read(trxf_classifier))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from .. import models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AbstractReader(abc.ABC):
|
|
8
|
+
T = typing.TypeVar('T')
|
|
9
|
+
|
|
10
|
+
@property
|
|
11
|
+
def data_dictionary(self):
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def read(self, model: T) -> models.SimplePMMLRuleSetModel:
|
|
16
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from pandas.api.types import is_bool_dtype, is_float_dtype, is_integer_dtype
|
|
5
|
+
|
|
6
|
+
from rulelearn_ri.trxf.classifier import ruleset_classifier
|
|
7
|
+
from rulelearn_ri.trxf.classifier.ruleset_classifier import RuleSetClassifier
|
|
8
|
+
from rulelearn_ri.trxf.core import Conjunction, Relation
|
|
9
|
+
from rulelearn_ri.trxf.pmml_export import models
|
|
10
|
+
from rulelearn_ri.trxf.pmml_export.models.data_dictionary import Value
|
|
11
|
+
from rulelearn_ri.trxf.pmml_export.reader import AbstractReader
|
|
12
|
+
from rulelearn_ri.trxf.pmml_export.models import SimplePredicate, Operator, CompoundPredicate, \
|
|
13
|
+
BooleanOperator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TrxfReader(AbstractReader):
|
|
17
|
+
def __init__(self, data_dictionary=None):
|
|
18
|
+
self._data_dictionary = data_dictionary
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def data_dictionary(self):
|
|
22
|
+
return self._data_dictionary
|
|
23
|
+
|
|
24
|
+
def read(self, trxf_classifier: RuleSetClassifier) -> models.SimplePMMLRuleSetModel:
|
|
25
|
+
"""
|
|
26
|
+
Translate a TRXF RuleSetClassifier to an internal SimplePMMLRuleSetModel
|
|
27
|
+
"""
|
|
28
|
+
trxf_to_pmml_rule_selection_map = {
|
|
29
|
+
ruleset_classifier.RuleSelectionMethod.FIRST_HIT: models.RuleSelectionMethod.firstHit,
|
|
30
|
+
ruleset_classifier.RuleSelectionMethod.WEIGHTED_SUM: models.RuleSelectionMethod.weightedSum,
|
|
31
|
+
ruleset_classifier.RuleSelectionMethod.WEIGHTED_MAX: models.RuleSelectionMethod.weightedMax
|
|
32
|
+
}
|
|
33
|
+
trxf_rules = trxf_classifier.rules
|
|
34
|
+
mining_schema = _extract_mining_schema(trxf_rules)
|
|
35
|
+
simple_rules = _convert_to_simple_rules(trxf_rules)
|
|
36
|
+
ruleset = models.RuleSet(
|
|
37
|
+
ruleSelectionMethod=[trxf_to_pmml_rule_selection_map[trxf_classifier.rule_selection_method]],
|
|
38
|
+
rules=simple_rules,
|
|
39
|
+
defaultScore=str(trxf_classifier.default_label))
|
|
40
|
+
rule_set_model = models.RuleSetModel(miningSchema=mining_schema, ruleSet=ruleset)
|
|
41
|
+
assert self._data_dictionary is not None
|
|
42
|
+
return models.SimplePMMLRuleSetModel(dataDictionary=self._data_dictionary, ruleSetModel=rule_set_model)
|
|
43
|
+
|
|
44
|
+
def load_data_dictionary(self, X: pd.DataFrame, values: Dict = None):
|
|
45
|
+
"""
|
|
46
|
+
Extract the data dictionary from a feature dataframe, and store it
|
|
47
|
+
|
|
48
|
+
@param X: Input dataframe
|
|
49
|
+
@param values: A dict mapping column name to a list of possible categorical values. It will be inferred from X if not provided.
|
|
50
|
+
"""
|
|
51
|
+
dtypes = X.dtypes
|
|
52
|
+
data_fields = []
|
|
53
|
+
for index, value in dtypes.items():
|
|
54
|
+
vals = None
|
|
55
|
+
# pandas dtype predicates also handle extension dtypes (e.g. the pandas 3 default StringDtype),
|
|
56
|
+
# which np.issubdtype cannot interpret
|
|
57
|
+
if is_bool_dtype(value):
|
|
58
|
+
data_type = models.DataType.boolean
|
|
59
|
+
op_type = models.OpType.categorical
|
|
60
|
+
elif is_integer_dtype(value):
|
|
61
|
+
data_type = models.DataType.integer
|
|
62
|
+
op_type = models.OpType.ordinal
|
|
63
|
+
elif is_float_dtype(value):
|
|
64
|
+
data_type = models.DataType.double if value.itemsize == 8 else models.DataType.float
|
|
65
|
+
op_type = models.OpType.continuous
|
|
66
|
+
else:
|
|
67
|
+
data_type = models.DataType.string
|
|
68
|
+
op_type = models.OpType.categorical
|
|
69
|
+
vals = values[index] if values is not None and index in values else list(X[index].unique())
|
|
70
|
+
wrapped_vals = list(map(lambda v: Value(v), vals)) if vals is not None else vals
|
|
71
|
+
data_fields.append(
|
|
72
|
+
models.DataField(name=str(index), optype=op_type, dataType=data_type, values=wrapped_vals))
|
|
73
|
+
self._data_dictionary = models.DataDictionary(data_fields)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _convert_to_simple_rules(trxf_rules):
|
|
77
|
+
simple_rules = []
|
|
78
|
+
for rule in trxf_rules:
|
|
79
|
+
predicate = _convert_to_pmml_predicate(rule.conjunction)
|
|
80
|
+
confidence = rule.confidence if rule.confidence is not None else models.DEFAULT_CONFIDENCE
|
|
81
|
+
weight = rule.weight if rule.weight is not None else models.DEFAULT_WEIGHT
|
|
82
|
+
simple_rule = models.SimpleRule(predicate=predicate, score=str(rule.label), id=str(rule.conjunction),
|
|
83
|
+
recordCount=rule.record_count,
|
|
84
|
+
nbCorrect=rule.nb_correct, confidence=confidence, weight=weight)
|
|
85
|
+
simple_rules.append(simple_rule)
|
|
86
|
+
return simple_rules
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _convert_to_pmml_predicate(trxf_conjunction: Conjunction):
|
|
90
|
+
trxf_to_pmml_op = {
|
|
91
|
+
Relation.EQ: Operator.equal,
|
|
92
|
+
Relation.NEQ: Operator.notEqual,
|
|
93
|
+
Relation.LT: Operator.lessThan,
|
|
94
|
+
Relation.LE: Operator.lessOrEqual,
|
|
95
|
+
Relation.GT: Operator.greaterThan,
|
|
96
|
+
Relation.GE: Operator.greaterOrEqual
|
|
97
|
+
}
|
|
98
|
+
simple_predicates = [SimplePredicate(operator=trxf_to_pmml_op[trxf_predicate.relation],
|
|
99
|
+
value=str(trxf_predicate.value),
|
|
100
|
+
field=str(trxf_predicate.feature.variable_names[0]))
|
|
101
|
+
for trxf_predicate in trxf_conjunction.predicates]
|
|
102
|
+
return CompoundPredicate(simplePredicates=simple_predicates, booleanOperator=BooleanOperator.and_)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _extract_mining_schema(trxf_rules):
|
|
106
|
+
mining_fields = {}
|
|
107
|
+
for rule in trxf_rules:
|
|
108
|
+
for predicate in rule.conjunction.predicates:
|
|
109
|
+
name = predicate.feature.variable_names[0]
|
|
110
|
+
if name not in mining_fields:
|
|
111
|
+
mining_field = models.MiningField(name=name)
|
|
112
|
+
mining_fields[name] = mining_field
|
|
113
|
+
return models.MiningSchema(miningFields=list(mining_fields.values()))
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import datetime
|
|
3
|
+
import io
|
|
4
|
+
|
|
5
|
+
import nyoka.PMML44 as nyoka_pmml # noqa
|
|
6
|
+
import nyoka.base.constants as nyoka_constants
|
|
7
|
+
|
|
8
|
+
from .abstract_serializer import AbstractSerializer
|
|
9
|
+
from .. import __version__ as version
|
|
10
|
+
from .. import models as models
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NyokaSerializer(AbstractSerializer):
|
|
14
|
+
APPLICATION_NAME = 'SimpleRuleSetExport'
|
|
15
|
+
COPYRIGHT_STRING = 'Copyright IBM Corp'
|
|
16
|
+
|
|
17
|
+
def __init__(self, timestamp: datetime = None):
|
|
18
|
+
self._timestamp = timestamp
|
|
19
|
+
|
|
20
|
+
def serialize(self, model: typing.Union[models.SimplePMMLRuleSetModel, models.Scorecard]) -> str:
|
|
21
|
+
if isinstance(model, models.SimplePMMLRuleSetModel):
|
|
22
|
+
pmml_model = self._nyoka_pmml_model(
|
|
23
|
+
model,
|
|
24
|
+
RuleSetModel=None if model.ruleSetModel is None else [self._nyoka_rule_set_model(model.ruleSetModel)])
|
|
25
|
+
elif isinstance(model, models.Scorecard):
|
|
26
|
+
pmml_model = self._nyoka_pmml_model(
|
|
27
|
+
model,
|
|
28
|
+
Scorecard=None if model is None else [self._nyoka_scorecard_model(model)])
|
|
29
|
+
else:
|
|
30
|
+
raise NotImplemented
|
|
31
|
+
string_io = io.StringIO()
|
|
32
|
+
pmml_model.export(outfile=string_io, level=0)
|
|
33
|
+
return string_io.getvalue()
|
|
34
|
+
|
|
35
|
+
def _nyoka_pmml_model(
|
|
36
|
+
self,
|
|
37
|
+
model: typing.Union[models.SimplePMMLRuleSetModel, models.Scorecard],
|
|
38
|
+
**build_args
|
|
39
|
+
) -> nyoka_pmml.PMML:
|
|
40
|
+
return nyoka_pmml.PMML(
|
|
41
|
+
version=nyoka_constants.PMML_SCHEMA.VERSION,
|
|
42
|
+
Header=nyoka_pmml.Header(
|
|
43
|
+
copyright=NyokaSerializer.COPYRIGHT_STRING,
|
|
44
|
+
description=nyoka_constants.HEADER_INFO.DEFAULT_DESCRIPTION,
|
|
45
|
+
Timestamp=nyoka_pmml.Timestamp(datetime.datetime.now() if self._timestamp is None else self._timestamp),
|
|
46
|
+
Application=nyoka_pmml.Application(name=NyokaSerializer.APPLICATION_NAME, version=version.version)),
|
|
47
|
+
DataDictionary=None if model.dataDictionary is None else self._nyoka_data_dictionary(model.dataDictionary),
|
|
48
|
+
**build_args)
|
|
49
|
+
|
|
50
|
+
def _nyoka_data_dictionary(self, data_dictionary: models.DataDictionary) -> nyoka_pmml.DataDictionary:
|
|
51
|
+
return nyoka_pmml.DataDictionary(
|
|
52
|
+
numberOfFields=0 if not data_dictionary.dataFields else len(data_dictionary.dataFields),
|
|
53
|
+
DataField=None if not data_dictionary.dataFields else [
|
|
54
|
+
self._nyoka_data_field(f) for f in data_dictionary.dataFields])
|
|
55
|
+
|
|
56
|
+
def _nyoka_data_field(self, data_field: models.DataField) -> nyoka_pmml.DataField:
|
|
57
|
+
return nyoka_pmml.DataField(
|
|
58
|
+
name=data_field.name,
|
|
59
|
+
optype=data_field.optype.name,
|
|
60
|
+
dataType=data_field.dataType.name,
|
|
61
|
+
Value=None if not data_field.values else [
|
|
62
|
+
nyoka_pmml.Value(value=val.value, property=val.property.name) for val in data_field.values])
|
|
63
|
+
|
|
64
|
+
def _nyoka_rule_set_model(self, rule_set_model: models.RuleSetModel) -> nyoka_pmml.RuleSetModel:
|
|
65
|
+
return nyoka_pmml.RuleSetModel(
|
|
66
|
+
functionName='classification',
|
|
67
|
+
algorithmName='RuleSet',
|
|
68
|
+
MiningSchema=None if rule_set_model.miningSchema is None else self._nyoka_mining_schema(
|
|
69
|
+
rule_set_model.miningSchema),
|
|
70
|
+
RuleSet=None if rule_set_model.ruleSet is None else self._nyoka_rule_set(rule_set_model.ruleSet))
|
|
71
|
+
|
|
72
|
+
def _nyoka_mining_schema(self, mining_schema: models.MiningSchema) -> nyoka_pmml.MiningSchema:
|
|
73
|
+
return nyoka_pmml.MiningSchema(
|
|
74
|
+
MiningField=[
|
|
75
|
+
nyoka_pmml.MiningField(name=f.name, usageType=f.usageType.name)
|
|
76
|
+
for f in mining_schema.miningFields])
|
|
77
|
+
|
|
78
|
+
def _nyoka_rule_set(self, rule_set: models.RuleSet) -> nyoka_pmml.RuleSet:
|
|
79
|
+
return nyoka_pmml.RuleSet(
|
|
80
|
+
RuleSelectionMethod=None if rule_set.ruleSelectionMethod is None else [
|
|
81
|
+
nyoka_pmml.RuleSelectionMethod(criterion=m.name)
|
|
82
|
+
for m in rule_set.ruleSelectionMethod],
|
|
83
|
+
SimpleRule=None if rule_set.rules is None else [self._nyoka_rule(r) for r in rule_set.rules],
|
|
84
|
+
recordCount=rule_set.recordCount,
|
|
85
|
+
nbCorrect=rule_set.nbCorrect,
|
|
86
|
+
defaultScore=rule_set.defaultScore,
|
|
87
|
+
defaultConfidence=rule_set.defaultConfidence)
|
|
88
|
+
|
|
89
|
+
def _nyoka_rule(self, simple_rule: models.SimpleRule) -> nyoka_pmml.SimpleRule:
|
|
90
|
+
return nyoka_pmml.SimpleRule(
|
|
91
|
+
SimplePredicate=None if (
|
|
92
|
+
simple_rule.predicate is None or not isinstance(simple_rule.predicate, models.SimplePredicate)) else
|
|
93
|
+
nyoka_pmml.SimplePredicate(
|
|
94
|
+
field=simple_rule.predicate.field,
|
|
95
|
+
operator=simple_rule.predicate.operator.name,
|
|
96
|
+
value=simple_rule.predicate.value),
|
|
97
|
+
CompoundPredicate=None if (
|
|
98
|
+
simple_rule.predicate is None or not isinstance(simple_rule.predicate,
|
|
99
|
+
models.CompoundPredicate)) else
|
|
100
|
+
nyoka_pmml.CompoundPredicate(
|
|
101
|
+
booleanOperator=simple_rule.predicate.booleanOperator.name,
|
|
102
|
+
SimplePredicate=[
|
|
103
|
+
nyoka_pmml.SimplePredicate(field=sp.field, operator=sp.operator.name, value=sp.value)
|
|
104
|
+
for sp in simple_rule.predicate.simplePredicates]),
|
|
105
|
+
score=simple_rule.score,
|
|
106
|
+
id=simple_rule.id,
|
|
107
|
+
recordCount=simple_rule.recordCount,
|
|
108
|
+
nbCorrect=simple_rule.nbCorrect,
|
|
109
|
+
confidence=simple_rule.confidence,
|
|
110
|
+
weight=simple_rule.weight)
|
|
111
|
+
|
|
112
|
+
def _nyoka_scorecard_model(self, scorecard: models.Scorecard) -> nyoka_pmml.Scorecard:
|
|
113
|
+
return nyoka_pmml.Scorecard(
|
|
114
|
+
functionName='regression',
|
|
115
|
+
algorithmName='ScoreCard',
|
|
116
|
+
MiningSchema=None if scorecard.miningSchema is None else self._nyoka_mining_schema(
|
|
117
|
+
scorecard.miningSchema),
|
|
118
|
+
initialScore=scorecard.initialScore,
|
|
119
|
+
useReasonCodes="false",
|
|
120
|
+
Output=None if scorecard.output is None else nyoka_pmml.Output(
|
|
121
|
+
OutputField=[
|
|
122
|
+
nyoka_pmml.OutputField(
|
|
123
|
+
name=outputField.name,
|
|
124
|
+
feature=outputField.feature,
|
|
125
|
+
dataType=outputField.dataType.name,
|
|
126
|
+
optype=outputField.optype.name) for outputField in scorecard.output.outputFields]),
|
|
127
|
+
Characteristics=None if scorecard.characteristics is None else self._nyoka_pmml_characteristics(
|
|
128
|
+
scorecard.characteristics))
|
|
129
|
+
|
|
130
|
+
def _nyoka_pmml_characteristics(self, characteristics: models.Characteristics) -> nyoka_pmml.Characteristics:
|
|
131
|
+
return nyoka_pmml.Characteristics(
|
|
132
|
+
Characteristic=[
|
|
133
|
+
nyoka_pmml.Characteristic(
|
|
134
|
+
name=characteristic.name,
|
|
135
|
+
Attribute=[self._nyoka_pmml_attributes(attribute) for attribute in characteristic.attributes])
|
|
136
|
+
for characteristic in characteristics.characteristics])
|
|
137
|
+
|
|
138
|
+
def _nyoka_pmml_attributes(self, attribute: models.Attribute) -> nyoka_pmml.Attribute:
|
|
139
|
+
return nyoka_pmml.Attribute(
|
|
140
|
+
partialScore=attribute.score if not isinstance(attribute.score, models.ComplexPartialScore) else None,
|
|
141
|
+
ComplexPartialScore=nyoka_pmml.ComplexPartialScore(
|
|
142
|
+
Apply=nyoka_pmml.Apply(
|
|
143
|
+
function='+',
|
|
144
|
+
Apply_member=[nyoka_pmml.Apply(
|
|
145
|
+
function='*',
|
|
146
|
+
FieldRef=[nyoka_pmml.FieldRef(field=attribute.score.feature_name)],
|
|
147
|
+
Constant=[nyoka_pmml.Constant(valueOf_=attribute.score.multiplier)])],
|
|
148
|
+
Constant=[nyoka_pmml.Constant(valueOf_=attribute.score.constant)])) if isinstance(
|
|
149
|
+
attribute.score, models.ComplexPartialScore) else None,
|
|
150
|
+
SimplePredicate=None if (attribute.predicate is None or not isinstance(
|
|
151
|
+
attribute.predicate, models.SimplePredicate)) else nyoka_pmml.SimplePredicate(
|
|
152
|
+
field=attribute.predicate.field,
|
|
153
|
+
operator=attribute.predicate.operator.name,
|
|
154
|
+
value=attribute.predicate.value),
|
|
155
|
+
CompoundPredicate=None if (attribute.predicate is None or not isinstance(
|
|
156
|
+
attribute.predicate, models.CompoundPredicate)) else nyoka_pmml.CompoundPredicate(
|
|
157
|
+
booleanOperator=attribute.predicate.booleanOperator.name,
|
|
158
|
+
SimplePredicate=[
|
|
159
|
+
nyoka_pmml.SimplePredicate(field=sp.field, operator=sp.operator.name, value=sp.value)
|
|
160
|
+
for sp in attribute.predicate.simplePredicates]),
|
|
161
|
+
True_=None if (attribute.predicate is None or not isinstance(
|
|
162
|
+
attribute.predicate, models.TruePredicate)) else nyoka_pmml.True_())
|