Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions rulekit/_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Contains helper functions and classes
"""
"""Contains helper functions and classes"""

import io
import json
from typing import Any
Expand All @@ -24,6 +24,20 @@
from rulekit.rules import BaseRule


def _to_java_object_array(values: np.ndarray) -> np.ndarray:
"""Copy an array to dtype=object with missing cells as None.

Args:
values: Array produced from a pandas frame or series.

Returns:
Object array safe to pass through JPype into DataTable.
"""
result = values.astype(object, copy=True)
result[pd.isna(result)] = None
return result


def get_rule_generator(expert: bool = False) -> Any:
"""Factory for Java RuleGenerator class object

Expand Down Expand Up @@ -158,7 +172,7 @@ def are_params_equal(
java_value = java_params.get(key)
python_value = python_params.get(key)
skip_check: bool = isinstance(python_value, Callable)
if java_value == 'None':
if java_value == "None":
java_value = None
if java_value is None and python_value is None:
continue
Expand Down Expand Up @@ -259,7 +273,7 @@ def _sanitize_y(self, y: Union[pd.Series, np.ndarray, list]):
elif isinstance(y, pd.Series):
self._label_name = y.name
self._attributes_names.append(self._label_name)
self._y = y.to_numpy()
self._y = _to_java_object_array(y.to_numpy())
elif isinstance(y, list):
self._label_name = self.DEFAULT_LABEL_ATTRIBUTE_NAME
self._attributes_names.append(self._label_name)
Expand All @@ -281,9 +295,7 @@ def _sanitize_X(
) -> tuple[np.ndarray, np.ndarray]:
if isinstance(X, pd.DataFrame):
self._attributes_names = X.columns.tolist()
# replace nan values with None
X = X.where(pd.notnull(X), None)
self._X = X.to_numpy()
self._X = _to_java_object_array(X.astype(object).to_numpy())
elif isinstance(X, np.ndarray):
self._attributes_names = [
f"{self.AUTOMATIC_ATTRIBUTES_NAMES_PREFIX}{index + 1}"
Expand Down Expand Up @@ -327,8 +339,8 @@ def _create_example_set(self) -> JObject:
table = DataTable(*args)
if self._y is not None:
ExampleSetFactory = JClass(
'adaa.analytics.rules.logic.representation.'
'exampleset.ExampleSetFactory'
"adaa.analytics.rules.logic.representation."
"exampleset.ExampleSetFactory"
)
factory = ExampleSetFactory(2)
example_set = factory.create(table)
Expand Down
22 changes: 16 additions & 6 deletions rulekit/_operator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Contains base classes for rule induction operators
"""
"""Contains base classes for rule induction operators"""

from __future__ import annotations

from abc import ABC
Expand Down Expand Up @@ -79,9 +79,7 @@ def fit( # pylint: disable=missing-function-docstring
self.model = RuleSet[BaseRule](java_model)
return self.model

def predict(
self, values: Data
) -> np.ndarray: # pylint: disable=missing-function-docstring
def predict(self, values: Data) -> np.ndarray: # pylint: disable=missing-function-docstring
if self.model is None:
raise ValueError('"fit" method must be called before calling this method')
example_set = ExampleSetFactory(self._get_problem_type()).make(values)
Expand All @@ -90,7 +88,8 @@ def predict(
)

def get_params(
self, deep: bool = True # pylint: disable=unused-argument
self,
deep: bool = True, # pylint: disable=unused-argument
) -> dict[str, Any]:
"""
Parameters
Expand Down Expand Up @@ -146,6 +145,17 @@ def get_coverage_matrix(self, values: Data) -> np.ndarray:
if self.model is None:
raise ValueError('"fit" method must be called before calling this method')
example_set = ExampleSetFactory(self._get_problem_type()).make(values)

# update mapping for nominal attributes
header = self.model._java_object.getTrainingHeader()
if header is not None:
try:
example_set = example_set.updateMapping(header)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co robi ta funkcja? Rozumiem, że to jest po to, że gdy w zbiorze treningowym mam np. atrybut rozmiar: big. small, medium to pod spodem jest to mapowane na: big: 0, small: 1, medium: 2 i jak podam inny zbiór z wartościami small, big, medium to dostaną one pod spodem inny mapping i przez to zostaną zwrócone złe wyniki i ten updateMapping to dopasowuje do zbioru treningowego?

Pytanie czy w takim razie w predict też nie musimy tego dodać jeżeli damy inny zbiór do predykcji? Tam też tworzymy nowy zbiór danych jak tutaj:
example_set = ExampleSetFactory(self._get_problem_type()).make(values)
return self.model._java_object.apply( # pylint: disable=protected-access
example_set
)

chyba, że w javie w apply to już jest dopasowywane?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tak, dokładnie - updateMapping po zmianach Adama to taki odpowiednik transform z sklearn. W predict nie trzeba wywoływać wprost, bo apply w klasie PredictionModel pod spodem wywołuje updateMapping(getTrainingHeader())

except Exception as e:
raise ValueError(
"Error updating mapping. Check if the given set agrees with the training set."
) from e

covering_info = self.model.covering(example_set)
if isinstance(values, (pd.Series, pd.DataFrame)):
values = values.to_numpy()
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion rulekit/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from rulekit._logging import _RuleKitJavaLoggerConfig

__RULEKIT_RELEASE_VERSION__ = "2.1.24"
__RULEKIT_RELEASE_VERSION__ = "2.1.26"
__VERSION__ = f"{__RULEKIT_RELEASE_VERSION__}.1"


Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setuptools.setup(
name="rulekit",
version='2.1.24.2',
version='2.1.26.1',
author="Cezary Maszczyk",
author_email="cezary.maszczyk@gmail.com",
description="Comprehensive suite for rule-based learning",
Expand All @@ -34,7 +34,7 @@
python_requires='>=3.9',
install_requires=[
'numpy>=1.24',
'pandas>=1.5',
'pandas>=1.5,<=3.0.5',
'scipy>=1.11',
'scikit-learn>=1.1',
'JPype1==1.5.2',
Expand Down
8 changes: 2 additions & 6 deletions tests/test_survival.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,7 @@ def test_passing_survival_time_column_to_fit_method(self):
)
X, y = test_case.example_set.values, test_case.example_set.labels
survival_time_col: pd.Series = X[test_case.survival_time]
X_without_time_col: pd.DataFrame = X.drop(
columns=[test_case.survival_time], axis=1
)
X_without_time_col: pd.DataFrame = X.drop(columns=[test_case.survival_time])
surv1.fit(X_without_time_col, y, survival_time=survival_time_col)
surv2.fit(X, y)

Expand All @@ -154,9 +152,7 @@ def test_ibs_calculation(self):
)
X, y = test_case.example_set.values, test_case.example_set.labels
survival_time_col: pd.Series = X[test_case.survival_time]
X_without_time_col: pd.DataFrame = X.drop(
columns=[test_case.survival_time], axis=1
)
X_without_time_col: pd.DataFrame = X.drop(columns=[test_case.survival_time])
surv.fit(X, y)

ibs: float = surv.score(X, y)
Expand Down
Loading