Skip to content

Commit 60cc842

Browse files
authored
Merge pull request #21 from person142/nper
BUG: fix issues with `nper`
2 parents 8709c4f + 2e20666 commit 60cc842

File tree

2 files changed

+41
-21
lines changed

2 files changed

+41
-21
lines changed

numpy_financial/_financial.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -281,21 +281,25 @@ def nper(rate, pmt, pv, fv=0, when='end'):
281281
282282
"""
283283
when = _convert_when(when)
284-
(rate, pmt, pv, fv, when) = map(np.asarray, [rate, pmt, pv, fv, when])
284+
rate, pmt, pv, fv, when = np.broadcast_arrays(rate, pmt, pv, fv, when)
285+
nper_array = np.empty_like(rate, dtype=np.float64)
285286

286-
use_zero_rate = False
287-
with np.errstate(divide="raise"):
288-
try:
289-
z = pmt*(1+rate*when)/rate
290-
except FloatingPointError:
291-
use_zero_rate = True
287+
zero = rate == 0
288+
nonzero = ~zero
292289

293-
if use_zero_rate:
294-
return (-fv + pv) / pmt
295-
else:
296-
A = -(fv + pv)/(pmt+0)
297-
B = np.log((-fv+z) / (pv+z))/np.log(1+rate)
298-
return np.where(rate == 0, A, B)
290+
with np.errstate(divide='ignore'):
291+
# Infinite numbers of payments are okay, so ignore the
292+
# potential divide by zero.
293+
nper_array[zero] = -(fv[zero] + pv[zero]) / pmt[zero]
294+
295+
nonzero_rate = rate[nonzero]
296+
z = pmt[nonzero] * (1 + nonzero_rate * when[nonzero]) / nonzero_rate
297+
nper_array[nonzero] = (
298+
np.log((-fv[nonzero] + z) / (pv[nonzero] + z))
299+
/ np.log(1 + nonzero_rate)
300+
)
301+
302+
return nper_array
299303

300304

301305
def ipmt(rate, per, nper, pv, fv=0, when='end'):

numpy_financial/tests/test_financial.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,6 @@ def test_ipmt_decimal(self):
141141
result = npf.ipmt(Decimal('0.1') / Decimal('12'), 1, 24, 2000)
142142
assert_equal(result.flat[0], Decimal('-16.66666666666666666666666667'))
143143

144-
def test_nper(self):
145-
assert_almost_equal(npf.nper(0.075, -2000, 0, 100000.),
146-
21.54, 2)
147-
148-
def test_nper2(self):
149-
assert_almost_equal(npf.nper(0.0, -2000, 0, 100000.),
150-
50.0, 1)
151-
152144
def test_npv(self):
153145
assert_almost_equal(
154146
npf.npv(0.05, [-15000, 1500, 2500, 3500, 4500, 6000]),
@@ -410,3 +402,27 @@ def test_broadcast_decimal(self):
410402
[Decimal('-74.998201'), Decimal('-75.62318601'),
411403
Decimal('-75.62318601'), Decimal('-76.88882405'),
412404
Decimal('-76.88882405')], 4)
405+
406+
407+
class TestNper:
408+
def test_basic_values(self):
409+
assert_allclose(
410+
npf.nper([0, 0.075], -2000, 0, 100000),
411+
[50, 21.544944], # Computed using Google Sheet's NPER
412+
rtol=1e-5,
413+
)
414+
415+
def test_gh_18(self):
416+
with numpy.errstate(divide='raise'):
417+
assert_allclose(
418+
npf.nper(0.1, 0, -500, 1500),
419+
11.52670461, # Computed using Google Sheet's NPER
420+
)
421+
422+
def test_infinite_payments(self):
423+
with numpy.errstate(divide='raise'):
424+
result = npf.nper(0, -0.0, 1000)
425+
assert_(result == numpy.inf)
426+
427+
def test_no_interest(self):
428+
assert_(npf.nper(0, -100, 1000) == 10)

0 commit comments

Comments
 (0)