Yoge-2004's picture YOGESHWARAN's picture
Compact Space history before deployment
3356253
Raw History Blame Contribute Delete
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)