From b85d9e29af1b39d6ac3970492c17db2575115658 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 9 Jul 2026 15:22:34 +0000 Subject: [PATCH 1/3] ENH Add P2S (Production Press Sensor Data) classification dataset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the `P2S` dataset (`AIML-TUDA/P2S` on the Hugging Face Hub) as a new time-series classification task. P2S contains force-sensor recordings from a metal stamping / deep-drawing production press; the task is binary classification — predict whether a press run produced a *normal* (0) or a *defective* (1) part from its 4096-step sensor series. The Hub ships two variants: **Normal** (honest train/test split) and **Decoy** (deliberately correlates production speed with the label to probe confounder reliance). This dataset loads **only the Normal variant**. Implementation - New `datasets/p2s.py` producing the benchmark's classification contract: `X_train`/`X_test` as lists of `(4096, 1)` float32 series, `y_train`/`y_test` int64 labels, `task="classification"`, metrics `["accuracy", "balanced_accuracy", "f1_weighted"]`, `n_classes=2`. - Loads via `pd.read_parquet("hf://…", columns=[...])`, reusing the existing Hugging Face pattern from `datasets/enedis.py` (`requirements = ["pip::huggingface_hub", "fsspec"]`, module-top imports as benchopt requirement checks). Only the sensor series and label columns are read; the run-`speed` and annotation-`mask` (confounder) columns are skipped, which also avoids downloading the large mask column. - `variant` parameter is templated (defaults to `["Normal"]`) so Decoy is a one-line addition later; `debug` keeps the first 20 samples per split via deterministic slicing, and `test_parameters` runs a tiny config. Notes - P2S is a **gated** dataset: reading it requires accepting the terms on the Hub and a Hugging Face token in the environment (`HF_TOKEN` or a cached `huggingface-cli login`). Accordingly, `p2s` is added to `_CI_FLAKY_DATASETS` in `test_config.py` so it is skipped in CI (which has no token) and runs locally. Verification - `get_data()` returns the expected shapes/dtypes; full splits match the dataset card (train n=1106, test n=1158; labels {0, 1}; n_classes=2). - `benchopt run . -d "P2S[debug=True]" -s Naive -n 1` completes with all three classification metrics finite. - `ruff check` and `ruff format --check` pass. Co-Authored-By: Claude Opus 4.8 --- datasets/p2s.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ test_config.py | 5 ++- 2 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 datasets/p2s.py diff --git a/datasets/p2s.py b/datasets/p2s.py new file mode 100644 index 0000000..ff314fd --- /dev/null +++ b/datasets/p2s.py @@ -0,0 +1,110 @@ +"""P2S (Production Press Sensor Data) time-series classification dataset. + +Wraps the gated ``AIML-TUDA/P2S`` dataset on the Hugging Face Hub. P2S contains +force-sensor recordings from a metal stamping / deep-drawing production press; +the task is binary classification — predict whether a press run produced a +*normal* (``0``) or a *defective* (``1``) part from its 4096-step sensor series. + +The Hub ships two variants, each with a ``train`` and ``test`` split: + +- **Normal** — the honest split (train/test share the same speed distribution). +- **Decoy** — deliberately correlates production speed with the label to probe + whether a model latches onto that confounder. + +We load **only the Normal variant**. The Hub stores, per row, the sensor series +(``dowel_deep_drawing_ow``, 4096 steps), the label, the run ``speed`` and an +annotation ``mask`` marking speed-affected intervals. We use only the sensor +series and the label; ``speed`` and ``mask`` (the confounder machinery) are +ignored. + +Authentication +-------------- +This dataset is **gated**: reading it requires accepting the terms on the Hub +and a Hugging Face token in the environment (``HF_TOKEN`` or a cached +``huggingface-cli login``). The dataset does not read tokens itself. + +Data contract output +-------------------- +X_train : List[np.ndarray (4096, 1)] one series per training sample +y_train : np.ndarray (N,) int class labels (0/1) +X_test : List[np.ndarray (4096, 1)] +y_test : np.ndarray (M,) int +task : "classification" +metrics : ["accuracy", "balanced_accuracy", "f1_weighted"] +n_classes : 2 +""" + +import numpy as np +import pandas as pd +from benchopt import BaseDataset + +# Requirement checks +import fsspec # noqa: F401 +from huggingface_hub import hf_hub_download # noqa: F401 + +# Columns to read from the parquet: the sensor series and the label. The run +# ``speed`` and annotation ``mask`` columns are the confounder machinery and are +# intentionally skipped (also avoids downloading the large ``mask`` column). +_SENSOR_COL = "dowel_deep_drawing_ow" +_LABEL_COL = "label" + +_HF_PARQUET = "hf://datasets/AIML-TUDA/P2S/{variant}/{split}-00000-of-00001.parquet" + +# Number of samples kept per split in ``debug`` mode. +_DEBUG_N = 20 + + +class Dataset(BaseDataset): + """P2S press-sensor binary classification dataset (Normal variant). + + Parameters + ---------- + variant : str + Which Hub variant to load. Only ``"Normal"`` is shipped; the path is + templated so ``"Decoy"`` is a one-line addition if ever needed. + debug : bool + If True, keep only the first 20 samples of each split for fast testing. + """ + + name = "P2S" + + requirements = ["pip::huggingface_hub", "fsspec"] + + parameters = { + "variant": ["Normal"], + "debug": [False], + } + + test_parameters = { + "variant": ["Normal"], + "debug": [True], + } + + def _load_split(self, split): + """Read one split into a list of ``(4096, 1)`` series and int labels.""" + url = _HF_PARQUET.format(variant=self.variant, split=split) + df = pd.read_parquet(url, columns=[_SENSOR_COL, _LABEL_COL]) + + if self.debug: + df = df.head(_DEBUG_N) + + X = [ + np.asarray(cell, dtype=np.float32).reshape(-1, 1) + for cell in df[_SENSOR_COL] + ] + y = df[_LABEL_COL].to_numpy(dtype=np.int64) + return X, y + + def get_data(self): + X_train, y_train = self._load_split("train") + X_test, y_test = self._load_split("test") + + return dict( + X_train=X_train, + y_train=y_train, + X_test=X_test, + y_test=y_test, + task="classification", + metrics=["accuracy", "balanced_accuracy", "f1_weighted"], + n_classes=2, + ) diff --git a/test_config.py b/test_config.py index cc0566d..80399b3 100644 --- a/test_config.py +++ b/test_config.py @@ -12,8 +12,9 @@ # These datasets load fine locally, but their download hosts block / rate-limit # CI runners (ucr: timeseriesclassification.com -> HTTP 401; mitdb: download -# timeout). So we run them locally and skip them *only in CI*. -_CI_FLAKY_DATASETS = {"ucr", "mitdb"} +# timeout) or require credentials CI does not have (p2s: gated Hugging Face +# dataset needing an HF token). So we run them locally and skip them *only in CI*. +_CI_FLAKY_DATASETS = {"ucr", "mitdb", "p2s"} def _skip_flaky_in_ci(name): From 399e87f6eaf3f41143059bd702050ec712f1dbd6 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 9 Jul 2026 21:16:15 +0000 Subject: [PATCH 2/3] REFACTOR Simplify P2S docs and inline single-use constants Trim the verbose module/class docstrings to match sibling datasets and inline _SENSOR_COL, _LABEL_COL, _HF_PARQUET and _DEBUG_N at their single use site in _load_split. Co-Authored-By: Claude Opus 4.8 --- datasets/p2s.py | 55 ++++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/datasets/p2s.py b/datasets/p2s.py index ff314fd..b4fcf96 100644 --- a/datasets/p2s.py +++ b/datasets/p2s.py @@ -1,27 +1,12 @@ -"""P2S (Production Press Sensor Data) time-series classification dataset. +"""P2S (Production Press Sensor Data) time series classification dataset. -Wraps the gated ``AIML-TUDA/P2S`` dataset on the Hugging Face Hub. P2S contains -force-sensor recordings from a metal stamping / deep-drawing production press; -the task is binary classification — predict whether a press run produced a -*normal* (``0``) or a *defective* (``1``) part from its 4096-step sensor series. +Wraps the gated ``AIML-TUDA/P2S`` Hugging Face dataset (Normal variant): binary +classification of whether a deep-drawing press run produced a normal (``0``) or +defective (``1``) part from its 4096-step force-sensor series. -The Hub ships two variants, each with a ``train`` and ``test`` split: - -- **Normal** — the honest split (train/test share the same speed distribution). -- **Decoy** — deliberately correlates production speed with the label to probe - whether a model latches onto that confounder. - -We load **only the Normal variant**. The Hub stores, per row, the sensor series -(``dowel_deep_drawing_ow``, 4096 steps), the label, the run ``speed`` and an -annotation ``mask`` marking speed-affected intervals. We use only the sensor -series and the label; ``speed`` and ``mask`` (the confounder machinery) are -ignored. - -Authentication --------------- -This dataset is **gated**: reading it requires accepting the terms on the Hub -and a Hugging Face token in the environment (``HF_TOKEN`` or a cached -``huggingface-cli login``). The dataset does not read tokens itself. +This dataset is gated: reading it requires accepting the terms on the Hub and a +Hugging Face token in the environment (``HF_TOKEN`` or a cached +``huggingface-cli login``). Data contract output -------------------- @@ -42,17 +27,6 @@ import fsspec # noqa: F401 from huggingface_hub import hf_hub_download # noqa: F401 -# Columns to read from the parquet: the sensor series and the label. The run -# ``speed`` and annotation ``mask`` columns are the confounder machinery and are -# intentionally skipped (also avoids downloading the large ``mask`` column). -_SENSOR_COL = "dowel_deep_drawing_ow" -_LABEL_COL = "label" - -_HF_PARQUET = "hf://datasets/AIML-TUDA/P2S/{variant}/{split}-00000-of-00001.parquet" - -# Number of samples kept per split in ``debug`` mode. -_DEBUG_N = 20 - class Dataset(BaseDataset): """P2S press-sensor binary classification dataset (Normal variant). @@ -60,8 +34,7 @@ class Dataset(BaseDataset): Parameters ---------- variant : str - Which Hub variant to load. Only ``"Normal"`` is shipped; the path is - templated so ``"Decoy"`` is a one-line addition if ever needed. + Which Hub variant to load. Only ``"Normal"`` is shipped. debug : bool If True, keep only the first 20 samples of each split for fast testing. """ @@ -82,17 +55,19 @@ class Dataset(BaseDataset): def _load_split(self, split): """Read one split into a list of ``(4096, 1)`` series and int labels.""" - url = _HF_PARQUET.format(variant=self.variant, split=split) - df = pd.read_parquet(url, columns=[_SENSOR_COL, _LABEL_COL]) + url = ( + f"hf://datasets/AIML-TUDA/P2S/{self.variant}/{split}-00000-of-00001.parquet" + ) + df = pd.read_parquet(url, columns=["dowel_deep_drawing_ow", "label"]) if self.debug: - df = df.head(_DEBUG_N) + df = df.head(20) X = [ np.asarray(cell, dtype=np.float32).reshape(-1, 1) - for cell in df[_SENSOR_COL] + for cell in df["dowel_deep_drawing_ow"] ] - y = df[_LABEL_COL].to_numpy(dtype=np.int64) + y = df["label"].to_numpy(dtype=np.int64) return X, y def get_data(self): From 3573606b769e095ad4f4e13dbd5eeb23d96300f3 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 10 Jul 2026 08:41:41 +0000 Subject: [PATCH 3/3] ENH P2S no longer gated: drop gated docs and enable in CI The AIML-TUDA/P2S dataset is now public, so no HF token is needed. Remove the gating notes from the docstring and drop p2s from the CI-skip set so it runs as part of the CI dataset tests. Co-Authored-By: Claude Opus 4.8 --- datasets/p2s.py | 6 +----- test_config.py | 5 ++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/datasets/p2s.py b/datasets/p2s.py index b4fcf96..5e9a155 100644 --- a/datasets/p2s.py +++ b/datasets/p2s.py @@ -1,13 +1,9 @@ """P2S (Production Press Sensor Data) time series classification dataset. -Wraps the gated ``AIML-TUDA/P2S`` Hugging Face dataset (Normal variant): binary +Wraps the ``AIML-TUDA/P2S`` Hugging Face dataset (Normal variant): binary classification of whether a deep-drawing press run produced a normal (``0``) or defective (``1``) part from its 4096-step force-sensor series. -This dataset is gated: reading it requires accepting the terms on the Hub and a -Hugging Face token in the environment (``HF_TOKEN`` or a cached -``huggingface-cli login``). - Data contract output -------------------- X_train : List[np.ndarray (4096, 1)] one series per training sample diff --git a/test_config.py b/test_config.py index 80399b3..cc0566d 100644 --- a/test_config.py +++ b/test_config.py @@ -12,9 +12,8 @@ # These datasets load fine locally, but their download hosts block / rate-limit # CI runners (ucr: timeseriesclassification.com -> HTTP 401; mitdb: download -# timeout) or require credentials CI does not have (p2s: gated Hugging Face -# dataset needing an HF token). So we run them locally and skip them *only in CI*. -_CI_FLAKY_DATASETS = {"ucr", "mitdb", "p2s"} +# timeout). So we run them locally and skip them *only in CI*. +_CI_FLAKY_DATASETS = {"ucr", "mitdb"} def _skip_flaky_in_ci(name):