Skip to content

Commit

Permalink
Merge branch 'main' into xgb-unused-params
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoralez committed Dec 6, 2024
2 parents 9c3be66 + da8d9e2 commit 2007ee5
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 532 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ jobs:
python-version: ${{ matrix.python-version }}

- name: Install the library
run: pip install uv && uv pip install --system ".[all]"
run: pip install uv && uv pip install --system ".[all]" "numba>=0.60" shap window-ops

- name: Run all tests
run: nbdev_test --n_workers 0 --do_print --timing --skip_file_re 'electricity' --flags 'polars'
run: nbdev_test --n_workers 0 --do_print --timing --skip_file_re 'electricity' --flags 'polars shap window_ops'

run-local-tests:
runs-on: ${{ matrix.os }}
Expand All @@ -44,7 +44,7 @@ jobs:
fail-fast: false
matrix:
os: [macos-13, macos-14, windows-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- name: Clone repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Expand Down Expand Up @@ -76,7 +76,7 @@ jobs:
python-version: "3.10"

- name: Install forecast notebook dependencies
run: pip install uv && uv pip install --system . datasetsforecast lightgbm matplotlib nbdev xgboost
run: pip install uv && uv pip install --system . lightgbm matplotlib nbdev pyarrow xgboost

- name: Run forecast notebook
run: nbdev_test --path nbs/forecast.ipynb
Expand Down
2 changes: 1 addition & 1 deletion mlforecast/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "0.15.1"
__version__ = "1.0.0"
__all__ = ['MLForecast']
from mlforecast.forecast import MLForecast
1 change: 0 additions & 1 deletion mlforecast/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,6 @@
'mlforecast.utils._ShortSeriesException': ('utils.html#_shortseriesexception', 'mlforecast/utils.py'),
'mlforecast.utils._ShortSeriesException.__init__': ( 'utils.html#_shortseriesexception.__init__',
'mlforecast/utils.py'),
'mlforecast.utils._ensure_shallow_copy': ('utils.html#_ensure_shallow_copy', 'mlforecast/utils.py'),
'mlforecast.utils.generate_daily_series': ('utils.html#generate_daily_series', 'mlforecast/utils.py'),
'mlforecast.utils.generate_prices_for_series': ( 'utils.html#generate_prices_for_series',
'mlforecast/utils.py')}}}
24 changes: 16 additions & 8 deletions mlforecast/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,26 @@ def __init__(self, *args, **kwargs): # noqa: ARG002
raise ImportError("Please install lightgbm to use this model.")


try:
from window_ops.shift import shift_array
except ImportError:

def shift_array(*_args, **_kwargs): # noqa: ARG002
raise Exception


try:
from xgboost import XGBRegressor
except ImportError:

class XGBRegressor:
def __init__(self, *args, **kwargs): # noqa: ARG002
raise ImportError("Please install xgboost to use this model.")


try:
from window_ops.shift import shift_array
except ImportError:
import numpy as np
from utilsforecast.compat import njit

@njit
def shift_array(x, offset):
if offset >= x.size or offset < 0:
return np.full_like(x, np.nan)
out = np.empty_like(x)
out[:offset] = np.nan
out[offset:] = x[:-offset]
return out
16 changes: 3 additions & 13 deletions mlforecast/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
_BaseGroupedArrayTargetTransform,
BaseTargetTransform,
)
from .utils import _ShortSeriesException, _ensure_shallow_copy
from .utils import _ShortSeriesException

# %% ../nbs/core.ipynb 10
date_features_dtypes = {
Expand Down Expand Up @@ -154,28 +154,19 @@ def _parse_transforms(
namer = _build_transform_name
for lag in lags:
transforms[f"lag{lag}"] = Lag(lag)
has_fns = False
for lag in lag_transforms.keys():
for tfm in lag_transforms[lag]:
if isinstance(tfm, _BaseLagTransform):
tfm_name = namer(tfm, lag)
transforms[tfm_name] = clone(tfm)._set_core_tfm(lag)
else:
has_fns = True
tfm, *args = _as_tuple(tfm)
assert callable(tfm)
tfm_name = namer(tfm, lag, *args)
transforms[tfm_name] = (lag, tfm, *args)
if has_fns:
warnings.warn(
"The `window_ops` package (and thus `numba`) will no longer be "
"a dependency in a future version.\n"
"Please make sure to add it to your requirements to ensure compatibility.",
category=FutureWarning,
)
return transforms

# %% ../nbs/core.ipynb 22
# %% ../nbs/core.ipynb 21
class TimeSeries:
"""Utility class for storing and transforming time series data."""

Expand Down Expand Up @@ -512,8 +503,7 @@ def _transform(
target_names = [f"{self.target_col}{i}" for i in range(max_horizon)]
df = ufp.assign_columns(df, target_names, target)
else:
if isinstance(df, pd.DataFrame):
df = _ensure_shallow_copy(df)
df = ufp.copy_if_pandas(df, deep=False)
df = ufp.assign_columns(df, self.target_col, target)
return df

Expand Down
25 changes: 10 additions & 15 deletions mlforecast/feature_engineering.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,22 @@
# %% ../nbs/feature_engineering.ipynb 3
from typing import Optional

from utilsforecast.compat import DataFrame
from utilsforecast.processing import (
drop_index_if_pandas,
horizontal_concat,
process_df,
take_rows,
)
import utilsforecast.processing as ufp
from utilsforecast.compat import DFType
from utilsforecast.validation import validate_format

from .core import _parse_transforms, Lags, LagTransforms
from .grouped_array import GroupedArray

# %% ../nbs/feature_engineering.ipynb 4
def transform_exog(
df: DataFrame,
df: DFType,
lags: Optional[Lags] = None,
lag_transforms: Optional[LagTransforms] = None,
id_col: str = "unique_id",
time_col: str = "ds",
num_threads: int = 1,
) -> DataFrame:
) -> DFType:
"""Compute lag features for dynamic exogenous regressors.
Parameters
Expand Down Expand Up @@ -60,11 +55,11 @@ def transform_exog(
# this is just a dummy target because process_df requires one
target_col = targets[0]
validate_format(df, id_col, time_col, target_col)
_, _, data, indptr, sort_idxs = process_df(df, id_col, time_col, target_col)
processed = ufp.process_df(df, id_col, time_col, target_col)
results = {}
cols = []
for j, target in enumerate(targets):
ga = GroupedArray(data[:, j], indptr)
ga = GroupedArray(processed.data[:, j], processed.indptr)
named_tfms = {f"{target}_{k}": v for k, v in tfms.items()}
if num_threads == 1 or len(named_tfms) == 1:
computed_tfms = ga.apply_transforms(
Expand All @@ -76,9 +71,9 @@ def transform_exog(
)
results.update(computed_tfms)
cols.extend(list(named_tfms.keys()))
if sort_idxs is not None:
base_df = take_rows(df, sort_idxs)
if processed.sort_idxs is not None:
base_df = ufp.take_rows(df, processed.sort_idxs)
else:
base_df = df
base_df = drop_index_if_pandas(base_df)
return horizontal_concat([base_df, type(df)(results)[cols]])
base_df = ufp.drop_index_if_pandas(base_df)
return ufp.horizontal_concat([base_df, type(df)(results)[cols]])
9 changes: 0 additions & 9 deletions mlforecast/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,6 @@ def __repr__(self):
return f"PredictionIntervals(n_windows={self.n_windows}, h={self.h}, method='{self.method}')"

# %% ../nbs/utils.ipynb 20
def _ensure_shallow_copy(df: pd.DataFrame) -> pd.DataFrame:
from packaging.version import Version

if Version(pd.__version__) < Version("1.4"):
# https://github.com/pandas-dev/pandas/pull/43406
df = df.copy()
return df

# %% ../nbs/utils.ipynb 21
class _ShortSeriesException(Exception):
def __init__(self, idxs):
self.idxs = idxs
23 changes: 16 additions & 7 deletions nbs/compat.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,28 @@
" )\n",
"\n",
"try:\n",
" from window_ops.shift import shift_array\n",
"except ImportError:\n",
" def shift_array(*_args, **_kwargs): # noqa: ARG002\n",
" raise Exception\n",
"\n",
"try:\n",
" from xgboost import XGBRegressor\n",
"except ImportError:\n",
" class XGBRegressor:\n",
" def __init__(self, *args, **kwargs): # noqa: ARG002\n",
" raise ImportError(\n",
" \"Please install xgboost to use this model.\"\n",
" )"
" )\n",
"\n",
"try:\n",
" from window_ops.shift import shift_array\n",
"except ImportError:\n",
" import numpy as np\n",
" from utilsforecast.compat import njit\n",
"\n",
" @njit\n",
" def shift_array(x, offset):\n",
" if offset >= x.size or offset < 0:\n",
" return np.full_like(x, np.nan)\n",
" out = np.empty_like(x)\n",
" out[:offset] = np.nan\n",
" out[offset:] = x[:-offset]\n",
" return out"
]
}
],
Expand Down
Loading

0 comments on commit 2007ee5

Please sign in to comment.