diff options
author | Travis Oliphant <oliphant@enthought.com> | 2008-04-04 06:11:24 +0000 |
---|---|---|
committer | Travis Oliphant <oliphant@enthought.com> | 2008-04-04 06:11:24 +0000 |
commit | effc09b20ea3146d3dcfc6884b4321ddf1638cb5 (patch) | |
tree | b730047a85df6b1c374c473979e6614c27692b50 /numpy | |
parent | d396f126cc9ac2f215c1363a8f8650d6c4e6c161 (diff) | |
download | numpy-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.py | 151 | ||||
-rw-r--r-- | numpy/lib/io.py | 22 | ||||
-rw-r--r-- | numpy/lib/tests/test_financial.py | 34 |
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() |