Download datasets.py from sparsetrace/dima: direct link, hf CLI and curl.
- Browser
- Download file 8.95 kB
-
https://huggingface.co/sparsetrace/dima/resolve/main/datasets.py
- Command line
-
hf download hf://sparsetrace/dima/datasets.py
-
curl -L -o datasets.py https://huggingface.co/sparsetrace/dima/resolve/main/datasets.py
8.95 kB
| # datasets.py | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import time | |
| import urllib.request | |
| from dataclasses import dataclass | |
| from typing import Iterable, Optional, Sequence, Tuple, Union | |
| import numpy as np | |
| try: | |
| import pandas as pd | |
| except Exception as e: # pragma: no cover | |
| raise ImportError("datasets.py requires pandas. Install with: pip install pandas") from e | |
| # ----------------------------- | |
| # Config | |
| # ----------------------------- | |
| ELECTRICITY_URL = "https://data.openei.org/files/8562/historic_load_hourly_2016_2023_county.h5" | |
| ELECTRICITY_H5 = "historic_load_hourly_2016_2023_county.h5" | |
| # ----------------------------- | |
| # Helpers | |
| # ----------------------------- | |
| def download_with_progress(url: str, out_path: str, chunk_size: int = 1024 * 1024) -> None: | |
| """ | |
| Download a large file with a simple text progress bar (no extra deps). | |
| Skips if file already exists. | |
| """ | |
| if os.path.exists(out_path): | |
| return | |
| os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True) | |
| print(f"[datasets] Downloading:\n {url}\n -> {out_path}") | |
| with urllib.request.urlopen(url) as response, open(out_path, "wb") as out_file: | |
| total = getattr(response, "length", None) | |
| downloaded = 0 | |
| start_time = time.time() | |
| while True: | |
| chunk = response.read(chunk_size) | |
| if not chunk: | |
| break | |
| out_file.write(chunk) | |
| downloaded += len(chunk) | |
| if total: | |
| frac = downloaded / total | |
| pct = frac * 100 | |
| bar_len = 30 | |
| filled = int(bar_len * frac) | |
| bar = "#" * filled + "-" * (bar_len - filled) | |
| rate = downloaded / (time.time() - start_time + 1e-9) | |
| sys.stdout.write( | |
| f"\r[{bar}] {pct:6.2f}% " | |
| f"{downloaded / 1e6:8.1f} / {total / 1e6:8.1f} MB " | |
| f"{rate / 1e6:4.1f} MB/s" | |
| ) | |
| sys.stdout.flush() | |
| if total: | |
| print("\n[datasets] Download complete.") | |
| else: | |
| print("[datasets] Download complete.") | |
| def _safe_cache_name( | |
| *, | |
| start: Optional[str], | |
| stop: Optional[str], | |
| resample: Optional[str], | |
| agg: str, | |
| n_features: Optional[int], | |
| feature_method: str, | |
| seed: int, | |
| downcast_float32: bool, | |
| ) -> str: | |
| def clean(x: Optional[str]) -> str: | |
| if x is None: | |
| return "none" | |
| return str(x).replace(":", "-").replace("/", "-").replace(" ", "_") | |
| return ( | |
| "electricity" | |
| f"_start-{clean(start)}" | |
| f"_stop-{clean(stop)}" | |
| f"_res-{clean(resample)}" | |
| f"_agg-{agg}" | |
| f"_nf-{n_features if n_features is not None else 'all'}" | |
| f"_fsel-{feature_method}" | |
| f"_seed-{int(seed)}" | |
| f"_f32-{int(bool(downcast_float32))}" | |
| ".npz" | |
| ) | |
| def _require_tables_hint(err: Exception) -> None: | |
| msg = ( | |
| "\n[datasets] Failed to read .h5. This usually means the optional dependency `tables` is missing.\n" | |
| "Install once to build a cache:\n" | |
| " pip install tables\n" | |
| "or, if using extras:\n" | |
| " pip install .[electricity]\n" | |
| ) | |
| raise ImportError(msg) from err | |
| def _coerce_datetime_index(df: pd.DataFrame) -> pd.DataFrame: | |
| if not isinstance(df.index, pd.DatetimeIndex): | |
| df.index = pd.to_datetime(df.index, utc=True) | |
| if df.index.tz is None: | |
| df.index = df.index.tz_localize("UTC") | |
| return df.sort_index() | |
| def _fill_missing(df: pd.DataFrame) -> pd.DataFrame: | |
| # Conservative: forward-fill then back-fill; if still NaN, fill 0. | |
| df = df.ffill().bfill() | |
| if df.isna().values.any(): | |
| df = df.fillna(0.0) | |
| return df | |
| def _select_features( | |
| df: pd.DataFrame, | |
| *, | |
| features: Optional[Sequence[str]] = None, | |
| n_features: Optional[int] = None, | |
| method: str = "variance", | |
| seed: int = 0, | |
| ) -> pd.DataFrame: | |
| """ | |
| Feature selection on columns. | |
| - features: explicit list of column labels | |
| - n_features: choose subset | |
| method: "variance" (top variance) or "random" | |
| """ | |
| if features is not None: | |
| missing = [c for c in features if c not in df.columns] | |
| if missing: | |
| raise KeyError(f"Requested features not found in columns: {missing[:10]}{'...' if len(missing)>10 else ''}") | |
| return df.loc[:, list(features)] | |
| if n_features is None: | |
| return df | |
| n_features = int(n_features) | |
| if n_features <= 0: | |
| raise ValueError("n_features must be positive") | |
| if n_features >= df.shape[1]: | |
| return df | |
| method = method.lower() | |
| if method == "random": | |
| rng = np.random.default_rng(int(seed)) | |
| cols = list(df.columns) | |
| idx = rng.choice(len(cols), size=n_features, replace=False) | |
| sel = [cols[i] for i in idx] | |
| return df.loc[:, sel] | |
| if method == "variance": | |
| # top variance across time (after missing fill) | |
| v = df.var(axis=0, ddof=0) | |
| sel = list(v.sort_values(ascending=False).index[:n_features]) | |
| return df.loc[:, sel] | |
| raise ValueError("feature_method must be one of: {'variance','random'}") | |
| # ----------------------------- | |
| # Main API: Electricity | |
| # ----------------------------- | |
| class ElectricityData: | |
| R_tX: np.ndarray | |
| t_index: np.ndarray # int64 UTC ns | |
| X_index: np.ndarray # dtype=object strings | |
| def load_electricity( | |
| *, | |
| data_dir: str = "data", | |
| url: str = ELECTRICITY_URL, | |
| h5_name: str = ELECTRICITY_H5, | |
| # slicing | |
| start: Optional[str] = None, # e.g. "2019-01-01" | |
| stop: Optional[str] = None, # e.g. "2020-01-01" | |
| # resampling | |
| resample: Optional[str] = None, # e.g. "D" for daily, "W" weekly; None keeps hourly | |
| agg: str = "sum", # "sum" or "mean" | |
| # feature downselect | |
| features: Optional[Sequence[str]] = None, | |
| n_features: Optional[int] = None, | |
| feature_method: str = "variance", # "variance" or "random" | |
| seed: int = 0, | |
| # dtype / missing | |
| downcast_float32: bool = True, | |
| # caching | |
| cache: bool = True, | |
| cache_dir: Optional[str] = None, | |
| force_rebuild_cache: bool = False, | |
| ) -> ElectricityData: | |
| """ | |
| Returns a manageable (T, X) array from the OpenEI historic county load dataset. | |
| Behavior: | |
| - If a matching .npz cache exists -> loads it (NO `tables` required). | |
| - Else downloads .h5 and reads via pandas (requires `tables` installed once), | |
| then applies slicing/resampling/feature-selection, and optionally writes cache. | |
| Notes: | |
| - index is returned as int64 UTC nanoseconds (portable, no pandas dependency later). | |
| - columns are returned as strings (county IDs like 'p36041'). | |
| """ | |
| data_dir = str(data_dir) | |
| os.makedirs(data_dir, exist_ok=True) | |
| if cache_dir is None: | |
| cache_dir = os.path.join(data_dir, "cache") | |
| os.makedirs(cache_dir, exist_ok=True) | |
| agg = agg.lower() | |
| if agg not in ("sum", "mean"): | |
| raise ValueError("agg must be 'sum' or 'mean'") | |
| cache_name = _safe_cache_name( | |
| start=start, stop=stop, resample=resample, agg=agg, | |
| n_features=n_features, feature_method=feature_method, | |
| seed=seed, downcast_float32=downcast_float32, | |
| ) | |
| cache_path = os.path.join(cache_dir, cache_name) | |
| # 1) Load cache if present | |
| if cache and (not force_rebuild_cache) and os.path.exists(cache_path): | |
| z = np.load(cache_path, allow_pickle=True) | |
| R_tX = z["R_tX"] | |
| t_index = z["t_index"] | |
| X_index = z["X_index"] | |
| return ElectricityData(R_tX=R_tX, t_index=t_index, X_index=X_index) | |
| # 2) Ensure H5 present | |
| h5_path = os.path.join(data_dir, h5_name) | |
| download_with_progress(url, h5_path) | |
| # 3) Read H5 (requires tables) | |
| try: | |
| df = pd.read_hdf(h5_path) | |
| except Exception as e: | |
| _require_tables_hint(e) | |
| # 4) Basic preprocess | |
| df = _coerce_datetime_index(df) | |
| df = _fill_missing(df) | |
| # 5) Time slice | |
| if start is not None or stop is not None: | |
| df = df.loc[start:stop] | |
| # 6) Resample | |
| if resample is not None: | |
| if agg == "sum": | |
| df = df.resample(resample).sum() | |
| else: | |
| df = df.resample(resample).mean() | |
| # 7) Feature selection | |
| df = _select_features(df, features=features, n_features=n_features, method=feature_method, seed=seed) | |
| # 8) Downcast | |
| if downcast_float32: | |
| df = df.astype("float32", copy=False) | |
| # 9) Build outputs | |
| R_tX = df.to_numpy() | |
| t_index = df.index.view("int64") # UTC ns | |
| X_index = np.asarray(df.columns.astype(str), dtype=object) | |
| # 10) Cache | |
| if cache: | |
| np.savez_compressed(cache_path, R_tX=R_tX, t_index=t_index, X_index=X_index) | |
| print(f"[datasets] Wrote cache: {cache_path}") | |
| return ElectricityData(R_tX=R_tX, t_index=t_index, X_index=X_index) | |