summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorTravis Oliphant <oliphant@enthought.com>2008-04-04 06:11:24 +0000
committerTravis Oliphant <oliphant@enthought.com>2008-04-04 06:11:24 +0000
commiteffc09b20ea3146d3dcfc6884b4321ddf1638cb5 (patch)
treeb730047a85df6b1c374c473979e6614c27692b50 /numpy
parentd396f126cc9ac2f215c1363a8f8650d6c4e6c161 (diff)
downloadnumpy-effc09b20ea3146d3dcfc6884b4321ddf1638cb5.tar.gz
Add fromregex function (needs more testing) and some simple spreadsheet-like financial calculations.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/lib/financial.py151
-rw-r--r--numpy/lib/io.py22
-rw-r--r--numpy/lib/tests/test_financial.py34
3 files changed, 207 insertions, 0 deletions
diff --git a/numpy/lib/financial.py b/numpy/lib/financial.py
new file mode 100644
index 000000000..885fffeea
--- /dev/null
+++ b/numpy/lib/financial.py
@@ -0,0 +1,151 @@
+# Some simple financial calculations
+from numpy import log, where
+import numpy as np
+
+__all__ = ['fv', 'pmt', 'nper', 'ipmt', 'ppmt', 'pv', 'rate', 'irr', 'npv']
+
+_when_to_num = {'end':0, 'begin':1,
+ 'e':0, 'b':1,
+ 0:0, 1:1,
+ 'beginning':1,
+ 'start':1,
+ 'finish':0}
+
+eqstr = """
+
+ Parameters
+ ----------
+ rate :
+ Rate of interest (per period)
+ nper :
+ Number of compounding periods
+ pmt :
+ Payment
+ pv :
+ Present value
+ fv :
+ Future value
+ when :
+ When payments are due ('begin' (1) or 'end' (0))
+
+ nper / (1 + rate*when) \ / nper \
+ fv + pv*(1+rate) + pmt*|-------------------|*| (1+rate) - 1 | = 0
+ \ rate / \ /
+
+ fv + pv + pmt * nper = 0 (when rate == 0)
+"""
+
+def fv(rate, nper, pmt, pv, when='end'):
+ """future value computed by solving the equation
+
+ %s
+ """ % eqstr
+ when = _when_to_num[when]
+ temp = (1+rate)**nper
+ fact = where(rate==0.0, nper, (1+rate*when)*(temp-1)/rate)
+ return -(pv*temp + pmt*fact)
+
+def pmt(rate, nper, pv, fv=0, when='end'):
+ """Payment computed by solving the equation
+
+ %s
+ """ % eqstr
+ when = _when_to_num[when]
+ temp = (1+rate)**nper
+ fact = where(rate==0.0, nper, (1+rate*when)*(temp-1)/rate)
+ return -(fv + pv*temp) / fact
+
+def nper(rate, pmt, pv, fv=0, when='end'):
+ """Number of periods found by solving the equation
+
+ %s
+ """ % eqstr
+ when = _when_to_num[when]
+ try:
+ z = pmt*(1.0+rate*when)/rate
+ except ZeroDivisionError:
+ z = 0.0
+ A = -(fv + pv)/(pmt+0.0)
+ B = (log(fv-z) - log(pv-z))/log(1.0+rate)
+ return where(rate==0.0, A, B) + 0.0
+
+def ipmt(rate, per, nper, pv, fv=0.0, when='end'):
+ raise NotImplementedError
+
+
+def ppmt(rate, per, nper, pv, fv=0.0, when='end'):
+ raise NotImplementedError
+
+def pv(rate, nper, pmt, fv=0.0, when='end'):
+ """Number of periods found by solving the equation
+
+ %s
+ """ % eqstr
+ when = _when_to_num[when]
+ temp = (1+rate)**nper
+ fact = where(rate == 0.0, nper, (1+rate*when)*(temp-1)/rate)
+ return -(fv + pmt*fact)/temp
+
+# Computed with Sage
+# (y + (r + 1)^n*x + p*((r + 1)^n - 1)*(r*w + 1)/r)/(n*(r + 1)^(n - 1)*x - p*((r + 1)^n - 1)*(r*w + 1)/r^2 + n*p*(r + 1)^(n - 1)*(r*w + 1)/r + p*((r + 1)^n - 1)*w/r)
+
+def _g_div_gp(r, n, p, x, y, w):
+ t1 = (r+1)**n
+ t2 = (r+1)**(n-1)
+ return (y + t1*x + p*(t1 - 1)*(r*w + 1)/r)/(n*t2*x - p*(t1 - 1)*(r*w + 1)/(r**2) + n*p*t2*(r*w + 1)/r + p*(t1 - 1)*w/r)
+
+# Use Newton's iteration until the change is less than 1e-6
+# for all values or a maximum of 100 iterations is reached.
+# Newton's rule is
+# r_{n+1} = r_{n} - g(r_n)/g'(r_n)
+# where
+# g(r) is the formula
+# g'(r) is the derivative with respect to r.
+def rate(nper, pmt, pv, fv, when='end', guess=0.10, tol=1e-6, maxiter=100):
+ """Number of periods found by solving the equation
+
+ %s
+ """ % eqstr
+ when = _when_to_num[when]
+ rn = guess
+ iter = 0
+ close = False
+ while (iter < maxiter) and not close:
+ rnp1 = rn - _g_div_gp(rn, nper, pmt, pv, fv, when)
+ diff = abs(rnp1-rn)
+ close = np.all(diff<tol)
+ iter += 1
+ rn = rnp1
+ if not close:
+ # Return nan's in array of the same shape as rn
+ return np.nan + rn
+ else:
+ return rn
+
+def irr(values):
+ """Internal Rate of Return
+
+ This is the rate of return that gives a net present value of 0.0
+
+ npv(irr(values), values) == 0.0
+ """
+ res = np.roots(values[::-1])
+ # Find the root(s) between 0 and 1
+ mask = (res.imag == 0) & (res.real > 0) & (res.real <= 1)
+ res = res[mask].real
+ if res.size == 0:
+ return np.nan
+ rate = 1.0/res - 1
+ if rate.size == 1:
+ rate = rate.item()
+ return rate
+
+def npv(rate, values):
+ """Net Present Value
+
+ sum ( values_k / (1+rate)**k, k = 1..n)
+ """
+ values = np.asarray(values)
+ return (values / (1+rate)**np.arange(1,len(values)+1)).sum(axis=0)
+
+
diff --git a/numpy/lib/io.py b/numpy/lib/io.py
index 7ef7df933..bb4d254a8 100644
--- a/numpy/lib/io.py
+++ b/numpy/lib/io.py
@@ -3,6 +3,7 @@ __all__ = ['savetxt', 'loadtxt',
'load', 'loads',
'save', 'savez',
'packbits', 'unpackbits',
+ 'fromregex',
'DataSource']
import numpy as np
@@ -361,3 +362,24 @@ def savetxt(fname, X, fmt='%.18e',delimiter=' '):
if origShape is not None:
X.shape = origShape
+
+import re
+def fromregex(file, regexp, **kwds):
+ """Construct a record array from a text file, using regular-expressions parsing.
+
+ Groups in the regular exespression are converted to fields.
+ """
+ if not hasattr(file, "read"):
+ file = open(file,'r')
+ if not hasattr(regexp, 'match'):
+ regexp = re.compile(regexp)
+
+ seq = regexp.findall(file.read())
+ dtypelist = []
+ for key, value in kwds.values():
+ dtypelist.append((key, value))
+ format = np.dtype(dtypelist)
+ output = array(seq, dtype=format)
+ return output
+
+
diff --git a/numpy/lib/tests/test_financial.py b/numpy/lib/tests/test_financial.py
new file mode 100644
index 000000000..bf0b4dfd4
--- /dev/null
+++ b/numpy/lib/tests/test_financial.py
@@ -0,0 +1,34 @@
+"""
+from numpy.lib.financial import *
+
+>>> rate(10,0,-3500,10000)
+0.11069085371426901
+
+>>> irr([-150000, 15000, 25000, 35000, 45000, 60000])
+0.052432888859414106
+
+>>> pv(0.07,20,12000,0)
+-127128.17094619398
+
+>>> fv(0.075, 20, -2000,0,0)
+86609.362673042924
+
+>>> pmt(0.08/12,5*12,15000)
+-304.14591432620773
+
+>>> nper(0.075,-2000,0,100000.)
+21.544944197323336
+
+>>> npv(0.05,[-15000,1500,2500,3500,4500,6000])
+117.04271900089589
+
+"""
+
+from numpy.testing import *
+import numpy as np
+
+class TestDocs(NumpyTestCase):
+ def check_doctests(self): return self.rundocs()
+
+if __name__ == "__main__":
+ NumpyTest().run()