benchstats 3.3.2__tar.gz → 3.3.3__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 (29) hide show
  1. {benchstats-3.3.2 → benchstats-3.3.3}/CHANGELOG.md +6 -0
  2. {benchstats-3.3.2/src/benchstats.egg-info → benchstats-3.3.3}/PKG-INFO +1 -1
  3. {benchstats-3.3.2 → benchstats-3.3.3}/pyproject.toml +1 -1
  4. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/compare.py +16 -14
  5. {benchstats-3.3.2 → benchstats-3.3.3/src/benchstats.egg-info}/PKG-INFO +1 -1
  6. {benchstats-3.3.2 → benchstats-3.3.3}/tests/test_compare.py +4 -1
  7. {benchstats-3.3.2 → benchstats-3.3.3}/tests/test_qbench.py +12 -5
  8. {benchstats-3.3.2 → benchstats-3.3.3}/LICENSE +0 -0
  9. {benchstats-3.3.2 → benchstats-3.3.3}/MANIFEST.in +0 -0
  10. {benchstats-3.3.2 → benchstats-3.3.3}/README.md +0 -0
  11. {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple1.svg +0 -0
  12. {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple2.svg +0 -0
  13. {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple3.svg +0 -0
  14. {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple4.svg +0 -0
  15. {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple4_bad.svg +0 -0
  16. {benchstats-3.3.2 → benchstats-3.3.3}/setup.cfg +0 -0
  17. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/__init__.py +0 -0
  18. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/__main__.py +0 -0
  19. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/cli_parser.py +0 -0
  20. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/common.py +0 -0
  21. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/parser_GbenchJson.py +0 -0
  22. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/parsers.py +0 -0
  23. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/qbench.py +0 -0
  24. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/render.py +0 -0
  25. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/SOURCES.txt +0 -0
  26. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/dependency_links.txt +0 -0
  27. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/entry_points.txt +0 -0
  28. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/requires.txt +0 -0
  29. {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/top_level.txt +0 -0
@@ -1,3 +1,9 @@
1
+ # 3.3.3
2
+ - breaking change, though unlikely important for anyone: due to T-test also being affected by
3
+ bad date, renamed `compareStats()` parameter `brunnermunzel_workaround` to `edge_cases_workaround`
4
+ and extended its effect on t-test.
5
+ - Fix `_at_least_one_differs` flag handling in `CompareStatsResult::updatePvalStats()`.
6
+
1
7
  # 3.3.1-.2
2
8
  - Changed the order of default metrics of `qbench.showBench()` to make `mean` the leading metric, as
3
9
  it's typically the most correct one.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: benchstats
3
- Version: 3.3.2
3
+ Version: 3.3.3
4
4
  Summary: Statistical Testing for Benchmark Results Comparison
5
5
  Author-email: Aleksei Rechinskii <5dpea9nhd@mozmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "benchstats"
7
- version = "3.3.2"
7
+ version = "3.3.3"
8
8
  description = "Statistical Testing for Benchmark Results Comparison"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Aleksei Rechinskii", email = "5dpea9nhd@mozmail.com" }]
@@ -162,6 +162,7 @@ class CompareStatsResult:
162
162
  assert isinstance(other, CompareStatsResult)
163
163
  assert other.method == self.method and other.alpha == self.alpha
164
164
  assert self._results.keys() == other._results.keys()
165
+ self._at_least_one_differs = self._at_least_one_differs or other._at_least_one_differs
165
166
  for bm, omdict in other._results.items():
166
167
  mdict = self._results[bm]
167
168
  assert mdict.keys() == omdict.keys()
@@ -249,7 +250,7 @@ def compareStats(
249
250
  main_metrics: None | list[str] | tuple[str] = None,
250
251
  debug_log: None | bool | LoggingConsole = True,
251
252
  store_sets: bool = False,
252
- brunnermunzel_workaround: None | bool = None,
253
+ edge_cases_workaround: None | bool = None,
253
254
  ) -> CompareStatsResult:
254
255
  """Perform comparison for statistical significance of benchmark results using a chosen
255
256
  statistical method.
@@ -281,21 +282,22 @@ def compareStats(
281
282
  `store_sets` is a bool, True will make store whole corresponding dataset into a BmCompResult
282
283
  instead of a mean of the set.
283
284
 
284
- `brunnermunzel_workaround`: Formally speaking, Brunner Munzel test method is "underspecified"
285
+ `edge_cases_workaround`: Formally speaking, Brunner Munzel test method is "underspecified"
285
286
  in a sense that by design it doesn't handle a case of non-intersecting sample sets, or sets
286
287
  consisting of the same single element (https://aakinshin.net/posts/brunner-munzel-corner-case/).
287
288
  scipy.stats.brunnermunzel() implementation at least in versions 1.10-1.15.2 directly follows the
288
289
  words of the original paper and doesn't introduce handling for that cases, which causes warnings
289
290
  and nans to appear in results. IMO from an engineering point of view this is more like just a
290
- bug (https://github.com/scipy/scipy/issues/22664). `brunnermunzel_workaround` is a flag to
291
- handle it: value of None handles the bug only for method == 'brunnermunzel', a bool otherwise
292
- controls whether to handle the bug irrespective of the method used (this might give different
293
- pvalues for other tests, but this shouldn't affect inference results unless alpha threshold
294
- value is too wild). It should be noted that for small sample sizes (under 10 elements in any of
291
+ bug (https://github.com/scipy/scipy/issues/22664). The same issue also applies to Student's
292
+ t-test. `edge_cases_workaround` is a flag to handle it: value of None handles the bug only for
293
+ affected methods, a bool otherwise controls whether to handle the bug irrespective of the method
294
+ used (this might give different pvalues for other tests, but this shouldn't affect inference
295
+ results unless alpha threshold value is too wild).
296
+ It should be noted that for small sample sizes (under 10 elements in any of
295
297
  sample sets) this most certainly leads to a wrong p-value result that underestimate a
296
298
  probability of error. However, with 10, or preferably more, sample set sizes, this should be
297
299
  negligible.
298
- While brunnermunzel_workaround allows to disable the workaround, I doubt it has any practical
300
+ While edge_cases_workaround allows to disable the workaround, I doubt it has any practical
299
301
  value in the context of benchmark results comparison.
300
302
 
301
303
  Returns an instance of CompareStatsResult class
@@ -306,7 +308,7 @@ def compareStats(
306
308
  assert isinstance(sg0, dict) and (isinstance(sg1, dict) or (with_alternatives and sg1 is None))
307
309
  assert isinstance(method, str) and method in kMethods, "unsupported method"
308
310
  assert isinstance(alpha, kAllowedFpTypes) and 0 < alpha and alpha < 0.5
309
- assert brunnermunzel_workaround is None or isinstance(brunnermunzel_workaround, bool)
311
+ assert edge_cases_workaround is None or isinstance(edge_cases_workaround, bool)
310
312
  assert main_metrics is None or isinstance(main_metrics, (list, tuple))
311
313
 
312
314
  if debug_log is None or (isinstance(debug_log, bool) and not debug_log):
@@ -317,8 +319,8 @@ def compareStats(
317
319
  logger = debug_log
318
320
  debug_log = True
319
321
 
320
- if brunnermunzel_workaround is None:
321
- brunnermunzel_workaround = method == "brunnermunzel"
322
+ if edge_cases_workaround is None:
323
+ edge_cases_workaround = (method in ("brunnermunzel", "ttest_ind"))
322
324
 
323
325
  def warn(*args, **kwargs):
324
326
  if debug_log:
@@ -330,11 +332,11 @@ def compareStats(
330
332
 
331
333
  def computePValues(s0, s1):
332
334
  """Computes and return pvalues of s0 being stochastically less or greater than s1"""
333
- if brunnermunzel_workaround:
335
+ if edge_cases_workaround:
334
336
  # test for edge cases that brunnermunzel can't handle properly
335
337
  mn0, mx0, mn1, mx1 = np.min(s0), np.max(s0), np.min(s1), np.max(s1)
336
338
  assert np.all(np.isfinite([mn0, mx0, mn1, mx1])) # sanity check, more asserts below
337
- all_eq = np.all([mn0 == mx0, mn0 == mn1, mn0 == mx1])
339
+ all_eq = all([mn0 == mx0, mn0 == mn1, mn0 == mx1])
338
340
  all_less, all_greater = mx0 < mn1, mn0 > mx1
339
341
 
340
342
  if all_eq:
@@ -348,7 +350,7 @@ def compareStats(
348
350
  res_greater = stat_func(s0, s1, alternative="greater")
349
351
 
350
352
  less_pvalue, greater_pvalue = res_less.pvalue, res_greater.pvalue
351
- if brunnermunzel_workaround:
353
+ if edge_cases_workaround:
352
354
  assert np.all(np.isfinite([less_pvalue, greater_pvalue]))
353
355
 
354
356
  return less_pvalue, greater_pvalue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: benchstats
3
- Version: 3.3.2
3
+ Version: 3.3.3
4
4
  Summary: Statistical Testing for Benchmark Results Comparison
5
5
  Author-email: Aleksei Rechinskii <5dpea9nhd@mozmail.com>
6
6
  License-Expression: MIT
@@ -109,4 +109,7 @@ class TestComparisonMethods(unittest.TestCase):
109
109
 
110
110
 
111
111
  if __name__ == "__main__":
112
- unittest.main()
112
+ import sys
113
+ import pytest
114
+
115
+ sys.exit(pytest.main(sys.argv))
@@ -1,8 +1,6 @@
1
- import sys
2
1
  from typing import Iterable
3
2
  import unittest
4
3
  import numpy as np
5
- import pytest
6
4
  import time
7
5
  import traceback
8
6
 
@@ -232,7 +230,7 @@ class TestQBench(unittest.TestCase):
232
230
  check_reset_counters(7 * 2, 7 * 2, 7 * 2, 0, 7 + 14)
233
231
 
234
232
  def test_showBench_with_delimiter(self):
235
- def_prms = {"alt_delimiter": "|", "render_report": False}
233
+ def_prms = {"alt_delimiter": "|", "render_report": False, "show_progress_each": 0}
236
234
  nreps = 10
237
235
  results = np.stack(
238
236
  (
@@ -252,7 +250,7 @@ class TestQBench(unittest.TestCase):
252
250
  assert "~" == sr.results["alg2 | A vs B"]["mean"].result
253
251
 
254
252
  def test_showBench_one_name(self):
255
- def_prms = {"render_report": False}
253
+ def_prms = {"render_report": False, "show_progress_each": 0}
256
254
  nreps = 10
257
255
  results = np.stack(
258
256
  (
@@ -268,7 +266,7 @@ class TestQBench(unittest.TestCase):
268
266
  assert ">" == sr.results["code | 0 vs 2"]["mean"].result
269
267
 
270
268
  def test_showBench_tuple(self):
271
- def_prms = {"render_report": False}
269
+ def_prms = {"render_report": False, "show_progress_each": 0}
272
270
  nreps = 10
273
271
  results = np.stack(
274
272
  (
@@ -295,6 +293,7 @@ class TestQBench(unittest.TestCase):
295
293
  alpha=0.499,
296
294
  render_report=False,
297
295
  console=None,
296
+ show_progress_each=0,
298
297
  )
299
298
  # seed is chosen to modify the first bootstrap result and produce the same second result
300
299
 
@@ -306,12 +305,20 @@ class TestQBench(unittest.TestCase):
306
305
  assert len(pvs["~"]) == same_len
307
306
  assert all(pvs["~"][i] == 1.0 for i in range(same_len))
308
307
 
308
+ cr = qb.showBench(r, pvalue_stats_bootstrap=0, **cmnargs)
309
+ assert not cr.at_least_one_differs
310
+
309
311
  cr = qb.showBench(r, pvalue_stats_bootstrap=1, **cmnargs)
312
+ assert cr.at_least_one_differs
310
313
  test_pvs(next(iter(next(iter(cr.pval_stats.values())).values())), 1)
311
314
 
312
315
  cr = qb.showBench(r, pvalue_stats_bootstrap=2, **cmnargs)
316
+ assert cr.at_least_one_differs
313
317
  test_pvs(next(iter(next(iter(cr.pval_stats.values())).values())), 2)
314
318
 
315
319
 
316
320
  if __name__ == "__main__":
321
+ import sys
322
+ import pytest
323
+
317
324
  sys.exit(pytest.main(sys.argv))
File without changes
File without changes
File without changes
File without changes