datapruning 2.1.4__tar.gz → 2.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {datapruning-2.1.4/datapruning.egg-info → datapruning-2.1.6}/PKG-INFO +1 -1
  2. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/base.py +59 -35
  3. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/margin_scorer.py +2 -2
  4. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/sliding_window.py +35 -20
  5. {datapruning-2.1.4 → datapruning-2.1.6/datapruning.egg-info}/PKG-INFO +1 -1
  6. {datapruning-2.1.4 → datapruning-2.1.6}/pyproject.toml +1 -1
  7. {datapruning-2.1.4 → datapruning-2.1.6}/LICENSE +0 -0
  8. {datapruning-2.1.4 → datapruning-2.1.6}/MANIFEST.in +0 -0
  9. {datapruning-2.1.4 → datapruning-2.1.6}/README.md +0 -0
  10. {datapruning-2.1.4 → datapruning-2.1.6}/build_src/_core.c +0 -0
  11. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/__init__.py +0 -0
  12. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/__init__.py +0 -0
  13. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/el2n_scorer.py +0 -0
  14. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/fusion_pipeline.py +0 -0
  15. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/kcenter_select.py +0 -0
  16. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/oof_utils.py +0 -0
  17. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/algorithms/registry.py +0 -0
  18. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/__init__.py +0 -0
  19. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/benchmark.py +0 -0
  20. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/explainability.py +0 -0
  21. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/intelligence.py +0 -0
  22. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/optimizer.py +0 -0
  23. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/scanner.py +0 -0
  24. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/strategy_display.py +0 -0
  25. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/engine/strategy_selector.py +0 -0
  26. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/multimodal/__init__.py +0 -0
  27. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/multimodal/clip_encoder.py +0 -0
  28. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/pipeline.py +0 -0
  29. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/reports/__init__.py +0 -0
  30. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/reports/exporter.py +0 -0
  31. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/__init__.py +0 -0
  32. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/__init__.py +0 -0
  33. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/compare.py +0 -0
  34. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/evaluation.py +0 -0
  35. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/benchmark/synthetic.py +0 -0
  36. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/difficulty.py +0 -0
  37. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/embeddings/__init__.py +0 -0
  38. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/embeddings/torch_utils.py +0 -0
  39. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/fusion.py +0 -0
  40. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/noise.py +0 -0
  41. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/normalize.py +0 -0
  42. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/pipeline.py +0 -0
  43. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/redundancy.py +0 -0
  44. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/__init__.py +0 -0
  45. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/aum.py +0 -0
  46. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/el2n.py +0 -0
  47. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/forgetting.py +0 -0
  48. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/grand.py +0 -0
  49. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/scores/margin.py +0 -0
  50. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selection.py +0 -0
  51. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/__init__.py +0 -0
  52. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/facility_location.py +0 -0
  53. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/kcenter.py +0 -0
  54. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/selectors/out_of_fold.py +0 -0
  55. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/research_pruning/strategy.py +0 -0
  56. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning/sdk.py +0 -0
  57. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/SOURCES.txt +0 -0
  58. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/dependency_links.txt +0 -0
  59. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/requires.txt +0 -0
  60. {datapruning-2.1.4 → datapruning-2.1.6}/datapruning.egg-info/top_level.txt +0 -0
  61. {datapruning-2.1.4 → datapruning-2.1.6}/setup.cfg +0 -0
  62. {datapruning-2.1.4 → datapruning-2.1.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datapruning
3
- Version: 2.1.4
3
+ Version: 2.1.6
4
4
  Summary: Intelligent dataset optimization for cleaner, smaller training data
5
5
  License: Proprietary
6
6
  Project-URL: Homepage, https://www.datapruning.com
@@ -54,53 +54,59 @@ class BaseOptimizer(ABC):
54
54
  X: pd.DataFrame,
55
55
  y: pd.Series,
56
56
  keep_ratio: float,
57
- n_random: int = 3,
58
- test_size: float = 0.3,
57
+ n_random: int = 5,
58
+ n_splits: int = 3,
59
59
  seed: int = 0,
60
60
  ) -> bool:
61
- """Check whether this strategy beats random on an internal holdout.
61
+ """Check whether this strategy beats random on internal holdouts.
62
62
 
63
- Splits the data 70/30, trains the strategy on the 70% subset,
64
- validates on the 30% holdout, and compares with random subsets.
65
- Returns True if the strategy is beneficial.
63
+ Runs n_splits stratified train/val splits, each with n_random
64
+ random baseline subsets. The strategy is beneficial only if its
65
+ mean accuracy exceeds random mean by at least half the random
66
+ standard deviation — removing noisy single-split false positives.
66
67
  """
67
- from sklearn.model_selection import train_test_split
68
+ from sklearn.model_selection import StratifiedKFold
68
69
  from sklearn.linear_model import LogisticRegression
69
70
 
70
71
  if len(y) < 50:
71
72
  return True
72
- y_vals = y.values if hasattr(y, 'values') else np.asarray(y)
73
+ y_vals = y.values if hasattr(y, "values") else np.asarray(y)
73
74
  n_unique = len(np.unique(y_vals))
74
75
  if n_unique < 2 or n_unique >= len(y_vals) * 0.8:
75
76
  return True
76
77
 
77
- tr_idx, va_idx = train_test_split(
78
- np.arange(len(y)), test_size=test_size,
79
- random_state=seed, stratify=y.values,
80
- )
81
- X_tr, y_tr = X.iloc[tr_idx], y.iloc[tr_idx]
82
- X_va, y_va = X.iloc[va_idx], y.iloc[va_idx]
83
- n_keep = max(1, int(len(tr_idx) * keep_ratio))
78
+ strat_accs_all = []
79
+ random_accs_all = []
84
80
 
85
81
  try:
86
- self.fit(X_tr, y_tr)
87
- s_keep = self._select_rows(X_tr, y_tr, keep_ratio)
88
- m = LogisticRegression(max_iter=300, C=0.1, random_state=seed)
89
- m.fit(X_tr.loc[s_keep], y_tr.loc[s_keep])
90
- strat_acc = m.score(X_va, y_va)
91
-
92
- rng = np.random.RandomState(seed)
93
- random_accs = []
94
- for s in range(n_random):
95
- r_idx = rng.choice(len(tr_idx), n_keep, replace=False)
96
- r_keep = X_tr.index[r_idx]
97
- m2 = LogisticRegression(max_iter=300, C=0.1, random_state=seed)
98
- m2.fit(X_tr.loc[r_keep], y_tr.loc[r_keep])
99
- random_accs.append(m2.score(X_va, y_va))
100
-
101
- return strat_acc >= np.mean(random_accs)
82
+ skf = StratifiedKFold(
83
+ n_splits=n_splits, shuffle=True, random_state=seed
84
+ )
85
+ for tr_idx, va_idx in skf.split(np.arange(len(y)), y.values):
86
+ X_tr, y_tr = X.iloc[tr_idx], y.iloc[tr_idx]
87
+ X_va, y_va = X.iloc[va_idx], y.iloc[va_idx]
88
+ n_keep = max(1, int(len(tr_idx) * keep_ratio))
89
+
90
+ self.fit(X_tr, y_tr)
91
+ s_keep = self._select_rows(X_tr, y_tr, keep_ratio)
92
+ m = LogisticRegression(max_iter=300, C=0.1, random_state=seed)
93
+ m.fit(X_tr.loc[s_keep], y_tr.loc[s_keep])
94
+ strat_accs_all.append(m.score(X_va, y_va))
95
+
96
+ rng = np.random.RandomState(seed + len(strat_accs_all))
97
+ for _ in range(n_random):
98
+ r_idx = rng.choice(len(tr_idx), n_keep, replace=False)
99
+ r_keep = X_tr.index[r_idx]
100
+ m2 = LogisticRegression(max_iter=300, C=0.1, random_state=seed)
101
+ m2.fit(X_tr.loc[r_keep], y_tr.loc[r_keep])
102
+ random_accs_all.append(m2.score(X_va, y_va))
103
+
104
+ mean_strat = np.mean(strat_accs_all)
105
+ mean_random = np.mean(random_accs_all)
106
+ std_random = np.std(random_accs_all)
107
+ return mean_strat > mean_random + std_random * 0.5
102
108
  except Exception:
103
- return True
109
+ return False
104
110
 
105
111
  def _random_select(self, X: pd.DataFrame, y: pd.Series, keep_ratio: float, seed: int = 42) -> pd.Index:
106
112
  n_target = max(1, int(len(y) * keep_ratio))
@@ -131,9 +137,27 @@ class BaseOptimizer(ABC):
131
137
 
132
138
  beneficial = self._beat_random_gate(X, y, keep_ratio)
133
139
  if not beneficial:
134
- keep_idx = self._random_select(X, y, keep_ratio)
135
- X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
136
- strategy_method = "random (fallback)"
140
+ if keep_ratio < 0.25 and self.key != "kcenter":
141
+ try:
142
+ from datapruning.algorithms.kcenter_select import KCenterSelectOptimizer
143
+ kcenter = KCenterSelectOptimizer()
144
+ kcenter_beneficial = kcenter._beat_random_gate(X, y, keep_ratio)
145
+ if kcenter_beneficial:
146
+ kcenter.fit(X, y)
147
+ X_res, y_res = kcenter.transform(X, y, keep_ratio)
148
+ strategy_method = "Diversity Selection (auto)"
149
+ else:
150
+ keep_idx = self._random_select(X, y, keep_ratio)
151
+ X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
152
+ strategy_method = "random (fallback)"
153
+ except Exception:
154
+ keep_idx = self._random_select(X, y, keep_ratio)
155
+ X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
156
+ strategy_method = "random (fallback)"
157
+ else:
158
+ keep_idx = self._random_select(X, y, keep_ratio)
159
+ X_res, y_res = X.loc[keep_idx], y.loc[keep_idx]
160
+ strategy_method = "random (fallback)"
137
161
  else:
138
162
  strategy_method = self.display_name
139
163
 
@@ -43,9 +43,9 @@ class MarginScorerOptimizer(BaseOptimizer):
43
43
  n_total_target = max(1, int(len(y) * keep_ratio))
44
44
  n_minority = min(len(minority_idx), int(n_total_target * 0.8))
45
45
  n_majority_target = max(n_total_target - n_minority, 0)
46
- majority_scores = self._scores.loc[majority_idx].sort_values(ascending=True)
46
+ majority_scores = self._scores.loc[majority_idx].sort_values(ascending=False)
47
47
  kept_majority_idx = majority_scores.index[:n_majority_target]
48
- kept_minority_idx = minority_idx[:n_minority]
48
+ kept_minority_idx = self._scores.loc[minority_idx].sort_values(ascending=False).index[:n_minority]
49
49
  return kept_minority_idx.append(kept_majority_idx)
50
50
 
51
51
  def why_selected(self) -> str:
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
  import numpy as np
3
3
  import pandas as pd
4
4
  from datapruning.algorithms.oof_utils import compute_oof_logits, logits_to_el2n
5
- from datapruning.research_pruning.selection import sliding_window_select
6
5
  from datapruning.algorithms.base import BaseOptimizer
7
6
  from datapruning.algorithms.registry import register
8
7
 
@@ -32,29 +31,45 @@ class SlidingWindowOptimizer(BaseOptimizer):
32
31
  def _select_rows(self, X: pd.DataFrame, y: pd.Series, keep_ratio: float) -> pd.Index:
33
32
  if self._scores is None:
34
33
  self.fit(X, y)
34
+
35
35
  y_arr = y.values
36
36
  classes = np.unique(y_arr)
37
- low_pct = max(0, (1 - keep_ratio) * 100 / 2)
38
- high_pct = min(100, 100 - low_pct)
37
+ n = len(y_arr)
38
+ n_target = max(1, int(n * keep_ratio))
39
+
39
40
  if len(classes) < 2:
40
- n_target = max(1, int(len(y) * keep_ratio))
41
41
  return y.sample(n=n_target, random_state=42).index
42
- selected = sliding_window_select(
43
- self._scores,
44
- low_percentile=low_pct,
45
- high_percentile=high_pct,
46
- labels=y_arr,
47
- preserve_minority=True,
48
- )
49
- minority_class = y.value_counts().idxmin()
50
- minority_idx = y[y == minority_class].index
51
- selected_idx = X.index[selected]
52
- all_idx = selected_idx.union(minority_idx).unique()
53
- n_target = max(1, int(len(y) * keep_ratio))
54
- if len(all_idx) > n_target:
55
- scores_at_idx = pd.Series(self._scores, index=X.index).loc[all_idx]
56
- all_idx = scores_at_idx.sort_values(ascending=True).index[:n_target]
57
- return all_idx
42
+
43
+ scores = self._scores
44
+ order = np.argsort(scores)
45
+ drop_n = max(1, int(n * 0.10))
46
+ pool = order[:n - drop_n]
47
+
48
+ floor_per_class = max(1, int(n_target * 0.5 / len(classes)))
49
+ selected = set()
50
+
51
+ for c in classes:
52
+ c_mask = y_arr[pool] == c
53
+ c_indices = pool[c_mask]
54
+ if len(c_indices) == 0:
55
+ c_all = np.where(y_arr == c)[0]
56
+ n_pick = min(floor_per_class, len(c_all))
57
+ picks = c_all[np.argsort(scores[c_all])[-n_pick:]]
58
+ for idx in picks:
59
+ selected.add(int(idx))
60
+ else:
61
+ sorted_c = c_indices[np.argsort(scores[c_indices])]
62
+ n_pick = min(floor_per_class, len(sorted_c))
63
+ for idx in sorted_c[-n_pick:]:
64
+ selected.add(int(idx))
65
+
66
+ for idx in order[::-1]:
67
+ if len(selected) >= n_target:
68
+ break
69
+ selected.add(int(idx))
70
+
71
+ selected_arr = np.array(sorted(selected)[:n_target], dtype=int)
72
+ return X.index[selected_arr]
58
73
 
59
74
  def why_selected(self) -> str:
60
75
  return ("Filters out both the easiest (redundant) and hardest (potentially mislabeled) "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datapruning
3
- Version: 2.1.4
3
+ Version: 2.1.6
4
4
  Summary: Intelligent dataset optimization for cleaner, smaller training data
5
5
  License: Proprietary
6
6
  Project-URL: Homepage, https://www.datapruning.com
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "datapruning"
7
- version = "2.1.4"
7
+ version = "2.1.6"
8
8
  description = "Intelligent dataset optimization for cleaner, smaller training data"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes
File without changes