ENH inv_norm method

This commit is contained in:
Kevin Johnson
2021-08-29 16:06:27 -07:00
parent c2bda51b8c
commit 93a7142ad6
6 changed files with 131 additions and 5 deletions
+1 -1
View File
@@ -113,7 +113,7 @@ $ pip install pandas_ta
Latest Version
--------------
Best choice! Version: *0.3.27b*
Best choice! Version: *0.3.28b*
* Includes all fixes and updates between **pypi** and what is covered in this README.
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
+3 -2
View File
@@ -2,7 +2,8 @@
from ._candles import *
from ._core import *
from ._math import *
from ._signals import *
from ._time import *
from ._metrics import *
from ._signals import *
from ._stats import *
from ._time import *
from .data import *
+2 -1
View File
@@ -110,7 +110,7 @@ def geometric_mean(series: Series) -> float:
def hpoly(array: npArray, x: Tuple[int, float]) -> float:
"""Horner Calculation for Polynomial Evaluation
"""Horner Calculation for Polynomial Evaluation (hpoly)
array: np.array of polynomial coefficients
* Convert list or Series to np.array prior to calling the method for
@@ -194,6 +194,7 @@ def strided_window(array, length):
"""as_strided
creates a view into the array given the exact strides and shape.
* Recommended to avoid when possible.
Source: https://numpy.org/devdocs/reference/generated/numpy.lib.stride_tricks.as_strided.html
Pandas TA Issue: https://github.com/twopirllc/pandas-ta/issues/285
"""
+112
View File
@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
from typing import Tuple
from numpy import array as npArray
from numpy import infty as npInfty
from numpy import log as npLog
from numpy import nan as npNaN
from numpy import pi as npPi
from numpy import sqrt as npSqrt
from pandas_ta import Imports
from ._math import hpoly
def inv_norm(x0: Tuple[float, int]) -> Tuple[float, None]:
"""Inverse Normal (inv_norm)
Calculates the 'x' in which the area under the Gaussian PDF is equal to x0.
If the user has package "statsmodels" installed, the method will call and
return norm().ppf(x0)
Source: https://github.com/scipy/scipy/blob/701ffcc8a6f04509d115aac5e5681c538b5265a2/scipy/special/cephes/ndtri.c
"""
if Imports["statsmodels"]:
from scipy.stats import norm
return norm().ppf(x0)
# Polynomial Coefficients
p0 = npArray([
-5.99633501014107895267E1, 9.80010754185999661536E1,
-5.66762857469070293439E1, 1.39312609387279679503E1,
-1.23916583867381258016E0
])
q0 = npArray([
1.00000000000000000000E0, 1.95448858338141759834E0,
4.67627912898881538453E0, 8.63602421390890590575E1,
-2.25462687854119370527E2, 2.00260212380060660359E2,
-8.20372256168333339912E1, 1.59056225126211695515E1,
-1.18331621121330003142E0
])
p1 = npArray([
4.05544892305962419923E0, 3.15251094599893866154E1,
5.71628192246421288162E1, 4.40805073893200834700E1,
1.46849561928858024014E1, 2.18663306850790267539E0,
-1.40256079171354495875E-1, -3.50424626827848203418E-2,
-8.57456785154685413611E-4
])
q1 = npArray([
1.00000000000000000000E0, 1.57799883256466749731E1,
4.53907635128879210584E1, 4.13172038254672030440E1,
1.50425385692907503408E1, 2.50464946208309415979E0,
-1.42182922854787788574E-1, -3.80806407691578277194E-2,
-9.33259480895457427372E-4
])
p2 = npArray([
3.23774891776946035970E0, 6.91522889068984211695E0,
3.93881025292474443415E0, 1.33303460815807542389E0,
2.01485389549179081538E-1, 1.23716634817820021358E-2,
3.01581553508235416007E-4, 2.65806974686737550832E-6,
6.23974539184983293730E-9
])
q2 = npArray([
1.00000000000000000000E0, 6.02427039364742014255E0,
3.67983563856160859403E0, 1.37702099489081330271E0,
2.16236993594496635890E-1, 1.34204006088543189037E-2,
3.28014464682127739104E-4, 2.89247864745380683936E-6,
6.79019408009981274425E-9
])
if x0 == 0.0: return -npInfty
if x0 == 1.0: return npInfty
if x0 < 0.0 or x0 > 1.0: return npNaN
negate = True
x = x0
sqrt2pi = npSqrt(2 * npPi)
threshold = 0.13533528323661269189
if x > 1.0 - threshold:
x, negate = 1.0 - x, False
# 0 <= |x - 0.5| <= 3/8
if x > threshold:
x -= 0.5
x2 = x * x
y = x + x * (x2 * hpoly(p0, x2) / hpoly(q0, x2))
y *= sqrt2pi
return y
y = npSqrt(-2.0 * npLog(x))
y0 = y - npLog(y) / y
z = 1.0 / y
if y < 8.0:
# Approximation for interval z = sqrt(-2 log x ) between 2 and 8
# i.e., x between exp(-2) = .135 and exp(-32) = 1.27e-14.
y1 = z * hpoly(p1, z) / hpoly(q1, z)
else:
# Approximation for interval z = sqrt(-2 log x ) between 8 and 64
# i.e., x between exp(-32) = 1.27e-14 and exp(-2048) = 3.67e-890.
y1 = z * hpoly(p2, z) / hpoly(q2, z)
y = y0 - y1
if negate: y = -y
return y
+1 -1
View File
@@ -19,7 +19,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
version=".".join(("0", "3", "27b")),
version=".".join(("0", "3", "28b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
+12
View File
@@ -223,6 +223,18 @@ class TestUtilities(TestCase):
self.assertEqual(self.utils.hpoly([1, 0, 1], 1), 2)
self.assertEqual(self.utils.hpoly([1, 1, 1], 1), 3)
def test_inv_norm(self):
np.testing.assert_equal(self.utils.inv_norm(-0.01), np.nan)
self.assertEqual(self.utils.inv_norm(0), -np.infty)
self.assertEqual(self.utils.inv_norm(1 - 0.96), -1.7506860712521692)
self.assertAlmostEqual(self.utils.inv_norm(1 - 0.8646), -1.101222112591979)
self.assertEqual(self.utils.inv_norm(0.5), 0)
self.assertAlmostEqual(self.utils.inv_norm(0.8646), 1.101222112591979)
self.assertEqual(self.utils.inv_norm(0.96), 1.7506860712521692)
self.assertEqual(self.utils.inv_norm(1), np.infty)
np.testing.assert_equal(self.utils.inv_norm(1.01), np.nan)
def test_linear_regression(self):
x = Series([1, 2, 3, 4, 5])
y = Series([1.8, 2.1, 2.7, 3.2, 4])