### Install Library and Launch Jupyter Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/README.md Install the library with example dependencies and launch Jupyter Notebook to access the examples. ```bash pip install purgedcv[examples] jupyter notebook examples/ ``` -------------------------------- ### Install Development and Example Dependencies Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/arxiv_paper.tex Installs the package in editable mode with development and example dependencies. This is useful for local development and running provided examples. ```latex pip install -e ".[dev,examples]" ``` -------------------------------- ### Install PurgedCV with Examples Extra Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md Install the 'examples' extra to run the worked notebooks, which may require Jupyter and Matplotlib. ```bash pip install "purgedcv[examples]" ``` -------------------------------- ### Install Development Dependencies and Run Microbenchmark Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/jdssv_paper.md Installs the package in editable mode with development and example dependencies, then runs a microbenchmark test. This is useful for local development and testing. ```bash pip install -e ".\[dev,examples\]" pytest -q python tools/microbench.py ``` -------------------------------- ### Install PurgedCV with Development Extras Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md Install the 'dev' extra to get tools for running the test suite, linter, and type checker locally. ```bash pip install "purgedcv[dev]" ``` -------------------------------- ### Install and Run Benchmarks Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/jdssv_paper.tex Commands to install the package with development dependencies, run tests, and execute microbenchmarks and full benchmarks. ```latex pip install -e ".[dev,examples]" pytest -q python tools/microbench.py python tools/competitor_benchmark.py --core-only --out-dir examples/data python tools/lcl_full_benchmark.py --k 20 --n 60 --seed 0 ``` -------------------------------- ### Install PurgedCV with Documentation Extras Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md Install the 'docs' extra to build the documentation site locally. ```bash pip install "purgedcv[docs]" ``` -------------------------------- ### Clone and Set Up Development Environment Source: https://github.com/eslazarev/purged-cross-validation/blob/main/CONTRIBUTING.md Clone the repository, create a virtual environment, activate it, and install the package in editable mode with development and documentation dependencies. ```bash git clone https://github.com/eslazarev/purged-cross-validation.git cd purged-cross-validation python -m venv .venv source .venv/bin/activate pip install -e ".[dev,docs]" ``` -------------------------------- ### Serve Documentation Site Locally Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md Run this command after installing the 'docs' extra to serve the documentation site. ```bash mkdocs serve ``` -------------------------------- ### Install Development Dependencies and Run Tests Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/code_metadata.md Installs the package in editable mode with development dependencies and then runs the full test suite. Use this to ensure the code is functioning correctly. ```bash pip install -e ".[dev]" pytest -q ``` -------------------------------- ### Install Competitor Time Series Libraries Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/arxiv_paper.tex Installs the tscv and timeseriescv libraries, which are used as competitors in the benchmark tests. These are required to reproduce benchmark results involving these libraries. ```latex pip install tscv timeseriescv ``` -------------------------------- ### Install purgedcv and matplotlib Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/cv-leakage-controlled-study.ipynb Installs the necessary libraries for the study. A kernel restart might be required after installation. ```bash %pip install -q purgedcv==0.1.1 matplotlib ``` -------------------------------- ### Import Libraries and Setup Constants Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/air_quality_clock_leakage.ipynb Imports necessary libraries for data manipulation, modeling, and cross-validation. Sets up constants for the forecast horizon and number of cross-validation splits. ```python import urllib.request import zipfile from io import BytesIO from pathlib import Path import numpy as np import pandas as pd from sklearn.model_selection import KFold, cross_val_score from sklearn.neighbors import KNeighborsRegressor from purgedcv import PurgedKFold, WalkForwardSplit from purgedcv.diagnostics import compute_overlap_fraction SEED = 0 H = 72 # label = mean concentration over the next 72 hours (3-day forecast) N_SPLITS = 5 ``` -------------------------------- ### Verify PurgedCV Installation Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md Run this command to confirm that purgedcv is installed correctly and to check its version. ```python python -c "import purgedcv; print(purgedcv.__version__)" ``` -------------------------------- ### Setup Walk-Forward Cross-Validation Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/predictive_maintenance_nasa.ipynb Initializes a WalkForwardSplit object for cross-validation. This setup uses an expanding window, defines prediction and evaluation times, and incorporates purge horizon and embargo periods to prevent temporal leakage, mimicking real-world deployment scenarios. ```python SENSOR_COLS = [f"sensor_{i}" for i in range(1, 22)] X = train_df[SENSOR_COLS].values y = train_df["rul"].values prediction_times = train_df["timestamp"].reset_index(drop=True) evaluation_times = prediction_times + pd.Timedelta(days=1) cv = WalkForwardSplit( n_splits=5, test_size=500, window="expanding", prediction_times=prediction_times, evaluation_times=evaluation_times, purge_horizon="3D", embargo="5D", ) print(f"Number of walk-forward folds: {cv.get_n_splits()}") print(f"Total training rows: {len(train_df):,}") ``` -------------------------------- ### Install Purged Cross-Validation from Repository Source: https://github.com/eslazarev/purged-cross-validation/blob/main/README.md Install the library directly from its GitHub repository using pip. This is useful for installing the latest development version. ```bash pip install git+https://github.com/eslazarev/purged-cross-validation.git ``` -------------------------------- ### Install PurgedCV from PyPI Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md Use this command to install the base purgedcv package, which includes splitters, metrics, and diagnostics. ```bash pip install purgedcv ``` -------------------------------- ### Benchmark Competitor Performance Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/code_metadata.md Installs development dependencies and runs a benchmark comparing scikit-learn with the purgedcv core functionality. Results are saved to a specified output directory. ```bash pip install -e ".[dev]" python tools/competitor_benchmark.py --core-only --out-dir /tmp/competitor ``` -------------------------------- ### Setup Cross-Validation and Models Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/earthquake_magnitude_leakage.ipynb Initializes KNeighborsRegressor and various cross-validation strategies including naive shuffled KFold, blocked KFold, PurgedKFold, and WalkForwardSplit. PurgedKFold and WalkForwardSplit are designed to prevent leakage in time-series data by considering prediction and evaluation times and purge horizons. ```python model = KNeighborsRegressor(n_neighbors=10) aive = KFold(n_splits=N_SPLITS, shuffle=True, random_state=SEED) blocked = KFold(n_splits=N_SPLITS, shuffle=False) purged = PurgedKFold( n_splits=N_SPLITS, prediction_times=pred, evaluation_times=evalu, purge_horizon="60D", embargo="60D", ) wf = WalkForwardSplit( n_splits=N_SPLITS, test_size=len(y) // 8, window="expanding", prediction_times=pred, evaluation_times=evalu, purge_horizon="60D", ) ``` -------------------------------- ### Install PurgedCV from conda-forge Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/installation.md For users of conda or mamba, install purgedcv from the conda-forge channel. ```bash conda install -c conda-forge purgedcv ``` ```bash mamba install -c conda-forge purgedcv ``` -------------------------------- ### PurgedKFold Example Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/jdssv_paper.txt Example usage of PurgedKFold for time-series cross-validation with temporal leakage assertion. ```APIDOC ## PurgedKFold Example This example demonstrates how to use `PurgedKFold` for cross-validation and includes an assertion to check for temporal leakage. ### Usage ```python from purgedcv import PurgedKFold from purgedcv.diagnostics import assert_no_temporal_leakage cv = PurgedKFold( n_splits=5, prediction_times=prediction_times, evaluation_times=evaluation_times, purge_horizon="12h", embargo="2h", ) for train_idx, test_idx in cv.split(X, y): assert_no_temporal_leakage( train_idx, test_idx, prediction_times, evaluation_times ) ``` ### Parameters for PurgedKFold - **n_splits** (int): The number of folds. - **prediction_times** (array-like): The prediction times for each sample. - **evaluation_times** (array-like): The evaluation times for each sample. - **purge_horizon** (str or timedelta): The duration to purge after each test set. - **embargo** (str or timedelta): The embargo duration after the purge horizon. ``` -------------------------------- ### Import Libraries and Set Up Constants Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/selection_regret_crypto.ipynb Imports necessary libraries for data manipulation, modeling, and plotting, and defines constants for the forward horizon and deployment period. ```python from pathlib import Path import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.base import clone from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import Ridge from sklearn.metrics import mean_absolute_error, r2_score from sklearn.model_selection import KFold, cross_val_score from sklearn.neighbors import KNeighborsRegressor from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from purgedcv import PurgedKFold SEED = 0 H = 5 # forward horizon (days) N_DEPLOY = 180 # ~6 trading months held out as deployment ``` -------------------------------- ### Setting up and using PurgedKFold Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/jdssv_paper.md Demonstrates how to initialize PurgedKFold with specific parameters and iterate through its splits. It also shows how to use the assert_no_temporal_leakage diagnostic function to validate the splits. ```python from purgedcv import PurgedKFold from purgedcv.diagnostics import assert_no_temporal_leakage cv = PurgedKFold( n_splits=5, prediction_times=prediction_times, evaluation_times=evaluation_times, purge_horizon="12h", embargo="2h", ) for train_idx, test_idx in cv.split(X, y): assert_no_temporal_leakage( train_idx, test_idx, prediction_times, evaluation_times ) ``` -------------------------------- ### Load and Prepare BTC/USDT Daily Data Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/backtest_overfitting_audit.ipynb Downloads BTC/USDT daily OHLCV data from Binance Spot, caches it locally, and calculates log returns. Requires the 'pricehub' library for data fetching. ```python from pathlib import Path import pandas as pd import numpy as np DATA_DIR = Path("data") if Path("data").exists() else Path("examples/data") DATA_DIR.mkdir(parents=True, exist_ok=True) cache = DATA_DIR / "btcusdt_1d_binance_spot_2021_2023.csv" if cache.exists(): raw = pd.read_csv(cache, index_col=0, parse_dates=True) else: from pricehub import get_ohlc raw = get_ohlc( broker="binance_spot", symbol="BTCUSDT", interval="1d", start="2021-01-01", end="2023-09-28", ) raw.to_csv(cache) close = raw["Close"].astype(float) log_ret = np.log(close / close.shift(1)) print(f"{len(close)} daily bars, {close.index[0].date()} to {close.index[-1].date()}") ``` -------------------------------- ### Backtesting Champion Model with CombinatorialPurgedCV Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/backtest_overfitting_audit.ipynb This snippet demonstrates how to build features, set up a CombinatorialPurgedCV object, train a pipeline with StandardScaler and Ridge regression, and backtest the champion model across multiple purged partitions. It then calculates and prints path metrics and a summary of Sharpe ratios. ```python champ = trial_params[champion] X = build_features(champ["w_mom"], champ["w_vol"], champ["w_rev"]) y = realised_next pred_times = pd.Series(close.index[idx]) eval_times = pred_times + pd.Timedelta(days=1) cv = CombinatorialPurgedCV( n_splits=8, n_test_groups=2, prediction_times=pred_times, evaluation_times=eval_times, purge_horizon="1D", ) model = make_pipeline(StandardScaler(), Ridge(alpha=champ["alpha"])) pred_paths = cv.backtest_paths(model, X, y) strat_paths = np.sign(pred_paths) * y[np.newaxis, :] paths_table = path_metrics(strat_paths, bars_per_year=BARS_PER_YEAR) print(paths_table.round(2).to_string()) print() print( f"path Sharpe median {paths_table['sharpe'].median():+.2f}" f" worst {paths_table['sharpe'].min():+.2f} best {paths_table['sharpe'].max():+.2f}" ) ``` -------------------------------- ### Import necessary libraries Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/ohlc_trading_signal.ipynb Imports required libraries for data manipulation, fetching OHLC data, and cross-validation. Ensure these libraries are installed. ```python from pathlib import Path import numpy as np import pandas as pd from pricehub import get_ohlc from sklearn.model_selection import KFold, cross_val_score from sklearn.neighbors import KNeighborsRegressor from purgedcv import PurgedKFold from purgedcv.diagnostics import compute_overlap_fraction INTERVAL = "1d" # any pricehub interval; bar duration is inferred from data SEED = 0 H = 20 # label = return over the next H trading days N_SPLITS = 5 ``` -------------------------------- ### CPCV Backtest Paths and Metric Calculation Source: https://github.com/eslazarev/purged-cross-validation/blob/main/README.md This snippet demonstrates the complete CPCV workflow. It initializes CombinatorialPurgedCV, generates backtest paths using a dummy regressor, and calculates the Probabilistic Sharpe Ratio (PSR) and minimum track record length (MinTRL). Use this for a comprehensive evaluation of time-series strategies. ```python import numpy as np import pandas as pd from sklearn.dummy import DummyRegressor from purgedcv import ( CombinatorialPurgedCV, probabilistic_sharpe_ratio, min_track_record_length, ) rng = np.random.default_rng(42) n = 120 pred = pd.Series(pd.date_range("2023-01-01", periods=n, freq="D")) evalu = pred + pd.Timedelta(days=2) X = rng.standard_normal((n, 3)) y = X @ np.array([0.5, -0.3, 0.2]) + rng.standard_normal(n) * 0.1 # N=6, K=2 → C(6,2) = 15 folds → C(5,1) = 5 backtest paths cv = CombinatorialPurgedCV( n_splits=6, n_test_groups=2, prediction_times=pred, evaluation_times=evalu, ) # paths.shape == (n_paths, n_samples); NaN where a sample was not OOS in that path paths = cv.backtest_paths(DummyRegressor(strategy="mean"), X, y) print(f"Backtest paths: {paths.shape}") # (5, 120) # PSR evaluates one strategy's realised RETURN series (not the prediction paths # above, which are model outputs, not returns). Shown on a synthetic daily series: strategy_returns = rng.normal(0.0008, 0.01, 504) psr = probabilistic_sharpe_ratio(strategy_returns, benchmark_skill=0.0) print(f"PSR: {psr:.3f}") # P(true Sharpe > 0) # MinTRL: observations needed to show an observed Sharpe beats a benchmark at # 95% confidence. The Sharpes and the count share one frequency, so a high # per-bar Sharpe (0.7) needs few bars. n_min = min_track_record_length( observed_sharpe=0.7, target_sharpe=0.5, alpha=0.05, skew=0.0, kurtosis=3.0 ) print(f"MinTRL: {int(n_min)} observations") ``` -------------------------------- ### TrialSharpeRecorder Source: https://github.com/eslazarev/purged-cross-validation/blob/main/docs/api.md Optuna integration for recording Sharpe ratio during trials. ```APIDOC ## Class: TrialSharpeRecorder ### Description Optuna integration for recording Sharpe ratio during trials. ``` -------------------------------- ### Import Libraries and Print Version Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/energy_demand_pjm.ipynb Imports necessary libraries for data manipulation, plotting, and cross-validation. It also prints the installed version of the purged-cross-validation library. ```python import urllib.request import warnings from pathlib import Path import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.ensemble import GradientBoostingRegressor from sklearn.exceptions import FitFailedWarning import purgedcv from purgedcv import ( CombinatorialPurgedCV, deflated_sharpe_ratio, diagnostics, min_track_record_length, probabilistic_sharpe_ratio, ) print(f"purged-cross-validation version: {purgedcv.__version__}") ``` -------------------------------- ### Reproduce LCL Full Benchmark Source: https://github.com/eslazarev/purged-cross-validation/blob/main/paper/supplementary/lcl_full_benchmark_summary.md Command to reproduce the benchmark results. Requires a local LCL CSV directory. Adjust parameters like k, n, and seed as needed. ```bash python tools/lcl_full_benchmark.py --raw-dir --k 20 --n 60 --seed 0 ``` -------------------------------- ### Build Documentation Locally Source: https://github.com/eslazarev/purged-cross-validation/blob/main/CONTRIBUTING.md Build the project documentation locally to check for broken links or missing autodoc references. The --strict flag treats warnings as errors. ```bash mkdocs build --strict ``` -------------------------------- ### Hyperparameter Grid and Cross-Validation Setup Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/selection_regret_crypto.ipynb Defines a grid of regression models and sets up both naive KFold and PurgedKFold for cross-validation. PurgedKFold is configured to avoid leakage by specifying prediction and evaluation times. ```python grid = [ ("kNN(k=1)", make_pipeline(StandardScaler(), KNeighborsRegressor(n_neighbors=1))), ("kNN(k=5)", make_pipeline(StandardScaler(), KNeighborsRegressor(n_neighbors=5))), ("kNN(k=20)", make_pipeline(StandardScaler(), KNeighborsRegressor(n_neighbors=20))), ("kNN(k=50)", make_pipeline(StandardScaler(), KNeighborsRegressor(n_neighbors=50))), ("Ridge a=.01", make_pipeline(StandardScaler(), Ridge(alpha=0.01))), ("Ridge a=1", make_pipeline(StandardScaler(), Ridge(alpha=1.0))), ("Ridge a=100", make_pipeline(StandardScaler(), Ridge(alpha=100.0))), ("RF d=None", RandomForestRegressor(n_estimators=200, max_depth=None, random_state=SEED, n_jobs=-1)), ("RF d=4", RandomForestRegressor(n_estimators=200, max_depth=4, random_state=SEED, n_jobs=-1)), ] naive_cv = KFold(n_splits=5, shuffle=True, random_state=SEED) honest_cv = PurgedKFold( n_splits=5, prediction_times=pred.iloc[sel].reset_index(drop=True), evaluation_times=evalu.iloc[sel].reset_index(drop=True), ) ``` -------------------------------- ### Import Libraries for Predictive Maintenance Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/predictive_maintenance_nasa.ipynb Imports necessary libraries including `urllib.request`, `pathlib`, `matplotlib`, `numpy`, `pandas`, `sklearn`, and `purgedcv`. This setup is required for data handling, visualization, and cross-validation. ```python import urllib.request from pathlib import Path import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.ensemble import GradientBoostingRegressor from sklearn.metrics import mean_absolute_error import purgedcv from purgedcv import WalkForwardSplit, diagnostics print(f"purged-cross-validation version: {purgedcv.__version__}") ``` -------------------------------- ### Feature Engineering and Model Setup for Prediction Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/backtest_overfitting_audit.ipynb Defines functions to build features (momentum, volatility, reversal) and calculate per-bar strategy returns using a Ridge model. It prepares data for in-sample fitting and out-of-sample prediction. ```python from sklearn.preprocessing import StandardScaler from sklearn.linear_model import Ridge from sklearn.pipeline import make_pipeline # Assume WARMUP, close, and log_ret are defined from the previous cell # WARMUP = 100 # Example value, actual value depends on context idx = np.arange(WARMUP, len(close) - 1) # valid bar positions t (need t+1) lr = log_ret.to_numpy() realised_next = lr[idx + 1] # next-bar return, the label and the payoff n_obs = idx.size split = int(0.7 * n_obs) # in-sample / out-of-sample boundary def build_features(w_mom: int, w_vol: int, w_rev: int) -> np.ndarray: mom = log_ret.rolling(w_mom).sum().to_numpy()[idx] vol = log_ret.rolling(w_vol).std().to_numpy()[idx] rev = (-log_ret.rolling(w_rev).sum()).to_numpy()[idx] return np.column_stack([mom, vol, rev]) def config_returns(w_mom: int, w_vol: int, w_rev: int, alpha: float) -> np.ndarray: # Per-bar strategy log-returns for one configuration. features = build_features(w_mom, w_vol, w_rev) model = make_pipeline(StandardScaler(), Ridge(alpha=alpha)) model.fit(features, realised_next) # in-sample fit: exactly the overfit setup position = np.sign(model.predict(features)) return position * realised_next def per_bar_sharpe(returns: np.ndarray) -> float: std = returns.std(ddof=1) return float(returns.mean() / std) if std > 0 else 0.0 print(f"{n_obs} usable bars; in-sample {split}, out-of-sample {n_obs - split}") ``` -------------------------------- ### Import necessary libraries for Optuna and PurgedCV Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/backtest_overfitting_audit.ipynb Imports essential libraries for data manipulation, machine learning, hyperparameter optimization with Optuna, and backtest overfitting auditing with PurgedCV. Sets up constants for the backtesting and search process. ```python from pathlib import Path import numpy as np import optuna import pandas as pd from sklearn.linear_model import Ridge from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from purgedcv import ( CombinatorialPurgedCV, deflated_sharpe_ratio_full, effective_n_trials, path_metrics, probability_of_backtest_overfitting, ) from purgedcv.optuna_integration import TrialSharpeRecorder optuna.logging.set_verbosity(optuna.logging.WARNING) SEED = 0 BARS_PER_YEAR = 252 WARMUP = 60 # rows reserved so every config shares one length MAX_LOOKBACK = 40 # longest feature window the search may pick N_TRIALS = 400 TOP_K = 80 # configurations entered into the PBO matrix ``` -------------------------------- ### Read LCL Data or Generate Synthetic Data Source: https://github.com/eslazarev/purged-cross-validation/blob/main/examples/uk_smart_meter_lcl.ipynb Reads the LCL half-hourly energy consumption data from a CSV file if available. Otherwise, it generates a synthetic dataset with a similar structure for demonstration purposes. This setup is crucial for running the analysis when the original data is not accessible. ```python from pathlib import Path import numpy as np import pandas as pd from sklearn.ensemble import HistGradientBoostingRegressor from sklearn.model_selection import GroupKFold, KFold from purgedcv import WalkForwardSplit SEED = 0 N_HOUSEHOLDS = 30 # real LCL has ~5,500; smaller here so it runs fast DAYS = 120 # half-hourly => 48 slots/day HALF_HOURS = 48 H = 12 # forecast = mean demand over the next H half-hours (6h) ``` ```python DATA_DIR = Path("data") if Path("data").exists() else Path("examples/data") DATA_DIR.mkdir(parents=True, exist_ok=True) cache = DATA_DIR / "lcl_halfhourly.csv" if cache.exists(): df = pd.read_csv(cache, parse_dates=["tstp"]) df = df.rename(columns={"energy_kwh": "load"}).dropna(subset=["load"]) MODE = "REAL LCL" print( f"REAL LCL: {df['LCLid'].nunique()} households, {len(df):,} rows, " f"{df['tstp'].min().date()} to {df['tstp'].max().date()}" ) else: rng = np.random.default_rng(SEED) idx = pd.date_range("2013-01-01", periods=DAYS * HALF_HOURS, freq="30min") slot = np.arange(HALF_HOURS) daily = 0.35 + 0.9 * np.exp(-((slot - 15) ** 2) / 8) + 1.3 * np.exp(-((slot - 38) ** 2) / 10) dow = idx.dayofweek.to_numpy() weekly = np.where(dow >= 5, 1.25, 1.0) doy = idx.dayofyear.to_numpy() annual = 1.0 + 0.15 * np.cos(2 * np.pi * doy / 365.0) hh_slot = idx.hour.to_numpy() * 2 + (idx.minute.to_numpy() // 30) frames = [] for hh in range(N_HOUSEHOLDS): base = rng.lognormal(mean=0.0, sigma=0.4) signal = base * daily[hh_slot] * weekly * annual ar = np.zeros(len(idx)) e = rng.normal(0, 0.08, len(idx)) for t in range(1, len(idx)): ar[t] = 0.6 * ar[t - 1] + e[t] load = np.maximum(0.05, signal * (1.0 + ar)) frames.append(pd.DataFrame({"LCLid": f"MAC{hh:05d}", "tstp": idx, "load": load})) df = pd.concat(frames, ignore_index=True) MODE = "SYNTHETIC (LCL-structured)" print(f"SYNTHETIC fallback: {N_HOUSEHOLDS} households, {len(df):,} rows") ```