diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index 026f4aa462d..38a499ab37e 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -696,6 +696,16 @@ of applications in statistics. Set *n* to 100 for percentiles which gives the 99 cuts points that separate the normal distribution into 100 equal sized groups. + .. method:: NormalDist.zscore(x) + + Compute the + `Standard Score `_ + describing *x* in terms of the number of standard deviations + above or below the mean of the normal distribution: + ``(x - mean) / stdev``. + + .. versionadded:: 3.9 + Instances of :class:`NormalDist` support addition, subtraction, multiplication and division by a constant. These operations are used for translation and scaling. For example: diff --git a/Lib/statistics.py b/Lib/statistics.py index 1e95c0b6639..9beafb341b3 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -999,6 +999,17 @@ class NormalDist: x2 = (a - b) / dv return 1.0 - (fabs(Y.cdf(x1) - X.cdf(x1)) + fabs(Y.cdf(x2) - X.cdf(x2))) + def zscore(self, x): + """Compute the Standard Score. (x - mean) / stdev + + Describes *x* in terms of the number of standard deviations + above or below the mean of the normal distribution. + """ + # https://www.statisticshowto.com/probability-and-statistics/z-score/ + if not self._sigma: + raise StatisticsError('zscore() not defined when sigma is zero') + return (x - self._mu) / self._sigma + @property def mean(self): "Arithmetic mean of the normal distribution." diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index a9a427bc8d9..0e46a7119f0 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2602,6 +2602,21 @@ class TestNormalDist: with self.assertRaises(self.module.StatisticsError): NormalDist(1, 0).overlap(X) # left operand sigma is zero + def test_zscore(self): + NormalDist = self.module.NormalDist + X = NormalDist(100, 15) + self.assertEqual(X.zscore(142), 2.8) + self.assertEqual(X.zscore(58), -2.8) + self.assertEqual(X.zscore(100), 0.0) + with self.assertRaises(TypeError): + X.zscore() # too few arguments + with self.assertRaises(TypeError): + X.zscore(1, 1) # too may arguments + with self.assertRaises(TypeError): + X.zscore(None) # non-numeric type + with self.assertRaises(self.module.StatisticsError): + NormalDist(1, 0).zscore(100) # sigma is zero + def test_properties(self): X = self.module.NormalDist(100, 15) self.assertEqual(X.mean, 100) diff --git a/Misc/NEWS.d/next/Library/2020-04-15-16-43-48.bpo-40290.eqCMGJ.rst b/Misc/NEWS.d/next/Library/2020-04-15-16-43-48.bpo-40290.eqCMGJ.rst new file mode 100644 index 00000000000..a930cee1c8b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-04-15-16-43-48.bpo-40290.eqCMGJ.rst @@ -0,0 +1 @@ +Added zscore() to statistics.NormalDist().