dataeval-flow 0.1.0__py3-none-any.whl

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 (94) hide show
  1. dataeval_flow/__init__.py +93 -0
  2. dataeval_flow/__main__.py +149 -0
  3. dataeval_flow/_app/__init__.py +5 -0
  4. dataeval_flow/_app/_model/__init__.py +5 -0
  5. dataeval_flow/_app/_model/_coerce.py +126 -0
  6. dataeval_flow/_app/_model/_discover.py +171 -0
  7. dataeval_flow/_app/_model/_execution.py +108 -0
  8. dataeval_flow/_app/_model/_introspect.py +280 -0
  9. dataeval_flow/_app/_model/_item.py +213 -0
  10. dataeval_flow/_app/_model/_registry.py +255 -0
  11. dataeval_flow/_app/_model/_state.py +322 -0
  12. dataeval_flow/_app/_model/_undo.py +61 -0
  13. dataeval_flow/_app/_panes/__init__.py +35 -0
  14. dataeval_flow/_app/_panes/_config_pane.py +173 -0
  15. dataeval_flow/_app/_panes/_result_pane.py +125 -0
  16. dataeval_flow/_app/_panes/_task_pane.py +91 -0
  17. dataeval_flow/_app/_panes/_widgets.py +111 -0
  18. dataeval_flow/_app/_screens/__init__.py +25 -0
  19. dataeval_flow/_app/_screens/_base.py +242 -0
  20. dataeval_flow/_app/_screens/_detail.py +333 -0
  21. dataeval_flow/_app/_screens/_model.py +102 -0
  22. dataeval_flow/_app/_screens/_params.py +80 -0
  23. dataeval_flow/_app/_screens/_pathpicker.py +68 -0
  24. dataeval_flow/_app/_screens/_section.py +621 -0
  25. dataeval_flow/_app/_screens/_settings.py +183 -0
  26. dataeval_flow/_app/_viewmodel/__init__.py +15 -0
  27. dataeval_flow/_app/_viewmodel/_builder_vm.py +272 -0
  28. dataeval_flow/_app/_viewmodel/_model_vm.py +70 -0
  29. dataeval_flow/_app/_viewmodel/_rendering.py +189 -0
  30. dataeval_flow/_app/_viewmodel/_result_vm.py +210 -0
  31. dataeval_flow/_app/_viewmodel/_section_vm.py +224 -0
  32. dataeval_flow/_app/app.py +742 -0
  33. dataeval_flow/_app/cli.py +592 -0
  34. dataeval_flow/_logging.py +102 -0
  35. dataeval_flow/cache.py +1355 -0
  36. dataeval_flow/config/__init__.py +80 -0
  37. dataeval_flow/config/_loader.py +79 -0
  38. dataeval_flow/config/_merge.py +92 -0
  39. dataeval_flow/config/_models.py +115 -0
  40. dataeval_flow/config/_paths.py +85 -0
  41. dataeval_flow/config/schemas/__init__.py +112 -0
  42. dataeval_flow/config/schemas/_dataset.py +111 -0
  43. dataeval_flow/config/schemas/_extractor.py +119 -0
  44. dataeval_flow/config/schemas/_metadata.py +28 -0
  45. dataeval_flow/config/schemas/_preprocessor.py +18 -0
  46. dataeval_flow/config/schemas/_selection.py +100 -0
  47. dataeval_flow/config/schemas/_task.py +89 -0
  48. dataeval_flow/config/schemas/_workflow.py +135 -0
  49. dataeval_flow/dataset.py +635 -0
  50. dataeval_flow/embeddings.py +135 -0
  51. dataeval_flow/metadata.py +48 -0
  52. dataeval_flow/preprocessing.py +141 -0
  53. dataeval_flow/py.typed +0 -0
  54. dataeval_flow/runner.py +118 -0
  55. dataeval_flow/selection.py +50 -0
  56. dataeval_flow/workflow/__init__.py +328 -0
  57. dataeval_flow/workflow/_text_report.py +511 -0
  58. dataeval_flow/workflow/base.py +69 -0
  59. dataeval_flow/workflow/orchestrator.py +454 -0
  60. dataeval_flow/workflows/__init__.py +1 -0
  61. dataeval_flow/workflows/analysis/__init__.py +38 -0
  62. dataeval_flow/workflows/analysis/outputs.py +202 -0
  63. dataeval_flow/workflows/analysis/params.py +114 -0
  64. dataeval_flow/workflows/analysis/workflow.py +1313 -0
  65. dataeval_flow/workflows/cleaning/__init__.py +23 -0
  66. dataeval_flow/workflows/cleaning/outputs.py +200 -0
  67. dataeval_flow/workflows/cleaning/params.py +160 -0
  68. dataeval_flow/workflows/cleaning/report.py +304 -0
  69. dataeval_flow/workflows/cleaning/workflow.py +794 -0
  70. dataeval_flow/workflows/drift/__init__.py +1 -0
  71. dataeval_flow/workflows/drift/outputs.py +144 -0
  72. dataeval_flow/workflows/drift/params.py +332 -0
  73. dataeval_flow/workflows/drift/report.py +201 -0
  74. dataeval_flow/workflows/drift/workflow.py +647 -0
  75. dataeval_flow/workflows/ood/__init__.py +1 -0
  76. dataeval_flow/workflows/ood/outputs.py +134 -0
  77. dataeval_flow/workflows/ood/params.py +161 -0
  78. dataeval_flow/workflows/ood/report.py +311 -0
  79. dataeval_flow/workflows/ood/workflow.py +728 -0
  80. dataeval_flow/workflows/prioritization/__init__.py +1 -0
  81. dataeval_flow/workflows/prioritization/outputs.py +122 -0
  82. dataeval_flow/workflows/prioritization/params.py +124 -0
  83. dataeval_flow/workflows/prioritization/report.py +117 -0
  84. dataeval_flow/workflows/prioritization/workflow.py +587 -0
  85. dataeval_flow/workflows/splitting/__init__.py +25 -0
  86. dataeval_flow/workflows/splitting/outputs.py +101 -0
  87. dataeval_flow/workflows/splitting/params.py +61 -0
  88. dataeval_flow/workflows/splitting/report.py +485 -0
  89. dataeval_flow/workflows/splitting/workflow.py +371 -0
  90. dataeval_flow-0.1.0.dist-info/METADATA +305 -0
  91. dataeval_flow-0.1.0.dist-info/RECORD +94 -0
  92. dataeval_flow-0.1.0.dist-info/WHEEL +4 -0
  93. dataeval_flow-0.1.0.dist-info/entry_points.txt +2 -0
  94. dataeval_flow-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1 @@
1
+ """Data prioritization workflow."""
@@ -0,0 +1,122 @@
1
+ """Data prioritization workflow outputs."""
2
+
3
+ from typing import TYPE_CHECKING, Any, Literal, TypeAlias
4
+
5
+ from pydantic import BaseModel, Field
6
+ from typing_extensions import TypedDict, TypeIs
7
+
8
+ from dataeval_flow.config import ResultMetadata
9
+ from dataeval_flow.workflow.base import Reportable, WorkflowOutputsBase, WorkflowReportBase
10
+
11
+ if TYPE_CHECKING:
12
+ from dataeval_flow.workflow import WorkflowResult
13
+
14
+ __all__ = [
15
+ "CleaningSummaryDict",
16
+ "DataPrioritizationMetadata",
17
+ "DataPrioritizationOutputs",
18
+ "DataPrioritizationRawOutputs",
19
+ "DataPrioritizationReport",
20
+ "DataPrioritizationResult",
21
+ "PerDatasetPrioritizationDict",
22
+ "is_prioritization_result",
23
+ ]
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # TypedDicts for serialized outputs
28
+ # ---------------------------------------------------------------------------
29
+
30
+
31
+ class PerDatasetPrioritizationDict(TypedDict):
32
+ """Prioritization results for a single additional dataset."""
33
+
34
+ source_name: str
35
+ original_size: int
36
+ cleaned_size: int
37
+ prioritized_indices: list[int]
38
+ scores: list[float] | None
39
+
40
+
41
+ class CleaningSummaryDict(TypedDict):
42
+ """Summary of the cleaning step (combined across all datasets)."""
43
+
44
+ total_combined: int
45
+ outliers_flagged: int
46
+ duplicates_flagged: int
47
+ total_removed: int
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Pydantic output models
52
+ # ---------------------------------------------------------------------------
53
+
54
+
55
+ class DataPrioritizationRawOutputs(WorkflowOutputsBase):
56
+ """Machine-readable results from the data-prioritization workflow."""
57
+
58
+ reference_size: int = Field(
59
+ default=0,
60
+ description="Number of items in the reference dataset.",
61
+ )
62
+ method: str = Field(
63
+ default="",
64
+ description="Ranking method used.",
65
+ )
66
+ order: str = Field(
67
+ default="",
68
+ description="Sort direction used.",
69
+ )
70
+ policy: str = Field(
71
+ default="",
72
+ description="Selection policy used.",
73
+ )
74
+ cleaning_summary: CleaningSummaryDict | None = Field(
75
+ default=None,
76
+ description="Cleaning step summary. None if cleaning was skipped.",
77
+ )
78
+ prioritizations: list[PerDatasetPrioritizationDict] = Field(
79
+ default_factory=list,
80
+ description="Per-dataset prioritization results.",
81
+ )
82
+
83
+
84
+ class DataPrioritizationReport(WorkflowReportBase):
85
+ """Human-readable report for the data-prioritization workflow."""
86
+
87
+ findings: list[Reportable] = Field(default_factory=list)
88
+
89
+
90
+ class DataPrioritizationOutputs(BaseModel):
91
+ """Complete data-prioritization workflow output."""
92
+
93
+ raw: DataPrioritizationRawOutputs
94
+ report: DataPrioritizationReport
95
+
96
+
97
+ class DataPrioritizationMetadata(ResultMetadata):
98
+ """Metadata for the data-prioritization workflow."""
99
+
100
+ mode: Literal["advisory", "preparatory"] = "advisory"
101
+ method: str = ""
102
+ order: str = ""
103
+ policy: str = ""
104
+ cleaning_enabled: bool = False
105
+ items_removed_by_cleaning: int = 0
106
+ per_source_clean_indices: dict[str, list[int]] = Field(default_factory=dict)
107
+ per_source_prioritized_indices: dict[str, list[int]] = Field(default_factory=dict)
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Type alias and TypeIs guard for type narrowing
112
+ # ---------------------------------------------------------------------------
113
+
114
+ #: Fully typed result alias for the data-prioritization workflow.
115
+ DataPrioritizationResult: TypeAlias = "WorkflowResult[DataPrioritizationMetadata, DataPrioritizationOutputs]"
116
+
117
+
118
+ def is_prioritization_result(
119
+ result: "WorkflowResult[Any, Any]",
120
+ ) -> TypeIs["WorkflowResult[DataPrioritizationMetadata, DataPrioritizationOutputs]"]:
121
+ """Narrow a generic ``WorkflowResult`` to a data-prioritization result."""
122
+ return isinstance(result.metadata, DataPrioritizationMetadata)
@@ -0,0 +1,124 @@
1
+ """Data prioritization workflow parameters."""
2
+
3
+ from collections.abc import Sequence
4
+ from typing import ClassVar, Literal
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+ from dataeval_flow.workflow.base import WorkflowParametersBase
9
+
10
+ __all__ = [
11
+ "CleaningConfig",
12
+ "DataPrioritizationHealthThresholds",
13
+ "DataPrioritizationParameters",
14
+ ]
15
+
16
+ MethodType = Literal["knn", "kmeans_distance", "kmeans_complexity", "hdbscan_distance", "hdbscan_complexity"]
17
+ OrderType = Literal["easy_first", "hard_first"]
18
+ PolicyType = Literal["difficulty", "stratified", "class_balanced"]
19
+
20
+
21
+ class CleaningConfig(BaseModel):
22
+ """Optional cleaning sub-config for outlier/duplicate removal before prioritization.
23
+
24
+ When provided, outlier and duplicate detection runs across all datasets
25
+ before prioritization. Flagged items are excluded from the ranking.
26
+ """
27
+
28
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
29
+
30
+ outlier_method: Literal["adaptive", "zscore", "modzscore", "iqr"] = Field(
31
+ description="Statistical method for outlier detection.",
32
+ )
33
+ outlier_flags: Sequence[Literal["dimension", "pixel", "visual"]] = Field(
34
+ min_length=1,
35
+ description="Image statistics groups for outlier detection. At least one required.",
36
+ )
37
+ outlier_threshold: float | None = Field(
38
+ default=None,
39
+ ge=0.0,
40
+ description="Custom threshold (None = use DataEval default for chosen method).",
41
+ )
42
+ duplicate_flags: Sequence[Literal["hash_basic", "hash_d4"]] | None = Field(
43
+ default=None,
44
+ description="Hash flag groups for duplicate detection. None = DataEval default (hash_basic).",
45
+ )
46
+ duplicate_merge_near: bool = Field(
47
+ default=True,
48
+ description="Merge overlapping near-duplicate groups from different detection methods.",
49
+ )
50
+ duplicate_exact_only: bool = Field(
51
+ default=False,
52
+ description="When True, only flag exact duplicates — skip near-duplicate detection.",
53
+ )
54
+
55
+
56
+ class DataPrioritizationHealthThresholds(BaseModel):
57
+ """Thresholds that control finding severity for the prioritization report."""
58
+
59
+ cleaning_removed_pct_warning: float = Field(
60
+ default=20.0,
61
+ ge=0.0,
62
+ le=100.0,
63
+ description="Warn if cleaning removes more than this percentage of the combined dataset.",
64
+ )
65
+
66
+
67
+ class DataPrioritizationParameters(WorkflowParametersBase):
68
+ """Parameters for the data-prioritization workflow.
69
+
70
+ Requires at least two sources: the first is the reference (labeled) dataset,
71
+ and subsequent sources are unlabeled data pools to prioritize.
72
+ """
73
+
74
+ # --- Prioritization method ---
75
+ method: MethodType = Field(
76
+ default="knn",
77
+ description="Ranking method: knn, kmeans_distance, kmeans_complexity, hdbscan_distance, hdbscan_complexity.",
78
+ )
79
+ k: int | None = Field(
80
+ default=None,
81
+ gt=0,
82
+ description="Number of nearest neighbors for knn method. None = sqrt(n_samples).",
83
+ )
84
+ c: int | None = Field(
85
+ default=None,
86
+ gt=0,
87
+ description="Number of clusters for clustering methods. None = sqrt(n_samples).",
88
+ )
89
+ n_init: int | Literal["auto"] = Field(
90
+ default="auto",
91
+ description="Number of K-means initializations (kmeans methods only).",
92
+ )
93
+ max_cluster_size: int | None = Field(
94
+ default=None,
95
+ gt=0,
96
+ description="Maximum cluster size for HDBSCAN methods.",
97
+ )
98
+
99
+ # --- Order and policy ---
100
+ order: OrderType = Field(
101
+ default="hard_first",
102
+ description="Sort direction: easy_first (prototypical first) or hard_first (novel/challenging first).",
103
+ )
104
+ policy: PolicyType = Field(
105
+ default="difficulty",
106
+ description="Selection policy: difficulty (direct ordering), stratified (binned), class_balanced.",
107
+ )
108
+ num_bins: int = Field(
109
+ default=50,
110
+ gt=0,
111
+ description="Number of bins for stratified policy.",
112
+ )
113
+
114
+ # --- Optional cleaning ---
115
+ cleaning: CleaningConfig | None = Field(
116
+ default=None,
117
+ description="Optional cleaning config. When set, outlier/duplicate detection runs before prioritization.",
118
+ )
119
+
120
+ # --- Health thresholds ---
121
+ health_thresholds: DataPrioritizationHealthThresholds = Field(
122
+ default_factory=DataPrioritizationHealthThresholds,
123
+ description="Warning thresholds for the prioritization report.",
124
+ )
@@ -0,0 +1,117 @@
1
+ """Findings builders for the data-prioritization workflow."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Literal
6
+
7
+ from dataeval_flow.workflow.base import Reportable
8
+ from dataeval_flow.workflows.prioritization.outputs import (
9
+ CleaningSummaryDict,
10
+ DataPrioritizationRawOutputs,
11
+ PerDatasetPrioritizationDict,
12
+ )
13
+ from dataeval_flow.workflows.prioritization.params import (
14
+ DataPrioritizationHealthThresholds,
15
+ DataPrioritizationParameters,
16
+ )
17
+
18
+
19
+ def _severity_for_cleaning(
20
+ removed_pct: float,
21
+ thresholds: DataPrioritizationHealthThresholds,
22
+ ) -> Literal["ok", "info", "warning"]:
23
+ """Determine severity based on percentage of items removed by cleaning."""
24
+ if removed_pct >= thresholds.cleaning_removed_pct_warning:
25
+ return "warning"
26
+ if removed_pct > 0:
27
+ return "info"
28
+ return "ok"
29
+
30
+
31
+ def _build_cleaning_finding(
32
+ summary: CleaningSummaryDict,
33
+ thresholds: DataPrioritizationHealthThresholds,
34
+ ) -> Reportable:
35
+ """Build a finding for the cleaning step."""
36
+ total = summary["total_combined"]
37
+ removed = summary["total_removed"]
38
+ removed_pct = (removed / total * 100) if total > 0 else 0.0
39
+ severity = _severity_for_cleaning(removed_pct, thresholds)
40
+
41
+ data: dict[str, Any] = {
42
+ "brief": f"{removed} items ({removed_pct:.1f}%)",
43
+ "total_combined": total,
44
+ "outliers_flagged": summary["outliers_flagged"],
45
+ "duplicates_flagged": summary["duplicates_flagged"],
46
+ "total_removed": removed,
47
+ "removed_pct": f"{removed_pct:.1f}%",
48
+ }
49
+
50
+ description = (
51
+ f"Pruning removed {removed}/{total} items ({removed_pct:.1f}%): "
52
+ f"{summary['outliers_flagged']} outliers, {summary['duplicates_flagged']} duplicates"
53
+ )
54
+
55
+ return Reportable(
56
+ report_type="key_value",
57
+ severity=severity,
58
+ title="Pruning",
59
+ data=data,
60
+ description=description,
61
+ )
62
+
63
+
64
+ def _build_prioritization_finding(
65
+ result: PerDatasetPrioritizationDict,
66
+ method: str,
67
+ order: str,
68
+ policy: str,
69
+ ) -> Reportable:
70
+ """Build a finding for a single dataset's prioritization results."""
71
+ n_items = result["cleaned_size"]
72
+ top_n = min(10, len(result["prioritized_indices"]))
73
+ top_indices = result["prioritized_indices"][:top_n]
74
+
75
+ data: dict[str, Any] = {
76
+ "brief": f"{n_items} items",
77
+ "source": result["source_name"],
78
+ "original_size": result["original_size"],
79
+ "cleaned_size": result["cleaned_size"],
80
+ "method": method,
81
+ "order": order,
82
+ "policy": policy,
83
+ "top_indices": top_indices,
84
+ }
85
+
86
+ if result["scores"] is not None:
87
+ top_scores = result["scores"][:top_n]
88
+ data["top_scores"] = [round(s, 4) for s in top_scores]
89
+
90
+ description = f"{result['source_name']}: {n_items} items prioritized via {method} ({order}, {policy})"
91
+
92
+ return Reportable(
93
+ report_type="key_value",
94
+ severity="info",
95
+ title=f"Prioritization: {result['source_name']}",
96
+ data=data,
97
+ description=description,
98
+ )
99
+
100
+
101
+ def build_findings(
102
+ raw: DataPrioritizationRawOutputs,
103
+ params: DataPrioritizationParameters,
104
+ ) -> list[Reportable]:
105
+ """Build all report findings from raw results."""
106
+ findings: list[Reportable] = []
107
+
108
+ # Cleaning finding
109
+ if raw.cleaning_summary is not None:
110
+ findings.append(_build_cleaning_finding(raw.cleaning_summary, params.health_thresholds))
111
+
112
+ # Per-dataset prioritization findings
113
+ findings.extend(
114
+ _build_prioritization_finding(result, raw.method, raw.order, raw.policy) for result in raw.prioritizations
115
+ )
116
+
117
+ return findings