diff --git a/Lib/statistics.py b/Lib/statistics.py index 758b5b58848fb9..edcd7588565cc0 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -1060,24 +1060,27 @@ def cdf(x): else: - sample = sorted(data) + data_snapshot = tuple(data) + sample = sorted(data_snapshot) bandwidth = h * support + def refresh(): + nonlocal n, data_snapshot, sample + updated_data = tuple(data) + if updated_data != data_snapshot: + data_snapshot = updated_data + sample = sorted(updated_data) + n = len(updated_data) + def pdf(x): - nonlocal n, sample - if len(data) != n: - sample = sorted(data) - n = len(data) + refresh() i = bisect_left(sample, x - bandwidth) j = bisect_right(sample, x + bandwidth) supported = sample[i : j] return sum(K((x - x_i) / h) for x_i in supported) / (n * h) def cdf(x): - nonlocal n, sample - if len(data) != n: - sample = sorted(data) - n = len(data) + refresh() i = bisect_left(sample, x - bandwidth) j = bisect_right(sample, x + bandwidth) supported = sample[i : j] diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 700c5ac304f717..d700fac42a83d8 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2428,6 +2428,14 @@ def integrate(func, low, high, steps=10_000): data.append(100) self.assertGreater(f_hat(100), 0.0) + for cumulative in (False, True): + with self.subTest(cumulative=cumulative): + data = [1, 2, 3] + estimate = kde(data, 1.0, 'triangular', cumulative=cumulative) + data[0] = 5 + expected = kde(data, 1.0, 'triangular', cumulative=cumulative) + self.assertEqual(estimate(1.5), expected(1.5)) + def test_kde_kernel_specs(self): # White-box test for the kernel formulas in isolation from # their downstream use in kde() and kde_random() diff --git a/Misc/NEWS.d/next/Library/2026-08-01-23-30-00.gh-issue-154942.mF8QvS.rst b/Misc/NEWS.d/next/Library/2026-08-01-23-30-00.gh-issue-154942.mF8QvS.rst new file mode 100644 index 00000000000000..67e29f3adad83c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-01-23-30-00.gh-issue-154942.mF8QvS.rst @@ -0,0 +1,2 @@ +Fix :func:`statistics.kde` with a bounded-support kernel to refresh its cached +sample when the contents of the input data change without changing its length.