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.
- {benchstats-3.3.1 → benchstats-3.3.3}/CHANGELOG.md +8 -1
- {benchstats-3.3.1/src/benchstats.egg-info → benchstats-3.3.3}/PKG-INFO +1 -1
- {benchstats-3.3.1 → benchstats-3.3.3}/pyproject.toml +1 -1
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/compare.py +20 -14
- {benchstats-3.3.1 → benchstats-3.3.3/src/benchstats.egg-info}/PKG-INFO +1 -1
- {benchstats-3.3.1 → benchstats-3.3.3}/tests/test_compare.py +4 -1
- {benchstats-3.3.1 → benchstats-3.3.3}/tests/test_qbench.py +12 -5
- {benchstats-3.3.1 → benchstats-3.3.3}/LICENSE +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/MANIFEST.in +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/README.md +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple1.svg +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple2.svg +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple3.svg +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple4.svg +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/docs/imgs/simple4_bad.svg +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/setup.cfg +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/__init__.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/__main__.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/cli_parser.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/common.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/parser_GbenchJson.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/parsers.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/qbench.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats/render.py +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/SOURCES.txt +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/dependency_links.txt +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/entry_points.txt +0 -0
- {benchstats-3.3.1 → benchstats-3.3.3}/src/benchstats.egg-info/requires.txt +0 -0
- {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
|
+
# 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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "benchstats"
|
|
7
|
-
version = "3.3.
|
|
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
|
-
|
|
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
|
-
`
|
|
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).
|
|
287
|
-
handle it: value of None handles the bug only for
|
|
288
|
-
controls whether to handle the bug irrespective of the method
|
|
289
|
-
pvalues for other tests, but this shouldn't affect inference
|
|
290
|
-
value is too wild).
|
|
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
|
|
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
|
|
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
|
|
317
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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,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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|