Skip to content

Commit

Permalink
drop python 3.9 support
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcovdBoom committed Nov 22, 2023
1 parent e7b1e7a commit 35687c1
Show file tree
Hide file tree
Showing 46 changed files with 400 additions and 396 deletions.
4 changes: 2 additions & 2 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ review and accept your changes.
If your contribution requires a new library dependency:

* Double-check that the new dependency is easy to install via pip and Anaconda.
* The library should support Python 3.9 and higher.
* The library should support Python 3.10 and 3.11.
* Make sure the code works with the latest version of the library.
* Update the dependencies in the documentation.
* Add the library with the minimum required version to `pyproject.toml`.

After submitting your pull request, GitHub will automatically run the tests
on your changes and make sure that the updated code builds successfully.
The checks run on Python 3.9, 3.10 and 3.11, on Ubuntu and Windows. We also
The checks run on Python 3.10 and 3.11, on Ubuntu and Windows. We also
use services that automatically check code style and test coverage.
2 changes: 1 addition & 1 deletion .github/workflows/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11"]
steps:
- name: Check out source repository
uses: actions/checkout@v3
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
--- | ---
**Repository** | [![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![Conda Recipe](https://img.shields.io/badge/recipe-atom--ml-green.svg)](https://anaconda.org/conda-forge/atom-ml) [![License: MIT](https://img.shields.io/github/license/tvdboom/ATOM)](https://opensource.org/licenses/MIT) [![Downloads](https://static.pepy.tech/badge/atom-ml)](https://pepy.tech/project/atom-ml)
**Release** | [![pdm-managed](https://img.shields.io/badge/pdm-managed-blueviolet)](https://pdm.fming.dev) [![PyPI version](https://img.shields.io/pypi/v/atom-ml)](https://pypi.org/project/atom-ml/) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/atom-ml.svg)](https://anaconda.org/conda-forge/atom-ml) [![DOI](https://zenodo.org/badge/195069958.svg)](https://zenodo.org/badge/latestdoi/195069958)
**Compatibility** | [![Python 3.9\|3.10\|3.11](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11-blue?logo=python)](https://www.python.org) [![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/atom-ml.svg)](https://anaconda.org/conda-forge/atom-ml)
**Compatibility** | [![Python 3.10\|3.11](https://img.shields.io/badge/python-3.10%20%7C%203.11-blue?logo=python)](https://www.python.org) [![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/atom-ml.svg)](https://anaconda.org/conda-forge/atom-ml)
**Build status** | [![Build Status](https://github.com/tvdboom/ATOM/workflows/ATOM/badge.svg)](https://github.com/tvdboom/ATOM/actions) [![Azure Pipelines](https://dev.azure.com/conda-forge/feedstock-builds/_apis/build/status/atom-ml-feedstock?branchName=master)](https://dev.azure.com/conda-forge/feedstock-builds/_build/latest?definitionId=10822&branchName=master) [![codecov](https://codecov.io/gh/tvdboom/ATOM/branch/master/graph/badge.svg)](https://codecov.io/gh/tvdboom/ATOM)
**Code analysis** | [![PEP8](https://img.shields.io/badge/code%20style-pep8-orange.svg)](https://www.python.org/dev/peps/pep-0008/) [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
**Code analysis** | [![PEP8](https://img.shields.io/badge/code%20style-pep8-orange.svg)](https://www.python.org/dev/peps/pep-0008/) [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) [![flake8](https://img.shields.io/badge/flake8-checked-blue)](https://flake8.pycqa.org/en/latest/) [![mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://www.mypy-lang.org/)


<br><br>
Expand Down
11 changes: 5 additions & 6 deletions atom/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import numpy as np
import pandas as pd
from beartype import beartype
from beartype.typing import Any, Callable, Iterator, Literal, TypeVar
from beartype.typing import Any, Callable, Iterator, Literal, Sequence, TypeVar
from joblib.memory import Memory
from pandas._typing import DtypeObj

Check notice on line 27 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 _typing of a class
from scipy import stats
Expand Down Expand Up @@ -55,9 +55,8 @@
FloatZeroToOneInc, Index, IndexSelector, Int, IntLargerEqualZero,
IntLargerTwo, IntLargerZero, MetricConstructor, ModelsConstructor, NItems,
NJobs, NormalizerStrats, NumericalStrats, Operators, Pandas, PrunerStrats,
RowSelector, Scalar, ScalerStrats, Sequence, SequenceTypes, Series,
TargetSelector, Transformer, TSIndexTypes, VectorizerStarts, Verbose,
Warnings, XSelector, YSelector,
RowSelector, Scalar, ScalerStrats, Series, TargetSelector, Transformer,
TSIndex, VectorizerStarts, Verbose, Warnings, XSelector, YSelector,
)
from atom.utils.utils import (
ClassMap, DataConfig, DataContainer, Goal, adjust_verbosity, bk,
Expand Down Expand Up @@ -541,7 +540,7 @@ def eda(

if isinstance(rows, str):
rows_c = [(self.branch._get_rows(rows), rows)]

Check notice on line 542 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_rows of a class
elif isinstance(rows, SequenceTypes):
elif isinstance(rows, Sequence):
rows_c = [(self.branch._get_rows(r), r) for r in rows]

Check notice on line 544 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_rows of a class
elif isinstance(rows, dict):
rows_c = [(self.branch._get_rows(v), k) for k, v in rows.items()]

Check notice on line 546 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_rows of a class
Expand Down Expand Up @@ -938,7 +937,7 @@ def stats(self, _vb: Int = -2, /):
for set_ in ("train", "test", "holdout"):
if (data := getattr(self, set_)) is not None:
self._log(f"{set_.capitalize()} set size: {len(data)}", _vb)
if isinstance(self.branch.train.index, TSIndexTypes):
if isinstance(self.branch.train.index, TSIndex):
self._log(f" --> From: {min(data.index)} To: {max(data.index)}", _vb)

self._log("-" * 37, _vb)
Expand Down
33 changes: 15 additions & 18 deletions atom/basemodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,10 @@
from atom.plots import RunnerPlot
from atom.utils.constants import DF_ATTRS
from atom.utils.types import (
HT, Backend, Bool, DataFrame, DataFrameTypes, Engine, FHSelector, Float,
FloatTypes, FloatZeroToOneExc, Int, IntLargerEqualZero, IntTypes,
MetricConstructor, NJobs, Pandas, PredictionMethod, Predictor, RowSelector,
Scalar, Scorer, Sequence, Stages, TargetSelector, Verbose, Warnings,
XSelector, YSelector,
HT, Backend, Bool, DataFrame, Engine, FHSelector, Float, FloatZeroToOneExc,
Int, IntLargerEqualZero, MetricConstructor, NJobs, Pandas,
PredictionMethod, Predictor, RowSelector, Scalar, Scorer, Sequence, Stages,
TargetSelector, Verbose, Warnings, XSelector, YSelector,
)
from atom.utils.utils import (
ClassMap, DataConfig, Goal, PlotCallback, ShapExplanation, Task,
Expand Down Expand Up @@ -282,7 +281,7 @@ def __contains__(self, item: str) -> bool:
return item in self.dataset

def __getitem__(self, item: Int | str | list) -> Pandas:
if isinstance(item, IntTypes):
if isinstance(item, Int):
return self.dataset[self.columns[item]]
else:
return self.dataset[item] # Get a subset of the dataset
Expand Down Expand Up @@ -412,7 +411,7 @@ def _trial_to_est(self, params: dict[str, Any]) -> dict[str, Any]:
"""
return deepcopy(params)

def _get_est(self, **params) -> Predictor:
def _get_est(self, params: dict[str, Any]) -> Predictor:
"""Get the estimator instance.
Use the multioutput meta-estimator if the estimator has
Expand All @@ -423,8 +422,8 @@ def _get_est(self, **params) -> Predictor:
Parameters
----------
**params
Unpacked hyperparameters for the estimator.
params: dict
Hyperparameters for the estimator.
Returns
-------
Expand Down Expand Up @@ -819,7 +818,7 @@ def _get_score(
else:
if threshold and self.task.is_binary and hasattr(self, "predict_proba"):
y_true, y_pred = self._get_pred(rows, attr="predict_proba")
if isinstance(y_pred, DataFrameTypes):
if isinstance(y_pred, DataFrame):
# Update every target column with its corresponding threshold
for i, value in enumerate(threshold):
y_pred.iloc[:, i] = (y_pred.iloc[:, i] > value).astype("int")
Expand Down Expand Up @@ -962,9 +961,7 @@ def fit_model(
trial.set_user_attr(key, value)

# Create estimator instance with trial-specific hyperparameters
estimator = self._get_est(
**{**self._est_params, **self._trial_to_est(params)}
)
estimator = self._get_est(self._est_params | self._trial_to_est(params))

# Check if the same parameters have already been evaluated
for t in trial.study.get_trials(False, states=(TrialState.COMPLETE,))[::-1]:
Expand All @@ -977,13 +974,13 @@ def fit_model(
# Follow the same stratification strategy as atom
cols = self._config.get_stratify_columns(self.og.train, self.og.y_train)

if isinstance(cv := self._ht["cv"], IntTypes):
if isinstance(cv := self._ht["cv"], Int):
if self.task.is_forecast:
if cv == 1:
splitter = SingleWindowSplitter(range(1, len(self.og.test)))
else:
splitter = TimeSeriesSplit(n_splits=cv)
elif isinstance(self._ht["cv"], IntTypes):
elif isinstance(self._ht["cv"], Int):
# We use ShuffleSplit instead of K-fold because it
# works with n_splits=1 and multioutput stratification
if cols is None:
Expand Down Expand Up @@ -1169,7 +1166,7 @@ def fit(self, X: DataFrame | None = None, y: Pandas | None = None):
# Assign estimator if not done already
if self._estimator is None:
self._check_est_params()
self._estimator = self._get_est(**{**self._est_params, **self.best_params})
self._estimator = self._get_est(self._est_params | self.best_params)

self._estimator = self._fit_estimator(
estimator=self.estimator,
Expand Down Expand Up @@ -1808,7 +1805,7 @@ def inference(*X) -> Scalar | str | list[Scalar | str]:
conv = lambda elem: elem.item() if hasattr(elem, "item") else elem

y_pred = self.inverse_transform(y=self.predict([X], verbose=0), verbose=0)
if isinstance(y_pred, DataFrameTypes):
if isinstance(y_pred, DataFrame):
return [conv(elem) for elem in y_pred.iloc[0, :]]
else:
return conv(y_pred[0])
Expand Down Expand Up @@ -2031,7 +2028,7 @@ def evaluate(
Scores of the model.
"""
if isinstance(threshold, FloatTypes):
if isinstance(threshold, Float):
threshold_c = [threshold] * self.branch._data.n_cols # Length=n_targets

Check notice on line 2032 in atom/basemodel.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 _data of a class
elif len(threshold) != self.branch._data.n_cols:

Check notice on line 2033 in atom/basemodel.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 _data of a class
raise ValueError(
Expand Down
27 changes: 13 additions & 14 deletions atom/baserunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@
from atom.pipeline import Pipeline
from atom.utils.constants import DF_ATTRS
from atom.utils.types import (
Bool, DataFrame, DataFrameTypes, FloatZeroToOneExc, Int, IntTypes,
MetricConstructor, Model, ModelSelector, ModelsSelector, Pandas,
RowSelector, Scalar, Segment, SegmentTypes, SequenceTypes, Series,
YSelector,
Bool, DataFrame, FloatZeroToOneExc, Int, MetricConstructor, Model,
ModelSelector, ModelsSelector, Pandas, RowSelector, Scalar, Segment,
Series, YSelector,
)
from atom.utils.utils import (
ClassMap, DataContainer, Task, bk, check_is_fitted, composed, crash,
Expand All @@ -53,7 +52,7 @@ class BaseRunner(BaseTracker, metaclass=ABCMeta):

def __getstate__(self) -> dict[str, Any]:
# Store an extra attribute with the package versions
return {**self.__dict__, "_versions": get_versions(self._models)}
return self.__dict__ | {"_versions": get_versions(self._models)}

def __setstate__(self, state: dict[str, Any]):
versions = state.pop("_versions", None)
Expand Down Expand Up @@ -109,7 +108,7 @@ def __getitem__(self, item: Int | str | list) -> Any:
"This instance has no dataset annexed to it. "
"Use the run method before calling __getitem__."
)
elif isinstance(item, IntTypes):
elif isinstance(item, Int):
return self.dataset[self.columns[item]]
elif isinstance(item, str):
if item in self._branches:
Expand Down Expand Up @@ -289,7 +288,7 @@ def _set_index(self, df: DataFrame, y: Pandas | None) -> DataFrame:
pass
elif self._config.index is False:
df = df.reset_index(drop=True)
elif isinstance(self._config.index, IntTypes):
elif isinstance(self._config.index, Int):
if -df.shape[1] <= self._config.index <= df.shape[1]:
df = df.set_index(df.columns[int(self._config.index)], drop=True)
else:
Expand Down Expand Up @@ -415,7 +414,7 @@ def _no_data_sets(
)
data = _subsample(data)

if isinstance(self._config.index, SequenceTypes):
if isinstance(self._config.index, Sequence):
if len(self._config.index) != len(data):
raise IndexError(
"Invalid value for the index parameter. Length of "
Expand Down Expand Up @@ -486,7 +485,7 @@ def _no_data_sets(

except ValueError as ex:
# Clarify common error with stratification for multioutput tasks
if "least populated class" in str(ex) and isinstance(y, DataFrameTypes):
if "least populated class" in str(ex) and isinstance(y, DataFrame):
raise ValueError(
"Stratification for multioutput tasks is applied over all target "
"columns, which results in a least populated class that has only "
Expand Down Expand Up @@ -572,7 +571,7 @@ def _has_data_sets(
)

# If the index is a sequence, assign it before shuffling
if isinstance(self._config.index, SequenceTypes):
if isinstance(self._config.index, Sequence):
len_data = len(train) + len(test)
if holdout is not None:
len_data += len(holdout)
Expand Down Expand Up @@ -605,7 +604,7 @@ def _has_data_sets(
# Process input arrays ===================================== >>

if len(arrays) == 0:
if self._goal.name == "forecast" and not isinstance(y, (IntTypes, str)):
if self._goal.name == "forecast" and not isinstance(y, Int | str):
# arrays=() and y=y for forecasting
sets = _no_data_sets(*self._check_input(y=y))
elif not self.branch._container:

Check notice on line 610 in atom/baserunner.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 _container of a class
Expand All @@ -626,7 +625,7 @@ def _has_data_sets(
X_train, y_train = self._check_input(arrays[0][0], arrays[0][1])

Check notice on line 625 in atom/baserunner.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

PEP 8 naming convention violation

Variable in function should be lowercase
X_test, y_test = self._check_input(arrays[1][0], arrays[1][1])

Check notice on line 626 in atom/baserunner.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

PEP 8 naming convention violation

Variable in function should be lowercase
sets = _has_data_sets(X_train, y_train, X_test, y_test)
elif isinstance(arrays[1], (*IntTypes, str)) or n_cols(arrays[1]) == 1:
elif isinstance(arrays[1], Int | str) or n_cols(arrays[1]) == 1:
if not self._goal.name == "forecast":
# arrays=(X, y)
sets = _no_data_sets(*self._check_input(arrays[0], arrays[1]))
Expand Down Expand Up @@ -730,11 +729,11 @@ def _get_models(
exc: list[Model] = []
if models is None:
inc = self._models.values()
elif isinstance(models, SegmentTypes):
elif isinstance(models, Segment):
inc = get_segment(self._models, models)
else:
for model in lst(models):
if isinstance(model, IntTypes):
if isinstance(model, Int):
try:
inc.append(self._models[model])
except KeyError:
Expand Down
6 changes: 3 additions & 3 deletions atom/basetrainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from atom.data_cleaning import BaseTransformer
from atom.models import MODELS, CustomModel
from atom.plots import RunnerPlot
from atom.utils.types import Model, SequenceTypes
from atom.utils.types import Model, Sequence
from atom.utils.utils import (
ClassMap, DataConfig, Goal, Task, check_dependency, get_custom_scorer, lst,
sign, time_to_str,
Expand Down Expand Up @@ -104,7 +104,7 @@ def _check_param(self, param: str, value: Any) -> dict:
Parameter with model names as keys.
"""
if isinstance(value, SequenceTypes):
if isinstance(value, Sequence):
if len(value) != len(self._models):
raise ValueError(
f"Invalid value for the {param} parameter. The length "
Expand Down Expand Up @@ -296,7 +296,7 @@ def _prepare_parameters(self):
)
elif k not in self._models:
self._ht_params[key][name][k] = v
elif key in {**sign(create_study), **sign(Study.optimize)}:
elif key in sign(create_study) | sign(Study.optimize):
self._ht_params[key] = {k: value for k in self._models.keys()}
else:
raise ValueError(
Expand Down
15 changes: 7 additions & 8 deletions atom/basetransformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@
from sklearn.utils.validation import check_memory

from atom.utils.types import (
Backend, Bool, BoolTypes, DataFrame, DataFrameTypes, Engine, Estimator,
Int, IntLargerEqualZero, IntTypes, Pandas, SequenceTypes, Severity,
Verbose, Warnings, XSelector, YSelector,
Backend, Bool, DataFrame, Engine, Estimator, Int, IntLargerEqualZero,
Pandas, Seq1dim, Severity, Verbose, Warnings, XSelector, YSelector,
)
from atom.utils.utils import crash, flt, n_cols, sign, to_df, to_pandas

Expand Down Expand Up @@ -209,7 +208,7 @@ def warnings(self) -> Warnings:
@warnings.setter
@beartype
def warnings(self, value: Bool | Warnings):
if isinstance(value, BoolTypes):
if isinstance(value, Bool):
self._warnings: Warnings = "once" if value else "ignore"
else:
self._warnings = value

Check notice on line 214 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 _warnings defined outside __init__
Expand Down Expand Up @@ -502,7 +501,7 @@ def _check_input(
)

# Prepare target column
if isinstance(y, (dict, *SequenceTypes, *DataFrameTypes)):
if isinstance(y, dict | Seq1dim | DataFrame):
if isinstance(y, dict):
yt = to_df(deepcopy(y), index=getattr(Xt, "index", None))
if n_cols(yt) == 1:
Expand All @@ -516,7 +515,7 @@ def _check_input(
for col in y:

Check warning on line 515 in atom/basetransformer.py

View workflow job for this annotation

GitHub Actions / Qodana Community for Python

Incorrect type

Expected type 'collections.Iterable', got 'None' instead
if col in Xt.columns:
targets.append(col)
elif isinstance(col, IntTypes):
elif isinstance(col, Int):
if -Xt.shape[1] <= col < Xt.shape[1]:
targets.append(Xt.columns[int(col)])
else:
Expand All @@ -541,7 +540,7 @@ def _check_input(
data=deepcopy(yt),
index=getattr(Xt, "index", None),
name=flt(name) if name is not None else "target",
columns=name if isinstance(name, SequenceTypes) else default_cols,
columns=name if isinstance(name, Sequence) else default_cols,
)

# Check X and y have the same indices
Expand All @@ -558,7 +557,7 @@ def _check_input(
else:
raise ValueError("X can't be None when y is a string.")

elif isinstance(y, IntTypes):
elif isinstance(y, Int):
if Xt is None:
raise ValueError("X can't be None when y is an int.")

Expand Down
Loading

0 comments on commit 35687c1

Please sign in to comment.