From 7a4df6d7f8d87c9c77a4d4d1fb8a738a5da2f1e2 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 May 2024 10:16:36 -0500 Subject: [PATCH 1/3] Minor speed/accuracy improvement for quartic and triweight --- Lib/statistics.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/statistics.py b/Lib/statistics.py index 450edfaabe8defd..2f897c2d8faf898 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -953,12 +953,14 @@ def kde(data, h, kernel='normal', *, cumulative=False): case 'quartic' | 'biweight': K = lambda t: 15/16 * (1.0 - t * t) ** 2 - W = lambda t: 3/16 * t**5 - 5/8 * t**3 + 15/16 * t + 1/2 + W = lambda t: sumprod((3/16, -5/8, 15/16, 1/2), + (t**5, t**3, t, 1.0)) support = 1.0 case 'triweight': K = lambda t: 35/32 * (1.0 - t * t) ** 3 - W = lambda t: 35/32 * (-1/7*t**7 + 3/5*t**5 - t**3 + t) + 1/2 + W = lambda t: sumprod((-5/32, 21/32, -35/32, 35/32, 1/2), + (t**7, t**5, t**3, t, 1.0)) support = 1.0 case 'cosine': @@ -1732,7 +1734,7 @@ def _quartic_invcdf_estimate(p): _quartic_invcdf = _newton_raphson( f_inv_estimate = _quartic_invcdf_estimate, - f = lambda t: 3/16 * t**5 - 5/8 * t**3 + 15/16 * t + 1/2, + f = lambda t: sumprod((3/16, -5/8, 15/16, 1/2), (t**5, t**3, t, 1.0)), f_prime = lambda t: 15/16 * (1.0 - t * t) ** 2) def _triweight_invcdf_estimate(p): @@ -1742,7 +1744,8 @@ def _triweight_invcdf_estimate(p): _triweight_invcdf = _newton_raphson( f_inv_estimate = _triweight_invcdf_estimate, - f = lambda t: 35/32 * (-1/7*t**7 + 3/5*t**5 - t**3 + t) + 1/2, + f = lambda t: sumprod((-5/32, 21/32, -35/32, 35/32, 1/2), + (t**7, t**5, t**3, t, 1.0)), f_prime = lambda t: 35/32 * (1.0 - t * t) ** 3) _kernel_invcdfs = { From 11b3c6fdf4b645b37300c81df6f378f22ac2ad14 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 7 May 2024 11:17:29 -0500 Subject: [PATCH 2/3] Add test for the accuracy improvement --- Lib/test/test_statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 6f68edd447c9534..cded8aba6e8cd79 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2444,7 +2444,7 @@ def test_kde_kernel_invcdfs(self): with self.subTest(kernel=kernel): cdf = kde([0.0], h=1.0, kernel=kernel, cumulative=True) for x in xarr: - self.assertAlmostEqual(invcdf(cdf(x)), x, places=5) + self.assertAlmostEqual(invcdf(cdf(x)), x, places=6) @support.requires_resource('cpu') def test_kde_random(self): From b48e38e3fcd8025a7ae91e728d6577abb7eadf2a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 8 May 2024 10:35:05 -0500 Subject: [PATCH 3/3] Eliminate local n which shadows a non-local n --- Lib/statistics.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/statistics.py b/Lib/statistics.py index 2f897c2d8faf898..c36145fe7f2a792 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -976,12 +976,10 @@ def kde(data, h, kernel='normal', *, cumulative=False): if support is None: def pdf(x): - n = len(data) - return sum(K((x - x_i) / h) for x_i in data) / (n * h) + return sum(K((x - x_i) / h) for x_i in data) / (len(data) * h) def cdf(x): - n = len(data) - return sum(W((x - x_i) / h) for x_i in data) / n + return sum(W((x - x_i) / h) for x_i in data) / len(data) else: