benchstats 3.3.1__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.1 → benchstats-3.3.3}/CHANGELOG.md +8 -1
  2. {benchstats-3.3.1/src/benchstats.egg-info → benchstats-3.3.3}/PKG-INFO +1 -1
  3. {benchstats-3.3.1 → benchstats-3.3.3}/pyproject.toml +1 -1
  4. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/compare.py +20 -14
  5. {benchstats-3.3.1 → benchstats-3.3.3/src/benchstats.egg-info}/PKG-INFO +1 -1
  6. {benchstats-3.3.1 → benchstats-3.3.3}/tests/test_compare.py +4 -1
  7. {benchstats-3.3.1 → benchstats-3.3.3}/tests/test_qbench.py +12 -5
  8. {benchstats-3.3.1 → benchstats-3.3.3}/LICENSE +0 -0
  9. {benchstats-3.3.1 → benchstats-3.3.3}/MANIFEST.in +0 -0
  10. {benchstats-3.3.1 → benchstats-3.3.3}/README.md +0 -0
  11. {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple1.svg +0 -0
  12. {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple2.svg +0 -0
  13. {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple3.svg +0 -0
  14. {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple4.svg +0 -0
  15. {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple4_bad.svg +0 -0
  16. {benchstats-3.3.1 → benchstats-3.3.3}/setup.cfg +0 -0
  17. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/__init__.py +0 -0
  18. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/__main__.py +0 -0
  19. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/cli_parser.py +0 -0
  20. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/common.py +0 -0
  21. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/parser_GbenchJson.py +0 -0
  22. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/parsers.py +0 -0
  23. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/qbench.py +0 -0
  24. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/render.py +0 -0
  25. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/SOURCES.txt +0 -0
  26. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/dependency_links.txt +0 -0
  27. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/entry_points.txt +0 -0
  28. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/requires.txt +0 -0
  29. {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/top_level.txt +0 -0
@@ -1,6 +1,13 @@
1
- # 3.3.1
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
+
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.
10
+ - Added Student's t-test support, `method='ttest_ind'`
4
11
 
5
12
  # 3.3.0
6
13
  - Added a possibility to display progress bar for p-value bootstrapping
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: benchstats
3
- Version: 3.3.1
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.1"
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" }]
@@ -17,6 +17,10 @@ kMethods = {
17
17
  "name": "Mann-Whitney U test",
18
18
  "url": "https://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U_test",
19
19
  },
20
+ "ttest_ind": {
21
+ "name": "Student's t-test",
22
+ "url": "https://en.wikipedia.org/wiki/T-test",
23
+ },
20
24
  }
21
25
 
22
26
  kDefaultAlpha = 0.001
@@ -158,6 +162,7 @@ class CompareStatsResult:
158
162
  assert isinstance(other, CompareStatsResult)
159
163
  assert other.method == self.method and other.alpha == self.alpha
160
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
161
166
  for bm, omdict in other._results.items():
162
167
  mdict = self._results[bm]
163
168
  assert mdict.keys() == omdict.keys()
@@ -245,7 +250,7 @@ def compareStats(
245
250
  main_metrics: None | list[str] | tuple[str] = None,
246
251
  debug_log: None | bool | LoggingConsole = True,
247
252
  store_sets: bool = False,
248
- brunnermunzel_workaround: None | bool = None,
253
+ edge_cases_workaround: None | bool = None,
249
254
  ) -> CompareStatsResult:
250
255
  """Perform comparison for statistical significance of benchmark results using a chosen
251
256
  statistical method.
@@ -277,21 +282,22 @@ def compareStats(
277
282
  `store_sets` is a bool, True will make store whole corresponding dataset into a BmCompResult
278
283
  instead of a mean of the set.
279
284
 
280
- `brunnermunzel_workaround`: Formally speaking, Brunner Munzel test method is "underspecified"
285
+ `edge_cases_workaround`: Formally speaking, Brunner Munzel test method is "underspecified"
281
286
  in a sense that by design it doesn't handle a case of non-intersecting sample sets, or sets
282
287
  consisting of the same single element (https://aakinshin.net/posts/brunner-munzel-corner-case/).
283
288
  scipy.stats.brunnermunzel() implementation at least in versions 1.10-1.15.2 directly follows the
284
289
  words of the original paper and doesn't introduce handling for that cases, which causes warnings
285
290
  and nans to appear in results. IMO from an engineering point of view this is more like just a
286
- bug (https://github.com/scipy/scipy/issues/22664). `brunnermunzel_workaround` is a flag to
287
- handle it: value of None handles the bug only for method == 'brunnermunzel', a bool otherwise
288
- controls whether to handle the bug irrespective of the method used (this might give different
289
- pvalues for other tests, but this shouldn't affect inference results unless alpha threshold
290
- 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
291
297
  sample sets) this most certainly leads to a wrong p-value result that underestimate a
292
298
  probability of error. However, with 10, or preferably more, sample set sizes, this should be
293
299
  negligible.
294
- 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
295
301
  value in the context of benchmark results comparison.
296
302
 
297
303
  Returns an instance of CompareStatsResult class
@@ -302,7 +308,7 @@ def compareStats(
302
308
  assert isinstance(sg0, dict) and (isinstance(sg1, dict) or (with_alternatives and sg1 is None))
303
309
  assert isinstance(method, str) and method in kMethods, "unsupported method"
304
310
  assert isinstance(alpha, kAllowedFpTypes) and 0 < alpha and alpha < 0.5
305
- assert brunnermunzel_workaround is None or isinstance(brunnermunzel_workaround, bool)
311
+ assert edge_cases_workaround is None or isinstance(edge_cases_workaround, bool)
306
312
  assert main_metrics is None or isinstance(main_metrics, (list, tuple))
307
313
 
308
314
  if debug_log is None or (isinstance(debug_log, bool) and not debug_log):
@@ -313,8 +319,8 @@ def compareStats(
313
319
  logger = debug_log
314
320
  debug_log = True
315
321
 
316
- if brunnermunzel_workaround is None:
317
- brunnermunzel_workaround = method == "brunnermunzel"
322
+ if edge_cases_workaround is None:
323
+ edge_cases_workaround = (method in ("brunnermunzel", "ttest_ind"))
318
324
 
319
325
  def warn(*args, **kwargs):
320
326
  if debug_log:
@@ -326,11 +332,11 @@ def compareStats(
326
332
 
327
333
  def computePValues(s0, s1):
328
334
  """Computes and return pvalues of s0 being stochastically less or greater than s1"""
329
- if brunnermunzel_workaround:
335
+ if edge_cases_workaround:
330
336
  # test for edge cases that brunnermunzel can't handle properly
331
337
  mn0, mx0, mn1, mx1 = np.min(s0), np.max(s0), np.min(s1), np.max(s1)
332
338
  assert np.all(np.isfinite([mn0, mx0, mn1, mx1])) # sanity check, more asserts below
333
- all_eq = np.all([mn0 == mx0, mn0 == mn1, mn0 == mx1])
339
+ all_eq = all([mn0 == mx0, mn0 == mn1, mn0 == mx1])
334
340
  all_less, all_greater = mx0 < mn1, mn0 > mx1
335
341
 
336
342
  if all_eq:
@@ -344,7 +350,7 @@ def compareStats(
344
350
  res_greater = stat_func(s0, s1, alternative="greater")
345
351
 
346
352
  less_pvalue, greater_pvalue = res_less.pvalue, res_greater.pvalue
347
- if brunnermunzel_workaround:
353
+ if edge_cases_workaround:
348
354
  assert np.all(np.isfinite([less_pvalue, greater_pvalue]))
349
355
 
350
356
  return less_pvalue, greater_pvalue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: benchstats
3
- Version: 3.3.1
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