evalsuite-python 0.3.0__py3-none-any.whl → 0.3.1__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.
evalsuite/benchmarks.py CHANGED
@@ -17,8 +17,10 @@ whose reference library is not installed is timed for EvalSuite only.
17
17
  from __future__ import annotations
18
18
 
19
19
  import contextlib
20
+ import importlib
20
21
  import json
21
22
  import platform
23
+ import re
22
24
  import time
23
25
  import tracemalloc
24
26
  import warnings
@@ -371,6 +373,28 @@ def _clinical_cases(n: int, rng: np.random.Generator) -> list[Case]:
371
373
  return [(name, ref, es_fn, own if own is not None else refs.get(name)) for name, ref, es_fn, own in cases]
372
374
 
373
375
 
376
+ _GROUPS = (
377
+ ("Classification and regression", ("binary", "10 classes", "regression")),
378
+ ("Clinical, calibration and statistics", ("clinical", "calibration", "decision", "statistics", "multiple")),
379
+ ("Segmentation and object detection", ("segmentation", "detection")),
380
+ )
381
+ MATCH_TOLERANCE = 1e-9
382
+
383
+
384
+ def benchmark_group(case: str) -> str:
385
+ """The suite a benchmark case belongs to, by its name."""
386
+ lowered = case.lower()
387
+ for title, prefixes in _GROUPS:
388
+ if lowered.startswith(prefixes):
389
+ return title
390
+ return "Other"
391
+
392
+
393
+ def _geomean(values: Sequence[float]) -> Optional[float]:
394
+ vals = [v for v in values if v is not None and v > 0]
395
+ return float(np.exp(np.mean(np.log(vals)))) if vals else None
396
+
397
+
374
398
  @dataclass(frozen=True, eq=False)
375
399
  class BenchmarkResult:
376
400
  """Rows of timings (milliseconds), peak memory (MiB) and agreement, plus the environment they ran in."""
@@ -382,6 +406,60 @@ class BenchmarkResult:
382
406
  object.__setattr__(self, "rows", tuple(MappingProxyType(dict(r)) for r in self.rows))
383
407
  object.__setattr__(self, "environment", MappingProxyType(dict(self.environment)))
384
408
 
409
+ def sorted_rows(self) -> list[Any]:
410
+ """Rows in alphabetical order of case, then by size."""
411
+ return sorted(self.rows, key=lambda r: (r["case"].lower(), r["n"]))
412
+
413
+ def overall(self) -> list[dict[str, Any]]:
414
+ """One summary row per suite and one for everything: cases, how many agree with the reference
415
+ (max |difference| <= 1e-9), and the geometric-mean, smallest and largest speed-up."""
416
+
417
+ def summarise(name: str, rows: Sequence[Any]) -> dict[str, Any]:
418
+ compared = [r for r in rows if r["speedup"] is not None]
419
+ speedups = [r["speedup"] for r in compared]
420
+ return {
421
+ "group": name,
422
+ "cases": len({re.sub(r"\s*\(\d+ images?\)", "", r["case"]) for r in rows}),
423
+ "rows": len(rows),
424
+ "compared": len(compared),
425
+ "matching": sum(
426
+ r["max_abs_diff"] is not None and r["max_abs_diff"] <= MATCH_TOLERANCE for r in rows
427
+ ),
428
+ "faster": sum(sp > 1 for sp in speedups),
429
+ "geomean_speedup": _geomean(speedups),
430
+ "min_speedup": min(speedups) if speedups else None,
431
+ "max_speedup": max(speedups) if speedups else None,
432
+ }
433
+
434
+ groups = sorted({benchmark_group(r["case"]) for r in self.rows})
435
+ out = [summarise(g, [r for r in self.rows if benchmark_group(r["case"]) == g]) for g in groups]
436
+ out.append(summarise("Overall", self.rows))
437
+ return out
438
+
439
+ _OVERALL_TITLES = ("Suite", "Cases", "Rows", "Match reference", "Faster", "Geo-mean speed-up", "Range")
440
+
441
+ def _overall_cells(self) -> list[list[str]]:
442
+ def x(v: Optional[float]) -> str:
443
+ return "–" if v is None else f"{v:.2f}×"
444
+
445
+ return [
446
+ [
447
+ o["group"],
448
+ str(o["cases"]),
449
+ str(o["rows"]),
450
+ f"{o['matching']}/{o['compared']}",
451
+ f"{o['faster']}/{o['compared']}",
452
+ x(o["geomean_speedup"]),
453
+ "–" if o["min_speedup"] is None else f"{x(o['min_speedup'])}–{x(o['max_speedup'])}",
454
+ ]
455
+ for o in self.overall()
456
+ ]
457
+
458
+ def overall_markdown(self) -> str:
459
+ """The overall summary as a Markdown table."""
460
+ lines = ["| " + " | ".join(self._OVERALL_TITLES) + " |", "| --- |" + " ---: |" * 6]
461
+ return "\n".join(lines + ["| " + " | ".join(c) + " |" for c in self._overall_cells()])
462
+
385
463
  def _cells(self, digits: int) -> list[list[str]]:
386
464
  def f(v: Any, d: int = digits) -> str:
387
465
  return "–" if v is None else f"{v:.{d}f}"
@@ -398,7 +476,7 @@ class BenchmarkResult:
398
476
  f(r.get("reference_peak_mb"), 2),
399
477
  "–" if r["max_abs_diff"] is None else f"{r['max_abs_diff']:.1e}",
400
478
  ]
401
- for r in self.rows
479
+ for r in self.sorted_rows()
402
480
  ]
403
481
 
404
482
  _TITLES = (
@@ -430,7 +508,14 @@ class BenchmarkResult:
430
508
  + f" | {env['machine']} | fastest of {env['repeat']} runs"
431
509
  )
432
510
  note = "Speed-up > 1 means EvalSuite is faster. Max |difference| compares EvalSuite with the reference."
433
- return "\n".join([head, "", line(self._TITLES), *(line(c) for c in cells), "", note])
511
+ ocells = self._overall_cells()
512
+ owidths = [max(len(t), *(len(c[i]) for c in ocells)) for i, t in enumerate(self._OVERALL_TITLES)]
513
+
514
+ def oline(c: Sequence[str]) -> str:
515
+ return " ".join(x.ljust(owidths[i]) if i == 0 else x.rjust(owidths[i]) for i, x in enumerate(c))
516
+
517
+ overall = ["Overall", oline(self._OVERALL_TITLES), *(oline(c) for c in ocells), ""]
518
+ return "\n".join([head, "", *overall, line(self._TITLES), *(line(c) for c in cells), "", note])
434
519
 
435
520
  def __repr__(self) -> str:
436
521
  return self.summary()
@@ -438,7 +523,13 @@ class BenchmarkResult:
438
523
  def to_dict(self) -> dict[str, Any]:
439
524
  return cast(
440
525
  "dict[str, Any]",
441
- _json_safe({"environment": dict(self.environment), "rows": [dict(r) for r in self.rows]}),
526
+ _json_safe(
527
+ {
528
+ "environment": dict(self.environment),
529
+ "overall": self.overall(),
530
+ "rows": [dict(r) for r in self.rows],
531
+ }
532
+ ),
442
533
  )
443
534
 
444
535
  def to_json(self, *, indent: Optional[int] = 2) -> str:
@@ -514,7 +605,7 @@ def run_benchmarks(
514
605
  if compare_sklearn:
515
606
  for mod in versions:
516
607
  with contextlib.suppress(ImportError):
517
- __import__(mod)
608
+ importlib.import_module(mod) # fixed names only: sklearn, statsmodels, pycocotools
518
609
  from importlib.metadata import version as _dist_version
519
610
 
520
611
  versions[mod] = _dist_version("scikit-learn" if mod == "sklearn" else mod)
@@ -45,9 +45,25 @@ def to_numpy(x: ArrayLike, name: str, *, allow_2d: bool = False) -> NDArray[Any]
45
45
  raise InputValidationError(f"{name} must be {expected}; received an array with shape {arr.shape}.")
46
46
  if arr.shape[0] == 0:
47
47
  raise InputValidationError(f"{name} is empty. Provide at least one observation.")
48
+ if (
49
+ arr.ndim == 1
50
+ and arr.dtype.kind in "US"
51
+ and isinstance(x, (list, tuple))
52
+ and not all(isinstance(v, (str, bytes)) for v in x)
53
+ ):
54
+ # NumPy silently turns [0, "a"] into ["0", "a"]; a mix of numbers and strings is almost always a bug.
55
+ raise InputValidationError(f"{name} mixes numbers and strings; use one label type throughout.")
48
56
  return arr
49
57
 
50
58
 
59
+ def _label_kind(arr: NDArray[Any]) -> str:
60
+ if arr.dtype.kind in "US":
61
+ return "string"
62
+ if arr.dtype.kind in "biuf":
63
+ return "number"
64
+ return "other"
65
+
66
+
51
67
  def check_finite(arr: NDArray[Any], name: str) -> None:
52
68
  if np.issubdtype(arr.dtype, np.number) and not np.all(np.isfinite(arr)):
53
69
  n_nan = int(np.isnan(arr).sum())
@@ -126,6 +142,11 @@ def resolve_labels(
126
142
  if np.unique(lab).shape[0] != lab.shape[0]:
127
143
  raise InputValidationError("labels contains duplicates.")
128
144
  return lab
145
+ if y_pred is not None and {_label_kind(y_true), _label_kind(y_pred)} == {"string", "number"}:
146
+ raise InputValidationError(
147
+ f"y_true has {_label_kind(y_true)} labels but y_pred has {_label_kind(y_pred)} labels; "
148
+ "use the same label type in both (for example map class names to integers first)."
149
+ )
129
150
  present = y_true if y_pred is None else np.concatenate([y_true, y_pred])
130
151
  try:
131
152
  return unique_labels(present)
@@ -202,7 +202,15 @@ def rmse(
202
202
  ) -> MetricResult:
203
203
  """Root mean squared error (square root taken per output, then averaged)."""
204
204
  inp = _inputs(y_true, y_pred, sample_weight)
205
- return _finish(np.sqrt(inp.mean(inp.error**2)), inp, multioutput, "rmse", "RMSE")
205
+ with np.errstate(over="ignore"):
206
+ per_output = np.sqrt(inp.mean(inp.error**2))
207
+ if not np.all(np.isfinite(per_output)):
208
+ # Errors above ~1e154 overflow when squared although the RMSE itself is representable:
209
+ # rescale by the largest error and square again.
210
+ scale = np.abs(inp.error).max(axis=0)
211
+ scale = np.where(np.isfinite(scale) & (scale > 0), scale, 1.0)
212
+ per_output = scale * np.sqrt(inp.mean((inp.error / scale) ** 2))
213
+ return _finish(per_output, inp, multioutput, "rmse", "RMSE")
206
214
 
207
215
 
208
216
  def _r2_per_output(inp: _Inputs) -> FloatArray:
@@ -10,7 +10,7 @@ from typing import Any, Optional
10
10
  import numpy as np
11
11
  from numpy.typing import NDArray
12
12
 
13
- from ..core.exceptions import InputValidationError
13
+ from ..core.exceptions import InputValidationError, StatisticalTestError
14
14
  from ..core.validation import target_type, to_numpy
15
15
 
16
16
  MetricFn = Callable[..., Any]
@@ -89,6 +89,11 @@ class MetricCall:
89
89
  f"observations. Received {self.y_true.shape[0]} and {self.second.shape[0]}."
90
90
  )
91
91
  self.n = self.y_true.shape[0]
92
+ if self.n < 2:
93
+ raise StatisticalTestError(
94
+ f"Resampling needs at least 2 observations; got {self.n}. An interval or test from a single "
95
+ "observation would have zero width and no meaning."
96
+ )
92
97
  self.weight = None if sample_weight is None else to_numpy(sample_weight, "sample_weight")
93
98
  self.kwargs = dict(kwargs or {})
94
99
  params = inspect.signature(fn).parameters
evalsuite/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Package version (single source of truth, read by the build backend)."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.3.1"
@@ -84,9 +84,17 @@ _REF_HD = (
84
84
 
85
85
  # ---- input handling ------------------------------------------------------------------------------
86
86
  def _as_images(masks: Masks, name: str) -> list[NDArray[Any]]:
87
+ images = None
87
88
  if isinstance(masks, (list, tuple)) or (isinstance(masks, np.ndarray) and masks.dtype == object):
88
89
  images = [np.asarray(m) for m in masks]
89
- else:
90
+ if (
91
+ images
92
+ and isinstance(masks, (list, tuple))
93
+ and all(m.ndim == 1 for m in images)
94
+ and not any(isinstance(m, np.ndarray) for m in masks)
95
+ ):
96
+ images = None # a nested Python list of rows, e.g. [[1, 1], [0, 0]], is one 2-D mask
97
+ if images is None:
90
98
  arr = np.asarray(masks)
91
99
  if arr.ndim < 2:
92
100
  raise InputValidationError(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: evalsuite-python
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: A unified Python framework for machine-learning, clinical, statistical, segmentation, object-detection, uncertainty, and model evaluation.
5
5
  Project-URL: Homepage, https://evalsuite-nine.vercel.app
6
6
  Project-URL: Documentation, https://evalsuite-nine.vercel.app/docs
@@ -49,7 +49,7 @@ Requires-Dist: pycocotools>=2.0.7; extra == 'dev'
49
49
  Requires-Dist: pytest-cov>=4; extra == 'dev'
50
50
  Requires-Dist: pytest>=7; extra == 'dev'
51
51
  Requires-Dist: ruff>=0.6; extra == 'dev'
52
- Requires-Dist: scikit-learn>=1.2; extra == 'dev'
52
+ Requires-Dist: scikit-learn>=1.4; extra == 'dev'
53
53
  Requires-Dist: statsmodels>=0.13; extra == 'dev'
54
54
  Requires-Dist: twine; extra == 'dev'
55
55
  Provides-Extra: plot
@@ -70,7 +70,7 @@ Description-Content-Type: text/markdown
70
70
  EvalSuite brings classification, regression, clinical, statistical, segmentation and object-detection
71
71
  evaluation into one consistent, validated, documented framework.
72
72
 
73
- > **Status: stable (0.3.0).** Every item on the 0.1.0, 0.2.0 and 0.3.0 roadmaps is implemented and verified.
73
+ > **Status: stable (0.3.1).** Every item on the 0.1.0, 0.2.0 and 0.3.0 roadmaps is implemented and verified.
74
74
 
75
75
  ## Installation
76
76
 
@@ -283,24 +283,28 @@ Input files can be CSV, TSV, Parquet or JSON. Output format follows `--format` o
283
283
  ## Performance
284
284
 
285
285
  Benchmarked against reference implementations on the same data (fastest of 5 runs; Python 3.12, NumPy 2.5,
286
- Linux x86_64). Every result agrees with the reference to floating-point rounding (largest difference
287
- 1.4e-14).
288
-
289
- | Case | n | Reference | EvalSuite (ms) | Reference (ms) | Speed-up |
290
- | --- | ---: | --- | ---: | ---: | ---: |
291
- | 8 binary label metrics via `evaluate()` | 1,000,000 | scikit-learn | 30.2 | 1020.2 | **33.8×** |
292
- | macro F1, 10 classes | 1,000,000 | scikit-learn | 22.4 | 128.7 | **5.8×** |
293
- | ROC AUC, binary | 1,000,000 | scikit-learn | 173.2 | 300.6 | **1.7×** |
294
- | MAE, MSE, RMSE, R² via `evaluate()` | 1,000,000 | scikit-learn | 19.1 | 9.7 | 0.51× |
295
- | sensitivity, specificity, LR+, LR− | 1,000,000 | scikit-learn | 66.2 | 392.8 | **5.9×** |
296
- | calibration slope and intercept | 1,000,000 | statsmodels | 178.0 | 1014.8 | **5.7×** |
297
- | decision curve, 99 thresholds | 1,000,000 | NumPy loop | 155.2 | 174.3 | **1.1×** |
298
- | diagnostic report (7 CIs) | 1,000,000 | statsmodels | 16.6 | 4.6 | 0.28× |
299
- | Welch t-test | 1,000,000 | SciPy | 14.5 | 7.3 | 0.51× |
300
- | Hochberg correction | 1,000,000 | statsmodels | 75.4 | 81.9 | **1.1×** |
301
- | segmentation Dice and IoU per class | 1,000,000 px | scikit-learn | 40.2 | 282.0 | **7.0×** |
302
- | COCO detection evaluation (12 numbers) | 1,000 images | pycocotools | 967.4 | 980.6 | **1.0×** |
303
- | Hausdorff distance | 50 images | SciPy | 26.2 | 20.8 | 0.79× |
286
+ Linux x86_64). **Overall: 45 of 45 rows agree with the reference** (largest
287
+ difference 2.8e-14), 32 are faster, and the geometric-mean speed-up across all
288
+ 15 cases and three sizes is **2.22×**. At 1,000,000 samples (pixels for
289
+ segmentation; 1,000 images for detection and 50 for Hausdorff), in alphabetical order:
290
+
291
+ | Case | Reference | EvalSuite (ms) | Reference (ms) | Speed-up |
292
+ | --- | --- | ---: | ---: | ---: |
293
+ | 10 classes: macro F1 | scikit-learn | 29.3 | 130.8 | **4.5×** |
294
+ | binary: 8 label metrics via evaluate() | scikit-learn | 39.5 | 1084.1 | **27.4×** |
295
+ | binary: ROC AUC | scikit-learn | 195.0 | 348.3 | **1.8×** |
296
+ | calibration: slope and intercept | statsmodels | 193.0 | 1068.0 | **5.5×** |
297
+ | clinical: diagnostic report (7 CIs) | statsmodels | 23.5 | 4.7 | 0.20× |
298
+ | clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 69.1 | 414.0 | **6.0×** |
299
+ | decision curve: 99 thresholds | NumPy loop | 156.3 | 181.6 | **1.2×** |
300
+ | detection: COCO evaluation (1000 images) | pycocotools | 817.3 | 983.7 | **1.2×** |
301
+ | multiple testing: Hochberg (n p-values) | statsmodels | 82.0 | 108.0 | **1.3×** |
302
+ | regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 23.1 | 10.8 | 0.47× |
303
+ | segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 43.0 | 290.0 | **6.8×** |
304
+ | segmentation: Hausdorff distance (50 images) | SciPy | 27.0 | 20.2 | 0.75× |
305
+ | statistics: Cramér's V (5×5 table) | SciPy | 0.5 | 0.4 | 0.88× |
306
+ | statistics: Mann–Whitney U | SciPy | 349.7 | 350.2 | **1.0×** |
307
+ | statistics: Welch t-test | SciPy | 14.5 | 7.2 | 0.49× |
304
308
 
305
309
  `evaluate()` validates inputs once and builds the confusion matrix once for all metrics, which is where most
306
310
  of the speed-up comes from. Hypothesis tests use SciPy underneath, so they match its speed at best; rows
@@ -1,12 +1,12 @@
1
1
  evalsuite/__init__.py,sha256=5QLHlf6OX5UMBiPPpo9TrIhJYgCmqLXlzOgUkhPxxKw,5267
2
2
  evalsuite/__main__.py,sha256=Nq90_8VlUN1mif-AVgX-kZRL63bd94F5-F7F9UkVZD4,117
3
3
  evalsuite/api.py,sha256=TU49b4DolAm6dwU3ypJprOEzv5hJOsnRiXLwccyaGew,10170
4
- evalsuite/benchmarks.py,sha256=aKeukw4y89NjiU2XiA1K5Nwk34bAAcGspomZK1u3xQc,23122
4
+ evalsuite/benchmarks.py,sha256=GzC6yNdxkih6Coe8hNqUSxTc6NLVfOP5MOK1K1TDuUo,26992
5
5
  evalsuite/calibration.py,sha256=sP7XO29Lk6wNp7qS5INNDtpz6SuVGGZhYUxSrTUxj94,14979
6
6
  evalsuite/plot.py,sha256=tfyuIF_8m-lrn87Eqq7GkQG5OWfD9yQNBRGKHCtYVLk,20016
7
7
  evalsuite/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  evalsuite/reporting.py,sha256=-LISv9vvEnltT26GtzLjVlxOCcAwmjv4-HyB8_1UF5o,9615
9
- evalsuite/version.py,sha256=LEC6quDhVmFCkRDuLm6La-9rNPb-dPGubQJbxTxof8g,98
9
+ evalsuite/version.py,sha256=FZk8M-bQVGqb0YvmxRi1eu5Z11BEwSB4mexHdE1qj48,98
10
10
  evalsuite/classification/__init__.py,sha256=ErBIXbBGnhfW6v-1Cn5SegiERoyxNlmO1tEKcLM15mA,833
11
11
  evalsuite/classification/_common.py,sha256=3ANV33ix_mnOzvOAG8glyJO-iYVhhTGO0S3YG0ZddB8,4543
12
12
  evalsuite/classification/metrics.py,sha256=8-RPjzuFUKJYlXndrDv6kXlzRdx6aR2lHp7mzKsas2g,36969
@@ -22,11 +22,11 @@ evalsuite/core/export.py,sha256=6M_ktQzW3E7usOrwqv5f5zOEdoi48JD6CvgyRpDkGvM,4303
22
22
  evalsuite/core/registry.py,sha256=3l2_o2PlckuKY89HxfnxPLf-sDaCe_-McUcAcUmXusk,2884
23
23
  evalsuite/core/result.py,sha256=L_fQZRGRgVgzII2jZb_jpRBK2jy-CdO5u9s-6pUk9fE,14927
24
24
  evalsuite/core/types.py,sha256=r6bhUk7CYAcrlQzjmf-6xWQsNqO3EOipZD-e-2yAJTI,720
25
- evalsuite/core/validation.py,sha256=uCD2QbWQWejctdy8ZwO9oL7NfvpCSYr6ZFc6ZPw18Bk,9071
25
+ evalsuite/core/validation.py,sha256=nWWyISLdW2TtzxUs3gT-Qx2uh6euDOlYMvbnCrFRYME,9975
26
26
  evalsuite/regression/__init__.py,sha256=t2XEHUw6e5tyWBHcEeiog2WWd-wwaxf4m5AIX8dJyng,571
27
- evalsuite/regression/metrics.py,sha256=jVdzmegdpBdyJNCSm1dFR1E-9VHDmQmTVtyrqyjk3KY,22459
27
+ evalsuite/regression/metrics.py,sha256=5i3hmiFfZGfmqtFDaDOxGHRYCXzdt48-GNfgvVOX7CM,22914
28
28
  evalsuite/stats/__init__.py,sha256=mtnEqpvlvscQ3C6ceRr8xLKoSHPQWsWB24pEVlvp6fs,1187
29
- evalsuite/stats/_resolve.py,sha256=SBuKR-jfqhBTZWYcUnD0RsrBryzGqn6QBYwBw48zyTc,4759
29
+ evalsuite/stats/_resolve.py,sha256=QhvI6A2Ui2gbS1qY5AAofz47hYO_DSoyQtKlRA2wZ7M,5036
30
30
  evalsuite/stats/compare.py,sha256=2c66hQNgsxl2DBEOeebl-SRxQ83ZkO_71lAfvf9jZEU,17998
31
31
  evalsuite/stats/effect.py,sha256=Esw1Di0BSabiErcsxoUinB9V0-ByEQ5fTLzAptf-dBo,6943
32
32
  evalsuite/stats/hypothesis.py,sha256=Sd3vcPUYVs_QwZ0IKKgQEFqtFcFan_CTy0EHjHEgIF4,12773
@@ -35,9 +35,9 @@ evalsuite/stats/paired.py,sha256=YK3AFndChKeCEpK512u56VtRlKvhlGdkFLHHYe77zmc,824
35
35
  evalsuite/stats/results.py,sha256=nqXHUiW8l0fQJkbMiunU6Gz-2be1NApZ6Fif2B1K_Yw,4407
36
36
  evalsuite/vision/__init__.py,sha256=9gpOBTPveM_srE6BM-SgCssdQurya2LPHhoMLGOh2A0,991
37
37
  evalsuite/vision/detection.py,sha256=eUnt5iFUVLEoUl1RB51q-vfr5juDCD3YH8ttLFrqxFg,27781
38
- evalsuite/vision/segmentation.py,sha256=s1dWXtwWd4-hprUOmARQCBhStLQROtc480Z9QNUCgPE,38311
39
- evalsuite_python-0.3.0.dist-info/METADATA,sha256=p6HSajrShVBi_YkL86kd4NYFwQskW2QRp6ZNzoOgZ8s,17364
40
- evalsuite_python-0.3.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
41
- evalsuite_python-0.3.0.dist-info/entry_points.txt,sha256=YYIwisn4L4sQt3ntzbM0_Z4yZE2KCu3GJ6KfWL1kIwc,54
42
- evalsuite_python-0.3.0.dist-info/licenses/LICENSE,sha256=cRRQ6Kk8w2qCqtDSSUJQGy-0rqjmjJG09cZ6iIiMvdc,1095
43
- evalsuite_python-0.3.0.dist-info/RECORD,,
38
+ evalsuite/vision/segmentation.py,sha256=7h0AirBU0G6XMWzdA7MQNO52hXz8oboNAX9s55y6SVQ,38647
39
+ evalsuite_python-0.3.1.dist-info/METADATA,sha256=75uFR4hsGdPZy40k1PwTyyubHsmSBSDOSAJK1Kyho1g,17640
40
+ evalsuite_python-0.3.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
41
+ evalsuite_python-0.3.1.dist-info/entry_points.txt,sha256=YYIwisn4L4sQt3ntzbM0_Z4yZE2KCu3GJ6KfWL1kIwc,54
42
+ evalsuite_python-0.3.1.dist-info/licenses/LICENSE,sha256=cRRQ6Kk8w2qCqtDSSUJQGy-0rqjmjJG09cZ6iIiMvdc,1095
43
+ evalsuite_python-0.3.1.dist-info/RECORD,,