Skip to content

Commit

Permalink
fix logger
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdboom committed Aug 28, 2023
1 parent 101cf8b commit d15e5e6
Show file tree
Hide file tree
Showing 42 changed files with 170 additions and 141 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ And lastly, analyze the results.

```python
atom.evaluate()

atom.plot_lift()
```

<br><br>
Expand Down
6 changes: 3 additions & 3 deletions atom/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ class ATOMClassifier(BaseTransformer, ATOM):
ATOM can't manage warnings that go from C/C++ code to stdout.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic name.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -498,7 +498,7 @@ class ATOMForecaster(BaseTransformer, ATOM):
ATOM can't manage warnings that go from C/C++ code to stdout.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic name.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -727,7 +727,7 @@ class ATOMRegressor(BaseTransformer, ATOM):
ATOM can't manage warnings that go from C/C++ code to stdout.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic name.
- Else: Python `logging.Logger` instance.
Expand Down
25 changes: 13 additions & 12 deletions atom/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import numpy as np
import pandas as pd
from scipy import stats
from sklearn.preprocessing import FunctionTransformer
from sklearn.utils.metaestimators import available_if
from sklearn.utils.validation import check_memory

Expand All @@ -45,12 +44,12 @@
TrainSizingRegressor,
)
from atom.utils import (
DATAFRAME, FEATURES, INT, PANDAS, SCALAR, SEQUENCE, SERIES, TARGET,
TS_INDEX_TYPES, ClassMap, DataConfig, Predictor, Runner, Transformer,
__version__, bk, check_dependency, check_is_fitted, check_scaling,
DATAFRAME, FEATURES, INT, MISSING_VALUES, PANDAS, SCALAR, SEQUENCE, SERIES,
TARGET, TS_INDEX_TYPES, ClassMap, DataConfig, Predictor, Runner,
Transformer, __version__, check_dependency, check_is_fitted, check_scaling,
composed, crash, custom_transform, fit_one, flt, get_cols,
get_custom_scorer, has_task, infer_task, is_multioutput, is_sparse, lst,
method_to_log, sign, variable_return, MISSING_VALUES
method_to_log, sign, variable_return,
)


Expand Down Expand Up @@ -846,7 +845,7 @@ def get_data(new_t: str) -> SERIES:
}

# Convert selected columns to the best nullable dtype
data = self.dataset[self.branch._get_columns(columns)]
data = self.dataset[self.branch._get_columns(columns)] # TODO: .convert_dtypes()

Check notice on line 848 in atom/atom.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

Accessing a protected member of a class or a module

Access to a protected member _get_columns of a class

for name, column in data.items():
if pd.api.types.is_sparse(column):
Expand Down Expand Up @@ -892,11 +891,11 @@ def get_data(new_t: str) -> SERIES:
from pandas.core.dtypes.cast import convert_dtypes
print(self.dtypes)
self.branch.dataset = self.branch.dataset.astype(
{
name: convert_dtypes(column, dtype_backend="pyarrow")
for name, column in data.items()
}
)
{
name: convert_dtypes(column, dtype_backend="pyarrow")
for name, column in data.items()
}
)

self.log("The column dtypes are successfully converted.", 1)

Expand Down Expand Up @@ -1264,8 +1263,10 @@ def apply(
Additional keyword arguments for the inverse function.
"""
estimator = self._get_est_class("FunctionTransformer", "preprocessing")

columns = kwargs.pop("columns", None)
function_transformer = FunctionTransformer(
function_transformer = estimator(
func=func,
inverse_func=inverse_func,
kw_args=kw_args,
Expand Down
2 changes: 1 addition & 1 deletion atom/basemodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class BaseModel(BaseTransformer, BaseTracker, HTPlot, PredictionPlot, ShapPlot):
ATOM can't manage warnings that go from C/C++ code to stdout.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic name.
- Else: Python `logging.Logger` instance.
Expand Down
14 changes: 9 additions & 5 deletions atom/baserunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,28 +180,32 @@ def metric(self) -> str | list[str] | None:
return flt(self._metric.keys())

@property
def winners(self) -> list[Model]:
def winners(self) -> list[Model] | None:
"""Models ordered by performance.
Performance is measured as the highest score on the model's
[`score_bootstrap`][adaboost-score_bootstrap] or
[`score_test`][adaboost-score_test] attributes, checked in
that order. For [multi-metric runs][], only the main metric
is compared.
is compared. Ties are resolved looking at the lowest
[time_fit][adaboost-time_fit].
"""
if self._models: # Returns None if not fitted
return sorted(self._models, key=lambda x: get_best_score(x), reverse=True)
return sorted(
self._models, key=lambda x: (get_best_score(x), x.time_fit), reverse=True
)

@property
def winner(self) -> Model:
def winner(self) -> Model | None:
"""Best performing model.
Performance is measured as the highest score on the model's
[`score_bootstrap`][adaboost-score_bootstrap] or
[`score_test`][adaboost-score_test] attributes, checked in
that order. For [multi-metric runs][], only the main metric
is compared.
is compared. Ties are resolved looking at the lowest
[time_fit][adaboost-time_fit].
"""
if self._models: # Returns None if not fitted
Expand Down
62 changes: 35 additions & 27 deletions atom/basetransformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from importlib import import_module
from importlib.util import find_spec
from logging import (
DEBUG, FileHandler, Formatter, Logger, NullHandler, getLogger,
DEBUG, FileHandler, Formatter, Logger, getLogger,
)
from multiprocessing import cpu_count
from typing import Any, Callable
Expand Down Expand Up @@ -162,8 +162,11 @@ def engine(self, value: dict | None):
if not find_spec("cuml"):
raise ModuleNotFoundError(
"Failed to import cuml. Package is not installed. Refer "
"to: https://rapids.ai/start.html#rapids-release-selector."
"to: https://rapids.ai/start.html#install."
)
else:
import cuml
cuml.internals.memory_utils.set_global_output_type("numpy")
elif models.lower() != "sklearn":
raise ValueError(
"Invalid value for the models key of the engine parameter, "
Expand Down Expand Up @@ -234,8 +237,8 @@ def warnings(self, value: bool | str):
os.environ["PYTHONWARNINGS"] = self._warnings # Affects subprocesses (joblib)

@property
def logger(self) -> Logger:
"""Logger of this class."""
def logger(self) -> Logger | None:
"""Logger for this instance."""
return self._logger

@logger.setter
Expand All @@ -250,37 +253,39 @@ def logger(self, value: str | Logger | None):
"evalml",
]

if isinstance(value, Logger):
logger = value
# Clear existing handlers for external loggers
for name in external_loggers:
for handler in (log := getLogger(name)).handlers:
handler.close()
log.handlers.clear()

if not value:
logger = None
else:
logger = getLogger(self.__class__.__name__)
logger.setLevel(DEBUG)
if isinstance(value, Logger):
logger = value
else:
logger = getLogger(self.__class__.__name__)
logger.setLevel(DEBUG)

# Clear existing handlers
for name in [logger.name] + external_loggers:
for handler in (log := getLogger(name)).handlers:
# Clear existing handlers for current logger
for handler in logger.handlers:
handler.close()
log.handlers.clear()
logger.handlers.clear()

# Setup file handler
if isinstance(value, str):
# Prepare the FileHandler
if not value.endswith(".log"):
value += ".log"
if os.path.basename(value) == "auto.log":
current = dt.now().strftime("%d%b%y_%Hh%Mm%Ss")
value = value.replace("auto", self.__class__.__name__ + "_" + current)

formatter = Formatter("%(asctime)s - %(levelname)s: %(message)s")
fh = FileHandler(value)
fh.setFormatter(Formatter("%(asctime)s - %(levelname)s: %(message)s"))

handler = FileHandler(value)
handler.setFormatter(formatter)
else:
handler = NullHandler()

# Redirect loggers to file handler
for log in [logger.name] + external_loggers:
getLogger(log).addHandler(handler)
# Redirect loggers to file handler
for log in [logger.name] + external_loggers:
getLogger(log).addHandler(fh)

self._logger = logger

Check notice on line 290 in atom/basetransformer.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

An instance attribute is defined outside `__init__`

Instance attribute _logger defined outside __init__

Expand Down Expand Up @@ -751,6 +756,8 @@ def _no_data_sets(
else:
test_size = self.test_size

splitter = self._get_est_class("train_test_split", "model_selection")

try:
# Define holdout set size
if self.holdout_size:
Expand All @@ -766,7 +773,7 @@ def _no_data_sets(
f"got {self.holdout_size}."
)

data, holdout = train_test_split(
data, holdout = splitter(

Check warning on line 776 in atom/basetransformer.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

Attempt to call a non-callable object

'Predictor' object is not callable
data,
test_size=holdout_size,
random_state=self.random_state,
Expand All @@ -777,7 +784,7 @@ def _no_data_sets(
else:
holdout = None

train, test = train_test_split(
train, test = splitter(

Check warning on line 787 in atom/basetransformer.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

Attempt to call a non-callable object

'Predictor' object is not callable
data,
test_size=test_size,
random_state=self.random_state,
Expand Down Expand Up @@ -1018,8 +1025,9 @@ def log(self, msg: SCALAR | str, level: INT = 0, severity: str = "info"):
elif severity == "info" and self.verbose >= level:
print(msg)

for text in str(msg).split("\n"):
getattr(self.logger, severity)(str(text))
if self.logger:
for text in str(msg).split("\n"):
getattr(self.logger, severity)(str(text))

@composed(crash, method_to_log)

Check warning on line 1032 in atom/basetransformer.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

Incorrect type

Expected type 'tuple', got '(f: (...) -\> Any) -\> (...) -\> Any' instead
def save(self, filename: str = "auto", *, save_data: bool = True):
Expand Down
21 changes: 11 additions & 10 deletions atom/data_cleaning.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
from scipy.stats import zscore
from sklearn.base import BaseEstimator, clone
from sklearn.impute import KNNImputer
from sklearn.preprocessing import FunctionTransformer

from atom.basetransformer import BaseTransformer
from atom.utils import (
Expand Down Expand Up @@ -226,7 +225,7 @@ class Balancer(BaseEstimator, TransformerMixin, BaseTransformer):
- 2 to print detailed information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -571,7 +570,7 @@ class Cleaner(BaseEstimator, TransformerMixin, BaseTransformer):
- 2 to print detailed information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -1004,7 +1003,7 @@ class Discretizer(BaseEstimator, TransformerMixin, BaseTransformer):
- 2 to print detailed information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -1207,8 +1206,10 @@ def get_labels(labels, bins):
else:
bins = [-np.inf] + list(bins) + [np.inf]

estimator = self._get_est_class("FunctionTransformer", "preprocessing")

# Make of cut a transformer
self._discretizers[col] = FunctionTransformer(
self._discretizers[col] = estimator(

Check warning on line 1212 in atom/data_cleaning.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

Attempt to call a non-callable object

'Predictor' object is not callable
func=bk.cut,
kw_args={"bins": bins, "labels": get_labels(labels, bins)},
).fit(X[[col]])
Expand Down Expand Up @@ -1316,7 +1317,7 @@ class Encoder(BaseEstimator, TransformerMixin, BaseTransformer):
- 2 to print detailed information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -1724,7 +1725,7 @@ class Imputer(BaseEstimator, TransformerMixin, BaseTransformer):
- 2 to print detailed information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -2124,7 +2125,7 @@ class Normalizer(BaseEstimator, TransformerMixin, BaseTransformer):
- 1 to print basic information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -2425,7 +2426,7 @@ class Pruner(BaseEstimator, TransformerMixin, BaseTransformer):
- 2 to print detailed information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down Expand Up @@ -2728,7 +2729,7 @@ class Scaler(BaseEstimator, TransformerMixin, BaseTransformer):
- 1 to print basic information.
logger: str, Logger or None, default=None
- If None: Doesn't save a logging file.
- If None: Logging isn't used.
- If str: Name of the log file. Use "auto" for automatic naming.
- Else: Python `logging.Logger` instance.
Expand Down
Loading

0 comments on commit d15e5e6

Please sign in to comment.