Download ml/src/expense_ml/data/sampling.py from Yoge-2004/expense-tracker-backend: direct link, hf CLI and curl.
- Browser
- Download file 3.15 kB
-
https://huggingface.co/spaces/Yoge-2004/expense-tracker-backend/resolve/main/ml/src/expense_ml/data/sampling.py
- Command line
-
hf download hf://spaces/Yoge-2004/expense-tracker-backend/ml/src/expense_ml/data/sampling.py
-
curl -L -o sampling.py https://huggingface.co/spaces/Yoge-2004/expense-tracker-backend/resolve/main/ml/src/expense_ml/data/sampling.py
3.15 kB
| from __future__ import annotations | |
| import math | |
| import pandas as pd | |
| def balanced_training_sample(frame: pd.DataFrame, max_rows: int, seed: int) -> pd.DataFrame: | |
| """Deterministically sample across country/source/class groups without replacement. | |
| Every label present in the input is guaranteed to remain represented when the | |
| requested sample can contain at least one row per label. | |
| """ | |
| if max_rows < 1: | |
| raise ValueError("max_rows must be >= 1") | |
| if frame.empty: | |
| return frame.copy().reset_index(drop=True) | |
| labels = frame["label"].dropna().astype(str).unique().tolist() if "label" in frame.columns else [] | |
| if labels and max_rows < len(labels): | |
| raise ValueError( | |
| f"max_rows={max_rows} is too small to preserve all {len(labels)} training labels." | |
| ) | |
| if len(frame) <= max_rows: | |
| return frame.sample(frac=1.0, random_state=seed).reset_index(drop=True) | |
| group_columns = [column for column in ("country", "source", "label") if column in frame.columns] | |
| if not group_columns: | |
| selected = frame.sample(n=max_rows, random_state=seed) | |
| else: | |
| grouped = frame.groupby(group_columns, dropna=False, sort=False) | |
| per_group_cap = max(1, math.ceil(max_rows / grouped.ngroups)) | |
| sampled_indices: list[int] = [] | |
| for _, group in grouped: | |
| n = min(len(group), per_group_cap) | |
| sampled_indices.extend(group.sample(n=n, random_state=seed).index.tolist()) | |
| selected = frame.loc[sampled_indices] | |
| if len(selected) > max_rows: | |
| # Downsample while explicitly retaining one representative per class. | |
| keep_indices = [] | |
| for _, group in selected.groupby("label", sort=False): | |
| keep_indices.append(group.sample(n=1, random_state=seed).index[0]) | |
| keep_index_set = set(keep_indices) | |
| remainder = selected.drop(index=keep_index_set) | |
| needed_remainder = max_rows - len(keep_indices) | |
| selected = pd.concat( | |
| [selected.loc[keep_indices], remainder.sample(n=needed_remainder, random_state=seed + 1)], | |
| ignore_index=False, | |
| ) | |
| elif len(selected) < max_rows: | |
| remaining = frame.drop(index=selected.index) | |
| needed = min(max_rows - len(selected), len(remaining)) | |
| if needed: | |
| selected = pd.concat( | |
| [selected, remaining.sample(n=needed, random_state=seed + 1)], | |
| ignore_index=False, | |
| ) | |
| if len(selected) != max_rows: | |
| raise AssertionError("Balanced sampler failed to produce the requested number of unique rows.") | |
| if selected.index.has_duplicates: | |
| raise AssertionError("Balanced sampler selected the same source row more than once.") | |
| if labels and not set(labels).issubset(set(selected["label"].astype(str))): | |
| missing = sorted(set(labels) - set(selected["label"].astype(str))) | |
| raise AssertionError(f"Balanced sampler dropped training labels: {missing}") | |
| return selected.sample(frac=1.0, random_state=seed).reset_index(drop=True) | |