dataeval 1.2.0rc1__tar.gz → 1.2.0rc2__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 (245) hide show
  1. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/PKG-INFO +1 -1
  2. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/pyproject.toml +1 -1
  3. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_version.py +2 -2
  4. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_completeness.py +10 -11
  5. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/scope/_coverage.py +173 -39
  6. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_base.py +10 -1
  7. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_wasserstein.py +164 -60
  8. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/.gitignore +0 -0
  9. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/LICENSE +0 -0
  10. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/README.md +0 -0
  11. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/__init__.py +0 -0
  12. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_embeddings.py +0 -0
  13. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_experimental.py +0 -0
  14. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_helpers.py +0 -0
  15. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_log.py +0 -0
  16. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/__init__.py +0 -0
  17. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_aggregate.py +0 -0
  18. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_collisions.py +0 -0
  19. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_columns.py +0 -0
  20. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_corrections.py +0 -0
  21. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_encoding.py +0 -0
  22. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_filters.py +0 -0
  23. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_input.py +0 -0
  24. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_keyed.py +0 -0
  25. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_known.py +0 -0
  26. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_links.py +0 -0
  27. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_loading.py +0 -0
  28. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_metadata.py +0 -0
  29. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_reductions.py +0 -0
  30. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_serialize.py +0 -0
  31. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_source_groups.py +0 -0
  32. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_store.py +0 -0
  33. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/__init__.py +0 -0
  34. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_accumulator.py +0 -0
  35. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_base.py +0 -0
  36. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_block.py +0 -0
  37. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_classification.py +0 -0
  38. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_data.py +0 -0
  39. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_dataset.py +0 -0
  40. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_detection.py +0 -0
  41. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_factors.py +0 -0
  42. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_frames.py +0 -0
  43. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_gather.py +0 -0
  44. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_instances.py +0 -0
  45. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_layout.py +0 -0
  46. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_ordering.py +0 -0
  47. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_propagation.py +0 -0
  48. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_reporting.py +0 -0
  49. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_reserved.py +0 -0
  50. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_select.py +0 -0
  51. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_source_index.py +0 -0
  52. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_metadata/_structurers/_tracking.py +0 -0
  53. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/_ontology.py +0 -0
  54. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/bias/__init__.py +0 -0
  55. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/bias/_balance.py +0 -0
  56. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/bias/_diversity.py +0 -0
  57. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/bias/_parity.py +0 -0
  58. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/config.py +0 -0
  59. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/__init__.py +0 -0
  60. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_alignment.py +0 -0
  61. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_annotation_match.py +0 -0
  62. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_ap_bounds.py +0 -0
  63. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_association.py +0 -0
  64. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_ber.py +0 -0
  65. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_bin.py +0 -0
  66. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/__init__.py +0 -0
  67. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_base.py +0 -0
  68. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_cache.py +0 -0
  69. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_descriptivestats.py +0 -0
  70. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_dimensionstats.py +0 -0
  71. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_egostats.py +0 -0
  72. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_hashstats.py +0 -0
  73. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_persistencestats.py +0 -0
  74. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_pixelstats.py +0 -0
  75. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_redundancystats.py +0 -0
  76. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_register.py +0 -0
  77. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_registry.py +0 -0
  78. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_video_base.py +0 -0
  79. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_video_register.py +0 -0
  80. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_calculators/_visualstats.py +0 -0
  81. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_clusterer.py +0 -0
  82. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_combination_scores.py +0 -0
  83. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_compute_ratios.py +0 -0
  84. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_compute_stats.py +0 -0
  85. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_concentration.py +0 -0
  86. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_consensus.py +0 -0
  87. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_coverage.py +0 -0
  88. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_divergence.py +0 -0
  89. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_diversity.py +0 -0
  90. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_effective_n.py +0 -0
  91. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_ego_stats.py +0 -0
  92. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_fast_hdbscan/_cluster_trees.py +0 -0
  93. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_fast_hdbscan/_disjoint_set.py +0 -0
  94. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_fast_hdbscan/_mst.py +0 -0
  95. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_feature_distance.py +0 -0
  96. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_hash.py +0 -0
  97. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_hash_index.py +0 -0
  98. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_image_stats.py +0 -0
  99. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_alignment.py +0 -0
  100. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_cooccurrence.py +0 -0
  101. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_coverage.py +0 -0
  102. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_errors.py +0 -0
  103. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_parity.py +0 -0
  104. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_reconciliation.py +0 -0
  105. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_label_stats.py +0 -0
  106. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_local_panels.py +0 -0
  107. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_metadata_insights.py +0 -0
  108. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_mst.py +0 -0
  109. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_mutual_info.py +0 -0
  110. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_nullmodel.py +0 -0
  111. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_ontology_validation.py +0 -0
  112. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_parity.py +0 -0
  113. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_persistence.py +0 -0
  114. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_rank.py +0 -0
  115. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_rao_scott.py +0 -0
  116. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_sequence_match.py +0 -0
  117. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_sequence_residuals.py +0 -0
  118. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_temporal.py +0 -0
  119. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_track_stats.py +0 -0
  120. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_uap.py +0 -0
  121. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_video_runner.py +0 -0
  122. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/core/_video_stats.py +0 -0
  123. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/__init__.py +0 -0
  124. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_camera_travel.py +0 -0
  125. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_chain.py +0 -0
  126. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_classbalance.py +0 -0
  127. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_classfilter.py +0 -0
  128. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_crop.py +0 -0
  129. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_crops.py +0 -0
  130. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_filter.py +0 -0
  131. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_frames.py +0 -0
  132. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_geometry.py +0 -0
  133. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_indices.py +0 -0
  134. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_invalidates.py +0 -0
  135. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_label_selectors.py +0 -0
  136. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_limit.py +0 -0
  137. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_locate.py +0 -0
  138. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_merge.py +0 -0
  139. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_planners.py +0 -0
  140. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_quality_gate.py +0 -0
  141. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_reads.py +0 -0
  142. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_relabel.py +0 -0
  143. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_resize.py +0 -0
  144. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_reverse.py +0 -0
  145. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_segments.py +0 -0
  146. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_selectchannels.py +0 -0
  147. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_selectors.py +0 -0
  148. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_shuffle.py +0 -0
  149. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_split.py +0 -0
  150. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_stitch.py +0 -0
  151. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_torchvision.py +0 -0
  152. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_tracks.py +0 -0
  153. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_unzip.py +0 -0
  154. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_video_proxies.py +0 -0
  155. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/data/_view.py +0 -0
  156. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/exceptions.py +0 -0
  157. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/__init__.py +0 -0
  158. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_bovw.py +0 -0
  159. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_flatten.py +0 -0
  160. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_geometry.py +0 -0
  161. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_onnx.py +0 -0
  162. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_scores.py +0 -0
  163. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_torch.py +0 -0
  164. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_uncertainty.py +0 -0
  165. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/extractors/_videotorch.py +0 -0
  166. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/flags.py +0 -0
  167. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/models/__init__.py +0 -0
  168. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/models/_backends.py +0 -0
  169. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/models/_input.py +0 -0
  170. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/models/_metadata.py +0 -0
  171. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/models/_predictors.py +0 -0
  172. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/__init__.py +0 -0
  173. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_aggregator.py +0 -0
  174. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility.py +0 -0
  175. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility_classification.py +0 -0
  176. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility_constants.py +0 -0
  177. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility_decide.py +0 -0
  178. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility_evidence.py +0 -0
  179. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility_target.py +0 -0
  180. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_feasibility_units.py +0 -0
  181. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_output.py +0 -0
  182. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/_sufficiency.py +0 -0
  183. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/performance/schedules.py +0 -0
  184. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/protocols.py +0 -0
  185. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/py.typed +0 -0
  186. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/__init__.py +0 -0
  187. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_duplicates.py +0 -0
  188. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_factor_stats.py +0 -0
  189. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_outliers.py +0 -0
  190. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_outliers_neighbors.py +0 -0
  191. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_outliers_rows.py +0 -0
  192. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_outliers_scores.py +0 -0
  193. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/quality/_shared.py +0 -0
  194. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/scope/__init__.py +0 -0
  195. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/scope/_prioritize.py +0 -0
  196. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/scope/_representation.py +0 -0
  197. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/__init__.py +0 -0
  198. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/__init__.py +0 -0
  199. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_chunk.py +0 -0
  200. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_domain_classifier.py +0 -0
  201. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_domain_spec.py +0 -0
  202. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_kneighbors.py +0 -0
  203. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_known_domains.py +0 -0
  204. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_mmd.py +0 -0
  205. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_reconstruction.py +0 -0
  206. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_drift/_univariate.py +0 -0
  207. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_ood/__init__.py +0 -0
  208. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_ood/_base.py +0 -0
  209. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_ood/_domain_classifier.py +0 -0
  210. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_ood/_kneighbors.py +0 -0
  211. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_ood/_reconstruction.py +0 -0
  212. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_shared/__init__.py +0 -0
  213. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_shared/_domain_classifier.py +0 -0
  214. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_shared/_kneighbors.py +0 -0
  215. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/_shared/_reconstruction.py +0 -0
  216. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/shift/update_strategies.py +0 -0
  217. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/__init__.py +0 -0
  218. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_array.py +0 -0
  219. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_config.py +0 -0
  220. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_evaluator.py +0 -0
  221. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_execution.py +0 -0
  222. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_factors.py +0 -0
  223. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_index.py +0 -0
  224. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_known_factors.py +0 -0
  225. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_ontology.py +0 -0
  226. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_output.py +0 -0
  227. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_removal.py +0 -0
  228. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_schema.py +0 -0
  229. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_target.py +0 -0
  230. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_task.py +0 -0
  231. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/types/_track.py +0 -0
  232. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/__init__.py +0 -0
  233. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/_array.py +0 -0
  234. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/_internal.py +0 -0
  235. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/_mask.py +0 -0
  236. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/_merge.py +0 -0
  237. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/data.py +0 -0
  238. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/losses.py +0 -0
  239. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/models.py +0 -0
  240. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/onnx.py +0 -0
  241. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/preprocessing.py +0 -0
  242. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/scipy/__init__.py +0 -0
  243. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/scipy/stats.py +0 -0
  244. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/thresholds.py +0 -0
  245. {dataeval-1.2.0rc1 → dataeval-1.2.0rc2}/src/dataeval/utils/training.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dataeval
3
- Version: 1.2.0rc1
3
+ Version: 1.2.0rc2
4
4
  Summary: DataEval provides a simple interface to characterize image data and its impact on model performance across classification and object-detection tasks
5
5
  Project-URL: Homepage, https://dataeval.ai/
6
6
  Project-URL: Repository, https://github.com/aria-ml/dataeval/
@@ -130,7 +130,7 @@ docs = [
130
130
  "numpy>=1.24.2", # floor capped by the JATIC program standard; uv.lock resolves current
131
131
  "av>=12.0.0",
132
132
  "certifi>=2024.07.04",
133
- "dataeval-plots[plotly]>=0.0.15",
133
+ "dataeval-plots[plotly]>=0.0.16",
134
134
  "ipykernel>=6.26.0",
135
135
  "ipywidgets>=8.1.1",
136
136
  "jinja2>=3.1.6",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.2.0rc1'
22
- __version_tuple__ = version_tuple = (1, 2, 0, 'rc1')
21
+ __version__ = version = '1.2.0rc2'
22
+ __version_tuple__ = version_tuple = (1, 2, 0, 'rc2')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -27,10 +27,13 @@ class CompletenessResult(TypedDict):
27
27
  space actually spanned by embeddings.
28
28
  nearest_neighbor_pairs : Sequence[tuple[int, int]]
29
29
  Sequence of tuples (i, j) representing point indices and their nearest neighbors,
30
- sorted by decreasing nearest neighbor distance. Each pair appears only once.
30
+ sorted by decreasing nearest neighbor distance. Each pair appears only once, so two
31
+ points that are each other's nearest neighbor share one entry. ``j`` is never ``i``,
32
+ even when ``i`` has an exact duplicate.
31
33
  nearest_neighbor_distances : Sequence[float]
32
34
  Distance between each pair in ``nearest_neighbor_pairs``, aligned 1:1 and sharing
33
35
  the same decreasing order. The small-distance tail flags near-duplicate samples.
36
+ Each point's own nearest-neighbor distance is the smallest among the pairs it is in.
34
37
  """
35
38
 
36
39
  completeness: float
@@ -246,16 +249,12 @@ def completeness(embeddings: Array) -> CompletenessResult:
246
249
  nearest_neighbor_distances=[],
247
250
  )
248
251
 
249
- # Fit nearest neighbors model (k=2 because first neighbor is always the point itself)
250
- nn_model = NearestNeighbors(n_neighbors=2, algorithm="auto")
251
- nn_model.fit(embeddings)
252
-
253
- # Get distances and indices for nearest neighbors
254
- distances, indices = nn_model.kneighbors(embeddings)
255
-
256
- # Extract the actual nearest neighbor (second column, index 1)
257
- nearest_neighbors = indices[:, 1]
258
- nearest_distances = distances[:, 1]
252
+ # Each point's nearest other point. Querying the fitted points themselves (no X) makes sklearn drop each
253
+ # point from its own neighbors by index, which stays right for exact duplicates: there a zero-distance tie
254
+ # can rank the point after its copy, so "the first neighbor is the point itself" does not hold.
255
+ distances, indices = NearestNeighbors(n_neighbors=1).fit(embeddings).kneighbors()
256
+ nearest_neighbors = indices[:, 0]
257
+ nearest_distances = distances[:, 0]
259
258
 
260
259
  # Create pairs, deduplicating so each pair appears only once
261
260
  seen_pairs = set()
@@ -15,8 +15,8 @@ label counts cannot reveal:
15
15
  varies (via :func:`~dataeval.core.completeness`). A class can spread far yet vary along a
16
16
  single axis — all images shot from one angle, lighting or zoom aside — which dispersion's
17
17
  single radius cannot distinguish.
18
- * **near-duplicate fraction** — the *redundancy*, the share of a class sitting in unusually
19
- tight nearest-neighbor pairs (repeated stock frames), derived from the same computation.
18
+ * **near-duplicate fraction** — the *redundancy*, the share of a class's images whose nearest
19
+ neighbor is unusually close (repeated stock frames), derived from the same computation.
20
20
 
21
21
  Alongside them, the global :func:`~dataeval.core.coverage_adaptive` result flags individual
22
22
  samples in under-sampled regions of the whole space.
@@ -41,12 +41,13 @@ from pydantic import PositiveFloat, PositiveInt
41
41
  from dataeval import Metadata
42
42
  from dataeval._helpers import BareLabels, is_labels_like, reject_filtered_metadata
43
43
  from dataeval._log import get_logger
44
+ from dataeval.core._completeness import CompletenessResult
44
45
  from dataeval.core._completeness import completeness as _completeness
45
46
  from dataeval.core._coverage import CoverageResult, coverage_adaptive, coverage_naive
46
47
  from dataeval.exceptions import ShapeMismatchError
47
48
  from dataeval.protocols import AnnotatedDataset, Array, ArrayLike, FeatureExtractor, LabelsLike
48
49
  from dataeval.types import ClassAxis, DataFrameOutput, Evaluator, EvaluatorConfig, set_metadata
49
- from dataeval.types._config import OpenUnitInterval
50
+ from dataeval.types._config import OpenUnitInterval, UnitInterval
50
51
 
51
52
  _logger = get_logger(__name__)
52
53
 
@@ -61,6 +62,10 @@ _PER_CLASS_SCHEMA: dict[str, pl.DataType] = {
61
62
  "isotropy": pl.Float64(),
62
63
  "near_duplicate_fraction": pl.Float64(),
63
64
  "assessable": pl.Boolean(),
65
+ # Appended, so the columns released before them keep their positions.
66
+ "is_clustered": pl.Boolean(),
67
+ "is_one_dimensional": pl.Boolean(),
68
+ "has_near_duplicates": pl.Boolean(),
64
69
  }
65
70
 
66
71
 
@@ -69,6 +74,19 @@ def _mean_dispersion(points: NDArray[np.float64]) -> float:
69
74
  return float(np.linalg.norm(points - points.mean(axis=0), axis=1).mean())
70
75
 
71
76
 
77
+ def _nearest_distances(result: CompletenessResult, n: int) -> NDArray[np.float64]:
78
+ """Each of the ``n`` points' distance to its nearest other point, from ``completeness``'s pairs.
79
+
80
+ The pairs list two mutual nearest neighbors once, so they count pairs, not points. A point's
81
+ nearest-neighbor distance is the smallest among the pairs it is in.
82
+ """
83
+ nearest = np.full(n, np.inf)
84
+ distances = np.asarray(result["nearest_neighbor_distances"], dtype=np.float64)
85
+ for points in np.asarray(result["nearest_neighbor_pairs"], dtype=np.intp).reshape(-1, 2).T:
86
+ np.minimum.at(nearest, points, distances)
87
+ return nearest
88
+
89
+
72
90
  class CoverageOutput(DataFrameOutput):
73
91
  """
74
92
  A dataset's per-class embedding-space coverage.
@@ -76,10 +94,47 @@ class CoverageOutput(DataFrameOutput):
76
94
  The wrapped DataFrame is the per-class breakdown — one row per class, sorted with the
77
95
  lowest-dispersion assessable classes first (the ones most worth collecting varied data
78
96
  for) and unassessable classes last — with columns ``class``, ``count``, ``uncovered``,
79
- ``uncovered_fraction``, ``dispersion``, ``isotropy``, ``near_duplicate_fraction``, and
80
- ``assessable``. ``isotropy`` and ``near_duplicate_fraction`` are null for classes below
81
- the sample floors that make them meaningful. The sample-level coverage detail hangs off
82
- it as attributes.
97
+ ``uncovered_fraction``, ``dispersion``, ``isotropy``, ``near_duplicate_fraction``,
98
+ ``assessable``, ``is_clustered``, ``is_one_dimensional``, and ``has_near_duplicates``.
99
+ ``isotropy`` and ``near_duplicate_fraction`` are null for classes below the sample floors
100
+ that make them meaningful. The sample-level coverage detail hangs off it as attributes.
101
+
102
+ The last three columns are a verdict on each variety signal, with cutoffs that are
103
+ parameters of :class:`Coverage`. They are independent, and a class can trip more than one:
104
+
105
+ - is_clustered: bool - True if dispersion < dispersion_factor (default 0.5): the class
106
+ spreads less than half as far from its centroid as the median class.
107
+ - is_one_dimensional: bool - True if isotropy < isotropy_factor (default 0.5) x the median
108
+ isotropy of the classes that report one, the class itself included. Isotropy is not
109
+ normalized by the evaluator, since it depends on the embedding dimension and on any
110
+ reduction applied first, so it is judged against its peers.
111
+ - has_near_duplicates: bool - True if near_duplicate_fraction > near_duplicate_threshold
112
+ (default 0.1).
113
+
114
+ A flag is null where it cannot judge, which is new: the flags of
115
+ :class:`~dataeval.bias.Balance` and :class:`~dataeval.bias.Diversity` are never null. A
116
+ flag is null where its signal is null, that is, for a class below ``min_class_samples`` or,
117
+ for ``is_one_dimensional``, below the isotropy sample floor. It is also null on every row
118
+ where the median it is judged against is not positive. A median spread of 0 leaves
119
+ ``dispersion`` in raw embedding units, and a median isotropy of 0 would flag no class at
120
+ all. With a single assessable class the median is the class itself, so neither
121
+ ``is_clustered`` nor ``is_one_dimensional`` can trip however flat the class is.
122
+
123
+ The defaults are conservative, so a flag means a class has collapsed, not that it sits a
124
+ little below its peers. Tighten a factor to catch a partial collapse. They are tuned for
125
+ deep embeddings, and two signals depend on more than the data:
126
+
127
+ - Isotropy rises with class size, so a small class reads lower beside large ones.
128
+ Tighten ``isotropy_factor`` only for classes of similar size.
129
+ - The near-duplicate share of a class with no duplicates rises as the embedding dimension
130
+ falls. On simulated Gaussian classes of 100 to 400 images it exceeded 0.1 for most
131
+ classes in 2 dimensions, for about half in 3 and for a few up to 7, and for none in 8
132
+ or more. Raise ``near_duplicate_threshold`` before reading ``has_near_duplicates`` on a
133
+ low-dimensional embedding, such as a few principal components.
134
+
135
+ .. versionadded:: 1.2
136
+ The ``is_clustered``, ``is_one_dimensional`` and ``has_near_duplicates`` columns,
137
+ after ``assessable`` so the earlier columns keep their positions.
83
138
 
84
139
  Attributes
85
140
  ----------
@@ -130,8 +185,12 @@ class Coverage(Evaluator):
130
185
  class); low means clustered even if numerous.
131
186
  * **isotropy** — shape of that spread (in how many independent directions the class
132
187
  varies); low means it varies along few axes even when dispersion is high.
133
- * **near_duplicate_fraction** — redundancy (share of the class in unusually tight
134
- nearest-neighbor pairs); high means repeated / near-identical samples.
188
+ * **near_duplicate_fraction** — redundancy (share of the class's images whose nearest
189
+ neighbor is unusually close); high means repeated / near-identical samples.
190
+
191
+ Each signal comes with a boolean verdict, ``is_clustered``, ``is_one_dimensional`` and
192
+ ``has_near_duplicates``, so the output table is the per-class gap report. See
193
+ :class:`CoverageOutput` for the exact rules and when a flag is null.
135
194
 
136
195
  Parameters
137
196
  ----------
@@ -156,8 +215,27 @@ class Coverage(Evaluator):
156
215
  effective-dimension estimate is degenerate when samples do not exceed dimensions).
157
216
  When None, defaults to one more than the embedding dimensionality.
158
217
  near_duplicate_factor : float, default 0.5
159
- A nearest-neighbor pair counts as a near-duplicate when its distance is below this
160
- fraction of the class's median nearest-neighbor distance (scale-free).
218
+ An image counts as a near-duplicate when its nearest neighbor in its class is no farther
219
+ than this fraction of a typical class's median nearest-neighbor distance (scale-free).
220
+ A scale for a distance, which sets ``near_duplicate_fraction``. Not to be confused with
221
+ ``near_duplicate_threshold``, the share of images that sets the flag.
222
+ dispersion_factor : float, default 0.5
223
+ ``is_clustered`` is True where ``dispersion < dispersion_factor``, that is, where a class
224
+ spreads less than this share of the median class's spread. Must be in (0, 1); a factor
225
+ near 1 flags about half the classes by construction.
226
+
227
+ .. versionadded:: 1.2
228
+ isotropy_factor : float, default 0.5
229
+ ``is_one_dimensional`` is True where ``isotropy < isotropy_factor * median(isotropy)``,
230
+ the median taken over the classes that report an isotropy. Must be in (0, 1).
231
+
232
+ .. versionadded:: 1.2
233
+ near_duplicate_threshold : float, default 0.1
234
+ ``has_near_duplicates`` is True where ``near_duplicate_fraction`` is above this share of
235
+ the class's images. Must be in [0, 1]: 0 flags any near-duplicate, and 1 turns the flag
236
+ off. A share, where ``near_duplicate_factor`` is a distance scale.
237
+
238
+ .. versionadded:: 1.2
161
239
  config : Coverage.Config or None, default None
162
240
  Optional configuration object; parameters passed directly to ``__init__`` override
163
241
  its values.
@@ -185,10 +263,25 @@ class Coverage(Evaluator):
185
263
  high-dimensional extractor most classes fall below that floor and report ``isotropy``
186
264
  as null rather than zero. Reduce the embedding dimensionality (for example with PCA) if
187
265
  you need the signal on classes of a few hundred samples. ``near_duplicate_fraction`` is the
188
- share of within-class nearest-neighbor pairs closer than ``near_duplicate_factor`` x the
189
- class median, surfacing repeated / near-identical samples that inflate counts without
190
- adding variety. Embeddings are auto-rescaled to the unit interval for the coverage
191
- computation. This evaluator assumes one embedding per label (image classification). For
266
+ share of a class's images whose nearest other image in the class is within
267
+ ``near_duplicate_factor`` x the *median* across assessable classes of each class's median
268
+ nearest-neighbor distance, surfacing repeated / near-identical samples that inflate counts
269
+ without adding variety. Both images of a near-identical pair count, and an exact copy counts
270
+ the same as one that differs by float noise. Like ``dispersion``, the scale comes from the
271
+ peers rather than the class itself, so a tight clump that is most of its class still shows;
272
+ the cost is that a class whose images sit that close together by nature reads high too.
273
+ With a single assessable class there are no peers, and a clump of more than half of it goes
274
+ unflagged. Embeddings are auto-rescaled to the unit interval for the coverage
275
+ computation.
276
+
277
+ The flag defaults are conservative and tuned for deep embeddings: at 0.5 a flag means a
278
+ class has collapsed. A flag is null where its signal is null, and on every row where the
279
+ median it is judged against is not positive. On an embedding of fewer than about 8
280
+ dimensions, healthy classes can have a near-duplicate share above the 0.1 default, so raise
281
+ ``near_duplicate_threshold`` there. Isotropy rises with class size, so tighten
282
+ ``isotropy_factor`` only for classes of similar size.
283
+
284
+ This evaluator assumes one embedding per label (image classification). For
192
285
  object detection, wrap the dataset with :class:`~dataeval.data.DetectionCrops` to present
193
286
  its boxes as an image-classification dataset (one crop per detection, aligned 1:1 with the
194
287
  labels) and evaluate that, or supply detection-level embeddings you have computed yourself.
@@ -244,8 +337,17 @@ class Coverage(Evaluator):
244
337
  isotropy_min_samples : int or None, default None
245
338
  Minimum samples for a class's isotropy to be reported (None: dims + 1).
246
339
  near_duplicate_factor : float, default 0.5
247
- Fraction of the class median nearest-neighbor distance below which a pair is a
248
- near-duplicate.
340
+ Fraction of a typical class's median nearest-neighbor distance within which an
341
+ image's nearest neighbor makes it a near-duplicate (a distance scale, which sets
342
+ ``near_duplicate_fraction``).
343
+ dispersion_factor : float, default 0.5
344
+ ``is_clustered`` is True where ``dispersion`` is below this, in (0, 1).
345
+ isotropy_factor : float, default 0.5
346
+ ``is_one_dimensional`` is True where ``isotropy`` is below this share of the median
347
+ isotropy, in (0, 1).
348
+ near_duplicate_threshold : float, default 0.1
349
+ ``has_near_duplicates`` is True where ``near_duplicate_fraction`` is above this
350
+ share of images, in [0, 1].
249
351
  """
250
352
 
251
353
  extractor: FeatureExtractor | None = None
@@ -256,6 +358,9 @@ class Coverage(Evaluator):
256
358
  min_class_samples: PositiveInt = 20
257
359
  isotropy_min_samples: PositiveInt | None = None
258
360
  near_duplicate_factor: PositiveFloat = 0.5
361
+ dispersion_factor: OpenUnitInterval = 0.5
362
+ isotropy_factor: OpenUnitInterval = 0.5
363
+ near_duplicate_threshold: UnitInterval = 0.1
259
364
 
260
365
  # Set by apply_config from Config.
261
366
  extractor: FeatureExtractor | None
@@ -266,6 +371,9 @@ class Coverage(Evaluator):
266
371
  min_class_samples: int
267
372
  isotropy_min_samples: int | None
268
373
  near_duplicate_factor: float
374
+ dispersion_factor: float
375
+ isotropy_factor: float
376
+ near_duplicate_threshold: float
269
377
  config: Config
270
378
 
271
379
  def __init__(
@@ -279,6 +387,9 @@ class Coverage(Evaluator):
279
387
  min_class_samples: int | None = None,
280
388
  isotropy_min_samples: int | None = None,
281
389
  near_duplicate_factor: float | None = None,
390
+ dispersion_factor: float | None = None,
391
+ isotropy_factor: float | None = None,
392
+ near_duplicate_threshold: float | None = None,
282
393
  config: Config | None = None,
283
394
  ) -> None:
284
395
  super().__init__(locals())
@@ -340,20 +451,6 @@ class Coverage(Evaluator):
340
451
  """Minimum class size for a non-degenerate isotropy estimate (samples must exceed dims)."""
341
452
  return self.isotropy_min_samples if self.isotropy_min_samples is not None else dim + 1
342
453
 
343
- def _near_duplicate_fraction(self, distances: Any) -> float | None:
344
- """Share of nearest-neighbor pairs closer than ``near_duplicate_factor`` x the class median.
345
-
346
- Relative-to-median so it is scale-free. A zero median means at least half the pairs are
347
- exact duplicates, so the exact-duplicate share is reported instead.
348
- """
349
- dists = np.asarray(distances, dtype=np.float64)
350
- if dists.size == 0:
351
- return None
352
- median = float(np.median(dists))
353
- if median == 0.0:
354
- return float(np.mean(dists == 0.0))
355
- return float(np.mean(dists < self.near_duplicate_factor * median))
356
-
357
454
  def _per_class(
358
455
  self,
359
456
  embeddings: NDArray[np.float64],
@@ -376,7 +473,8 @@ class Coverage(Evaluator):
376
473
  # Normalize by the median assessable class, so a typical class scores ~1 regardless of
377
474
  # how far apart the classes sit in the embedding space (the global spread would conflate
378
475
  # within-class variety with between-class separation). Avoid /0.
379
- reference = (float(np.median(list(raw_spread.values()))) if raw_spread else 1.0) or 1.0
476
+ median_spread = float(np.median(list(raw_spread.values()))) if raw_spread else 0.0
477
+ reference = median_spread or 1.0
380
478
 
381
479
  # One completeness pass per assessable class yields both directional variety (isotropy)
382
480
  # and the nearest-neighbor distances that expose near-duplicates.
@@ -388,27 +486,46 @@ class Coverage(Evaluator):
388
486
  # don't exceed dimensions, so it needs more samples than dispersion (default: more than dims).
389
487
  isotropy_floor = max(self.min_class_samples, self._isotropy_floor(embeddings.shape[1]))
390
488
  isotropy = {cls: float(r["isotropy"]) for cls, r in results.items() if counts[cls] >= isotropy_floor}
391
- near_duplicate = {
392
- cls: self._near_duplicate_fraction(r["nearest_neighbor_distances"]) for cls, r in results.items()
393
- }
489
+ # Isotropy is not normalized, so its flag compares each class with the median class instead.
490
+ median_isotropy = float(np.median(list(isotropy.values()))) if isotropy else 0.0
491
+ # Each image's distance to its nearest other image in its class; a class of one has none.
492
+ nearest = {cls: _nearest_distances(r, counts[cls]) for cls, r in results.items() if counts[cls] > 1}
493
+ # Like dispersion, measure against a typical (median) class's nearest-neighbor spacing rather than
494
+ # the class's own, which a tight clump covering most of the class would set. `<=` keeps exact
495
+ # duplicates when that spacing is itself zero.
496
+ spacing = float(np.median([np.median(d) for d in nearest.values()])) if nearest else 0.0
497
+ near_duplicate = {cls: float(np.mean(d <= self.near_duplicate_factor * spacing)) for cls, d in nearest.items()}
394
498
 
395
499
  rows: list[dict[str, Any]] = []
396
500
  for cls, mask in masks.items():
397
501
  count = counts[cls]
398
502
  uncovered = int(is_uncovered[mask].sum())
399
503
  assessable = cls in raw_spread
504
+ dispersion = raw_spread[cls] / reference if assessable else None
505
+ class_isotropy = isotropy.get(cls)
506
+ duplicates = near_duplicate.get(cls)
400
507
  rows.append({
401
508
  "class": index2label.get(cls, str(cls)),
402
509
  "count": count,
403
510
  "uncovered": uncovered,
404
511
  "uncovered_fraction": uncovered / count if count else 0.0,
405
512
  # Spread relative to a typical class; < 1 == clustered / near-duplicate.
406
- "dispersion": raw_spread[cls] / reference if assessable else None,
513
+ "dispersion": dispersion,
407
514
  # In how many independent directions the class varies; null when too few samples.
408
- "isotropy": isotropy.get(cls),
409
- # Share of the class sitting in near-duplicate nearest-neighbor pairs.
410
- "near_duplicate_fraction": near_duplicate.get(cls),
515
+ "isotropy": class_isotropy,
516
+ # Share of the class's images whose nearest neighbor is a near-duplicate.
517
+ "near_duplicate_fraction": duplicates,
411
518
  "assessable": assessable,
519
+ # A flag is null where its signal is, or where the median it is judged against is not positive.
520
+ "is_clustered": (
521
+ dispersion < self.dispersion_factor if dispersion is not None and median_spread > 0 else None
522
+ ),
523
+ "is_one_dimensional": (
524
+ class_isotropy < self.isotropy_factor * median_isotropy
525
+ if class_isotropy is not None and median_isotropy > 0
526
+ else None
527
+ ),
528
+ "has_near_duplicates": duplicates > self.near_duplicate_threshold if duplicates is not None else None,
412
529
  })
413
530
  # Lowest-dispersion assessable classes first (most worth broadening); unassessable last.
414
531
  return sorted(rows, key=lambda row: (not row["assessable"], row["dispersion"] or 0.0, row["class"]))
@@ -474,6 +591,23 @@ class Coverage(Evaluator):
474
591
  │ plane ┆ 27 ┆ 0 ┆ 1.194517 │
475
592
  │ person ┆ 20 ┆ 1 ┆ 1.488841 │
476
593
  └────────┴───────┴───────────┴────────────┘
594
+
595
+ The flags give the verdict. ``car`` spreads less than half as far as the median class.
596
+ ``is_one_dimensional`` is null because no class has more crops than the embedding has
597
+ dimensions, so none reports an isotropy:
598
+
599
+ >>> result.data().select("class", "is_clustered", "is_one_dimensional", "has_near_duplicates")
600
+ shape: (4, 4)
601
+ ┌────────┬──────────────┬────────────────────┬─────────────────────┐
602
+ │ class ┆ is_clustered ┆ is_one_dimensional ┆ has_near_duplicates │
603
+ │ --- ┆ --- ┆ --- ┆ --- │
604
+ │ str ┆ bool ┆ bool ┆ bool │
605
+ ╞════════╪══════════════╪════════════════════╪═════════════════════╡
606
+ │ car ┆ true ┆ null ┆ false │
607
+ │ boat ┆ false ┆ null ┆ false │
608
+ │ plane ┆ false ┆ null ┆ false │
609
+ │ person ┆ false ┆ null ┆ false │
610
+ └────────┴──────────────┴────────────────────┴─────────────────────┘
477
611
  """
478
612
  reject_filtered_metadata(dataset, "Coverage")
479
613
 
@@ -102,6 +102,9 @@ class DriftOutput(DictOutput, Generic[TDetails]):
102
102
  Populated when the detector's ``extractor`` is a
103
103
  :class:`~dataeval.protocols.NamedFeatureExtractor`, such as
104
104
  :class:`~dataeval.Metadata`; empty otherwise.
105
+ baseline : Sequence[float]
106
+ Metric value of each reference chunk, from which the thresholds are
107
+ derived. Populated in chunked mode and empty otherwise.
105
108
  """
106
109
 
107
110
  drifted: bool
@@ -110,6 +113,11 @@ class DriftOutput(DictOutput, Generic[TDetails]):
110
113
  metric_name: str
111
114
  details: TDetails
112
115
  feature_names: Sequence[str] = ()
116
+ baseline: Sequence[float] = ()
117
+
118
+ @property
119
+ def plot_type(self) -> Literal["drift"]:
120
+ return "drift"
113
121
 
114
122
 
115
123
  @dataclass(frozen=True, repr=False)
@@ -550,7 +558,7 @@ class ChunkedDrift(Generic[TDetails]):
550
558
  *reference_data : Any
551
559
  Reference dataset(s), forwarded verbatim to the underlying detector's
552
560
  ``fit()``. Most detectors take a single reference; detectors with a
553
- calibrated baseline (e.g. :class:`DriftWasserstein`) take both a
561
+ validation baseline (e.g. :class:`DriftWasserstein`) take both a
554
562
  training and a validation reference. When the underlying detector has an
555
563
  ``extractor`` configured, each reference may be any input the extractor
556
564
  accepts (e.g. a full MAITE dataset or raw images); otherwise each must be
@@ -700,6 +708,7 @@ class ChunkedDrift(Generic[TDetails]):
700
708
  distance=distance,
701
709
  metric_name=self._detector._metric_name,
702
710
  feature_names=self._detector._feature_names,
711
+ baseline=tuple(float(v) for v in self._baseline_values),
703
712
  details=df,
704
713
  )
705
714