dataeval-plots 0.0.7__tar.gz → 0.0.9__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.
@@ -19,3 +19,5 @@ output/
19
19
  .vscode/launch.json
20
20
 
21
21
  src/dataeval_plots/_version.py
22
+
23
+ examples/**/
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: dataeval-plots
3
- Version: 0.0.7
3
+ Version: 0.0.9
4
4
  Summary: DataEval companion package for plotting utilities
5
5
  Project-URL: Homepage, https://dataeval.ai/
6
6
  Project-URL: Repository, https://github.com/aria-ml/dataeval/
@@ -33,6 +33,13 @@ Provides-Extra: opencv
33
33
  Requires-Dist: opencv-python-headless>=4.12.0.88; extra == 'opencv'
34
34
  Provides-Extra: plotly
35
35
  Requires-Dist: plotly>=5.0; extra == 'plotly'
36
+ Provides-Extra: projection
37
+ Requires-Dist: scikit-learn>=1.3; extra == 'projection'
38
+ Provides-Extra: projection-all
39
+ Requires-Dist: pacmap>=0.7; extra == 'projection-all'
40
+ Requires-Dist: phate>=1.0; extra == 'projection-all'
41
+ Requires-Dist: scikit-learn>=1.3; extra == 'projection-all'
42
+ Requires-Dist: umap-learn>=0.5; extra == 'projection-all'
36
43
  Provides-Extra: seaborn
37
44
  Requires-Dist: seaborn>=0.12; extra == 'seaborn'
38
45
  Description-Content-Type: text/markdown
@@ -36,6 +36,13 @@ seaborn = ["seaborn>=0.12"]
36
36
  plotly = ["plotly>=5.0"]
37
37
  altair = ["altair>=5.0"]
38
38
  opencv = ["opencv-python-headless>=4.12.0.88"]
39
+ projection = ["scikit-learn>=1.3"]
40
+ projection-all = [
41
+ "scikit-learn>=1.3",
42
+ "umap-learn>=0.5",
43
+ "pacmap>=0.7",
44
+ "phate>=1.0",
45
+ ]
39
46
  all = [
40
47
  "seaborn>=0.12",
41
48
  "plotly>=5.0",
@@ -68,8 +75,26 @@ dev = [
68
75
  { include-group = "lint" },
69
76
  { include-group = "test" },
70
77
  { include-group = "type" },
78
+ "dataeval",
79
+ "torch",
80
+ "torchvision",
81
+ "onnx",
82
+ "onnxruntime",
83
+ "requests",
84
+ "nbformat",
85
+ "ipykernel",
86
+ "ipywidgets",
71
87
  ]
72
88
 
89
+ [[tool.uv.index]]
90
+ name = "pytorch-cpu"
91
+ url = "https://download.pytorch.org/whl/cpu"
92
+ explicit = true
93
+
94
+ [tool.uv.sources]
95
+ torch = { index = "pytorch-cpu" }
96
+ torchvision = { index = "pytorch-cpu" }
97
+
73
98
  [project.urls]
74
99
  Homepage = "https://dataeval.ai/"
75
100
  Repository = "https://github.com/aria-ml/dataeval/"
@@ -102,6 +127,13 @@ source = ["src/dataeval_plots"]
102
127
  branch = true
103
128
  omit = ["src/dataeval_plots/_version.py"]
104
129
 
130
+ # Remap coverage data recorded under any checkout root (CI runner, container, local
131
+ # clone) onto this tree, so `coverage combine` resolves files no matter where each
132
+ # matrix leg ran. Replaces a `cp --recursive $(pwd) /dataeval-plots` step that
133
+ # used to sit in the CI coverage job.
134
+ [tool.coverage.paths]
135
+ source = ["src/dataeval_plots", "*/src/dataeval_plots"]
136
+
105
137
  [tool.coverage.report]
106
138
  exclude_also = [
107
139
  "raise NotImplementedError",
@@ -112,7 +144,7 @@ include = ["*/src/dataeval_plots/*"]
112
144
  fail_under = 80
113
145
 
114
146
  [tool.codespell]
115
- skip = './*env*,./output,uv.lock'
147
+ skip = './*env*,./output,uv.lock,./examples/*'
116
148
 
117
149
  [tool.ruff]
118
150
  exclude = [
@@ -0,0 +1,332 @@
1
+ """Plotting backends for DataEval outputs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable, Mapping, Sequence
6
+ from typing import Any, Literal, overload
7
+
8
+ import numpy as np
9
+ from numpy.typing import ArrayLike, NDArray
10
+
11
+ from dataeval_plots._registry import (
12
+ get_available_backends,
13
+ get_backend,
14
+ register_backend,
15
+ set_default_backend,
16
+ )
17
+ from dataeval_plots.backends._shared import MethodType
18
+ from dataeval_plots.protocols import (
19
+ Dataset,
20
+ PlottableBalance,
21
+ PlottableDiversity,
22
+ PlottableDriftMVDC,
23
+ PlottableStats,
24
+ PlottableSufficiency,
25
+ PlottableType,
26
+ )
27
+
28
+ __all__ = [
29
+ "plot",
30
+ "project",
31
+ "register_backend",
32
+ "set_default_backend",
33
+ "get_backend",
34
+ "get_available_backends",
35
+ ]
36
+
37
+
38
+ @overload
39
+ def plot(
40
+ output: PlottableBalance,
41
+ /,
42
+ figsize: tuple[float, float] | None = None,
43
+ backend: str | None = None,
44
+ *,
45
+ row_labels: Sequence[Any] | NDArray[Any] | None = None,
46
+ col_labels: Sequence[Any] | NDArray[Any] | None = None,
47
+ plot_classwise: bool = False,
48
+ ) -> Any: ...
49
+
50
+
51
+ @overload
52
+ def plot(
53
+ output: PlottableDiversity,
54
+ /,
55
+ figsize: tuple[float, float] | None = None,
56
+ backend: str | None = None,
57
+ *,
58
+ row_labels: Sequence[Any] | NDArray[Any] | None = None,
59
+ col_labels: Sequence[Any] | NDArray[Any] | None = None,
60
+ plot_classwise: bool = False,
61
+ ) -> Any: ...
62
+
63
+
64
+ @overload
65
+ def plot(
66
+ output: PlottableSufficiency,
67
+ /,
68
+ figsize: tuple[float, float] | None = None,
69
+ backend: str | None = None,
70
+ *,
71
+ class_names: Sequence[str] | None = None,
72
+ show_error_bars: bool = True,
73
+ show_asymptote: bool = True,
74
+ reference_outputs: Sequence[PlottableSufficiency] | PlottableSufficiency | None = None,
75
+ ) -> Any: ...
76
+
77
+
78
+ @overload
79
+ def plot(
80
+ output: PlottableStats,
81
+ /,
82
+ figsize: tuple[float, float] | None = None,
83
+ backend: str | None = None,
84
+ *,
85
+ log: bool = True,
86
+ channel_limit: int | None = None,
87
+ channel_index: int | Iterable[int] | None = None,
88
+ ) -> Any: ...
89
+
90
+
91
+ @overload
92
+ def plot(
93
+ output: PlottableDriftMVDC,
94
+ /,
95
+ figsize: tuple[float, float] | None = None,
96
+ backend: str | None = None,
97
+ ) -> Any: ...
98
+
99
+
100
+ @overload
101
+ def plot(
102
+ output: Dataset,
103
+ /,
104
+ figsize: tuple[float, float] | None = None,
105
+ backend: str | None = None,
106
+ *,
107
+ indices: Sequence[int],
108
+ images_per_row: int = 3,
109
+ show_labels: bool = False,
110
+ show_metadata: bool = False,
111
+ additional_metadata: Sequence[dict[str, Any]] | None = None,
112
+ ) -> Any: ...
113
+
114
+
115
+ @overload
116
+ def plot(
117
+ output: PlottableType,
118
+ /,
119
+ figsize: tuple[float, float] | None = None,
120
+ backend: str | None = None,
121
+ **kwargs: Any,
122
+ ) -> Any: ...
123
+
124
+
125
+ def plot(
126
+ output: PlottableType, /, figsize: tuple[float, float] | None = None, backend: str | None = None, **kwargs: Any
127
+ ) -> Any:
128
+ """
129
+ Plot any DataEval output object.
130
+
131
+ Parameters
132
+ ----------
133
+ output : Plottable
134
+ DataEval output object to visualize (must implement Plottable protocol)
135
+ figsize : tuple[float, float] or None, default None
136
+ Figure size in inches (width, height). If None, uses backend defaults.
137
+ backend : str or None, default None
138
+ Plotting backend ('matplotlib', 'seaborn', 'plotly', 'altair').
139
+ If None, uses default backend.
140
+ **kwargs
141
+ Backend-specific plotting parameters
142
+
143
+ Returns
144
+ -------
145
+ Figure
146
+ Backend-specific figure object
147
+
148
+ Raises
149
+ ------
150
+ ImportError
151
+ If backend dependencies are not installed
152
+ NotImplementedError
153
+ If plotting is not implemented for the given output type
154
+
155
+ Examples
156
+ --------
157
+ >>> from dataeval_plots import plot
158
+ >>> from dataeval.metrics.bias import coverage
159
+ >>> result = coverage(embeddings)
160
+ >>> fig = plot(result, images=dataset, top_k=6)
161
+ >>> fig.savefig("coverage.png")
162
+
163
+ >>> # Specify custom figure size
164
+ >>> plot(result, figsize=(12, 8), images=dataset)
165
+
166
+ >>> # Use a different backend
167
+ >>> plot(result, backend="seaborn", images=dataset)
168
+
169
+ >>> # Set default backend
170
+ >>> from dataeval_plots import set_default_backend
171
+ >>> set_default_backend("seaborn")
172
+ >>> plot(result, images=dataset) # Uses seaborn
173
+ """
174
+ plotting_backend = get_backend(backend)
175
+ return plotting_backend.plot(output, figsize=figsize, **kwargs)
176
+
177
+
178
+ def project(
179
+ embeddings: ArrayLike,
180
+ *,
181
+ method: MethodType | Sequence[MethodType] | None = "pca",
182
+ dimensions: Literal[2, 3] = 2,
183
+ labels: ArrayLike | None = None,
184
+ label_names: Mapping[int, str] | None = None,
185
+ figsize: tuple[float, float] | None = None,
186
+ backend: str | None = None,
187
+ title: str | None = None,
188
+ perplexity: float = 30.0,
189
+ n_neighbors: int = 15,
190
+ min_dist: float = 0.1,
191
+ random_state: int | None = 0,
192
+ ) -> Any:
193
+ """
194
+ Plot embeddings projected into 2D or 3D space.
195
+
196
+ Reduces high-dimensional embeddings using the specified dimensionality
197
+ reduction method(s) and plots the result as a scatter plot. When multiple
198
+ methods are provided, renders a grid of subplots for comparison.
199
+
200
+ Parameters
201
+ ----------
202
+ embeddings : ArrayLike
203
+ High-dimensional embeddings with shape ``(N, D)``. If ``method`` is
204
+ None, must already have shape ``(N, 2)`` or ``(N, 3)``.
205
+ method : str, Sequence[str], or None, default "pca"
206
+ Dimensionality reduction method(s). Pass a list to compare multiple
207
+ methods side-by-side in a grid:
208
+
209
+ - ``"pca"``: Principal Component Analysis (fast, linear)
210
+ - ``"tsne"``: t-SNE (nonlinear, preserves local structure)
211
+ - ``"umap"``: UMAP (nonlinear, preserves global + local). Requires ``umap-learn``.
212
+ - ``"isomap"``: Isomap (preserves geodesic distances)
213
+ - ``"mds"``: Multidimensional Scaling (preserves pairwise distances)
214
+ - ``"spectral"``: Spectral Embedding (reveals cluster structure)
215
+ - ``"truncated_svd"``: Truncated SVD (works on sparse data)
216
+ - ``"pacmap"``: PaCMAP (balanced local/global). Requires ``pacmap``.
217
+ - ``"phate"``: PHATE (trajectory structure). Requires ``phate``.
218
+ - None: Skip reduction, plot embeddings as-is (must be 2D or 3D).
219
+
220
+ dimensions : {2, 3}, default 2
221
+ Number of dimensions for the projection.
222
+ labels : ArrayLike or None, default None
223
+ Class labels for coloring points, shape ``(N,)``.
224
+ label_names : dict[int, str] or None, default None
225
+ Mapping from integer labels to display names for the legend.
226
+ figsize : tuple[float, float] or None, default None
227
+ Figure size in inches (width, height).
228
+ backend : str or None, default None
229
+ Plotting backend (``"matplotlib"``, ``"seaborn"``, ``"plotly"``,
230
+ ``"altair"``). If None, uses default backend.
231
+ title : str or None, default None
232
+ Plot title. If None, auto-generated from method name(s).
233
+ perplexity : float, default 30.0
234
+ Perplexity parameter for t-SNE. Ignored for other methods.
235
+ n_neighbors : int, default 15
236
+ Number of neighbors for neighbor-based methods (UMAP, Isomap,
237
+ Spectral, PaCMAP, PHATE). Ignored for other methods.
238
+ min_dist : float, default 0.1
239
+ Minimum distance for UMAP. Ignored for other methods.
240
+ random_state : int or None, default 0
241
+ Random seed for reproducibility.
242
+
243
+ Returns
244
+ -------
245
+ Any
246
+ Backend-specific figure object.
247
+
248
+ Raises
249
+ ------
250
+ ImportError
251
+ If scikit-learn or a required optional package is not installed.
252
+ ValueError
253
+ If ``method`` is None and embeddings don't have 2 or 3 columns.
254
+
255
+ Examples
256
+ --------
257
+ >>> from dataeval_plots import project
258
+ >>> fig = project(embeddings, method="tsne", labels=class_labels)
259
+
260
+ >>> # Compare multiple methods
261
+ >>> fig = project(embeddings, method=["pca", "tsne", "umap"], labels=y)
262
+
263
+ >>> # Pre-reduced embeddings
264
+ >>> fig = project(reduced_2d, method=None)
265
+
266
+ >>> # 3D with UMAP
267
+ >>> fig = project(embeddings, method="umap", dimensions=3)
268
+ """
269
+ from dataeval_plots.backends._shared import reduce_embeddings
270
+
271
+ embeddings_array = np.asarray(embeddings)
272
+ labels_array = np.asarray(labels) if labels is not None else None
273
+ plotting_backend = get_backend(backend)
274
+
275
+ # Multiple methods → grid of subplots
276
+ if isinstance(method, Sequence) and not isinstance(method, str):
277
+ methods = list(method)
278
+ if not methods:
279
+ raise ValueError("method sequence must not be empty")
280
+ if len(methods) == 1:
281
+ # Unwrap single-element list so it takes the single-plot path
282
+ method = methods[0]
283
+ else:
284
+ reduced_list = [
285
+ reduce_embeddings(
286
+ embeddings_array,
287
+ method=m,
288
+ dimensions=dimensions,
289
+ perplexity=perplexity,
290
+ n_neighbors=n_neighbors,
291
+ min_dist=min_dist,
292
+ random_state=random_state,
293
+ )
294
+ for m in methods
295
+ ]
296
+ return plotting_backend.project_grid(
297
+ reduced_list,
298
+ methods=methods,
299
+ labels=labels_array,
300
+ label_names=label_names,
301
+ dimensions=dimensions,
302
+ figsize=figsize,
303
+ title=title,
304
+ )
305
+
306
+ # Single method
307
+ if method is not None:
308
+ embeddings_array = reduce_embeddings(
309
+ embeddings_array,
310
+ method=method,
311
+ dimensions=dimensions,
312
+ perplexity=perplexity,
313
+ n_neighbors=n_neighbors,
314
+ min_dist=min_dist,
315
+ random_state=random_state,
316
+ )
317
+ else:
318
+ if embeddings_array.ndim != 2 or embeddings_array.shape[1] not in (2, 3):
319
+ raise ValueError(
320
+ f"When method is None, embeddings must have shape (N, 2) or (N, 3), got {embeddings_array.shape}"
321
+ )
322
+ dimensions = embeddings_array.shape[1] # type: ignore[assignment]
323
+
324
+ return plotting_backend.project(
325
+ embeddings_array,
326
+ labels=labels_array,
327
+ label_names=label_names,
328
+ method=method or "custom",
329
+ dimensions=dimensions,
330
+ figsize=figsize,
331
+ title=title,
332
+ )
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.0.9'
22
+ __version_tuple__ = version_tuple = (0, 0, 9)
23
+
24
+ __commit_id__ = commit_id = None