Skip to content

Commit 81c35a0

Browse files
committed
TST: clean up ppmt tests
- Move them into their own class - Add tests for invalid `per` arguments
1 parent 2d7b5ed commit 81c35a0

File tree

1 file changed

+125
-117
lines changed

1 file changed

+125
-117
lines changed

numpy_financial/tests/test_financial.py

Lines changed: 125 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
21
from decimal import Decimal
2+
import math
33

44
# Don't use 'import numpy as np', to avoid accidentally testing
55
# the versions in numpy instead of numpy_financial.
66
import numpy
77
from numpy.testing import (
8-
assert_, assert_almost_equal, assert_allclose, assert_equal, assert_raises
8+
assert_, assert_almost_equal, assert_allclose, assert_equal
99
)
1010
import pytest
1111

@@ -71,35 +71,6 @@ def test_pmt_decimal(self):
7171
assert_equal(res[1][0], tgt[1][0])
7272
assert_equal(res[1][1], tgt[1][1])
7373

74-
def test_ppmt(self):
75-
assert_equal(numpy.round(npf.ppmt(0.1 / 12, 1, 60, 55000), 2), -710.25)
76-
77-
def test_ppmt_decimal(self):
78-
assert_equal(npf.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'),
79-
Decimal('60'), Decimal('55000')),
80-
Decimal('-710.2541257864217612489830917'))
81-
82-
# Two tests showing how Decimal is actually getting at a more exact result
83-
# .23 / 12 does not come out nicely as a float but does as a decimal
84-
def test_ppmt_special_rate(self):
85-
assert_equal(numpy.round(npf.ppmt(0.23 / 12, 1, 60, 10000000000), 8),
86-
-90238044.232277036)
87-
88-
def test_ppmt_special_rate_decimal(self):
89-
# When rounded out to 8 decimal places like the float based test,
90-
# this should not equal the same value as the float, substituted
91-
# for the decimal
92-
def raise_error_because_not_equal():
93-
assert_equal(
94-
round(npf.ppmt(Decimal('0.23') / Decimal('12'), 1, 60,
95-
Decimal('10000000000')), 8),
96-
Decimal('-90238044.232277036'))
97-
98-
assert_raises(AssertionError, raise_error_because_not_equal)
99-
assert_equal(npf.ppmt(Decimal('0.23') / Decimal('12'), 1, 60,
100-
Decimal('10000000000')),
101-
Decimal('-90238044.2322778884413969909'))
102-
10374
def test_npv(self):
10475
assert_almost_equal(
10576
npf.npv(0.05, [-15000, 1500, 2500, 3500, 4500, 6000]),
@@ -172,15 +143,6 @@ def test_when(self):
172143
assert_equal(npf.pmt(0.08 / 12, 5 * 12, 15000., 0, 0),
173144
npf.pmt(0.08 / 12, 5 * 12, 15000., 0, 'end'))
174145

175-
# begin
176-
assert_equal(npf.ppmt(0.1 / 12, 1, 60, 55000, 0, 1),
177-
npf.ppmt(0.1 / 12, 1, 60, 55000, 0, 'begin'))
178-
# end
179-
assert_equal(npf.ppmt(0.1 / 12, 1, 60, 55000, 0),
180-
npf.ppmt(0.1 / 12, 1, 60, 55000, 0, 'end'))
181-
assert_equal(npf.ppmt(0.1 / 12, 1, 60, 55000, 0, 0),
182-
npf.ppmt(0.1 / 12, 1, 60, 55000, 0, 'end'))
183-
184146
# begin
185147
assert_equal(npf.nper(0.075, -2000, 0, 100000., 1),
186148
npf.nper(0.075, -2000, 0, 100000., 'begin'))
@@ -224,86 +186,10 @@ def test_decimal_with_when(self):
224186
npf.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'),
225187
Decimal('0'), 'end'))
226188

227-
# begin
228-
assert_equal(npf.pmt(Decimal('0.08') / Decimal('12'),
229-
Decimal('5') * Decimal('12'), Decimal('15000.'),
230-
Decimal('0'), Decimal('1')),
231-
npf.pmt(Decimal('0.08') / Decimal('12'),
232-
Decimal('5') * Decimal('12'), Decimal('15000.'),
233-
Decimal('0'), 'begin'))
234-
# end
235-
assert_equal(npf.pmt(Decimal('0.08') / Decimal('12'),
236-
Decimal('5') * Decimal('12'), Decimal('15000.'),
237-
Decimal('0')),
238-
npf.pmt(Decimal('0.08') / Decimal('12'),
239-
Decimal('5') * Decimal('12'), Decimal('15000.'),
240-
Decimal('0'), 'end'))
241-
assert_equal(npf.pmt(Decimal('0.08') / Decimal('12'),
242-
Decimal('5') * Decimal('12'), Decimal('15000.'),
243-
Decimal('0'), Decimal('0')),
244-
npf.pmt(Decimal('0.08') / Decimal('12'),
245-
Decimal('5') * Decimal('12'), Decimal('15000.'),
246-
Decimal('0'), 'end'))
247-
248-
# begin
249-
assert_equal(npf.ppmt(Decimal('0.1') / Decimal('12'),
250-
Decimal('1'), Decimal('60'), Decimal('55000'),
251-
Decimal('0'), Decimal('1')),
252-
npf.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'),
253-
Decimal('60'), Decimal('55000'),
254-
Decimal('0'), 'begin'))
255-
# end
256-
assert_equal(npf.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'),
257-
Decimal('60'), Decimal('55000'), Decimal('0')),
258-
npf.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'),
259-
Decimal('60'), Decimal('55000'), Decimal('0'),
260-
'end'))
261-
assert_equal(npf.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'),
262-
Decimal('60'), Decimal('55000'), Decimal('0'),
263-
Decimal('0')),
264-
npf.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'),
265-
Decimal('60'), Decimal('55000'), Decimal('0'),
266-
'end'))
267-
268189
def test_broadcast(self):
269190
assert_almost_equal(npf.nper(0.075, -2000, 0, 100000., [0, 1]),
270191
[21.5449442, 20.76156441], 4)
271192

272-
assert_almost_equal(npf.ppmt(0.1 / 12, list(range(5)), 24, 2000),
273-
[numpy.nan, -75.62318601, -76.25337923,
274-
-76.88882405, -77.52956425], 4)
275-
276-
assert_almost_equal(npf.ppmt(0.1 / 12, list(range(5)), 24, 2000, 0,
277-
[0, 0, 1, 'end', 'begin']),
278-
[numpy.nan, -75.62318601, -75.62318601,
279-
-76.88882405, -76.88882405], 4)
280-
281-
def test_broadcast_decimal(self):
282-
# Use almost equal because precision is tested in the explicit tests,
283-
# this test is to ensure broadcast with Decimal is not broken.
284-
assert_almost_equal(npf.ppmt(Decimal('0.1') / Decimal('12'),
285-
list(range(1, 5)), Decimal('24'),
286-
Decimal('2000')),
287-
[Decimal('-75.62318601'),
288-
Decimal('-76.25337923'), Decimal('-76.88882405'),
289-
Decimal('-77.52956425')], 4)
290-
291-
result = npf.ppmt(
292-
Decimal('0.1') / Decimal('12'),
293-
list(range(1, 5)),
294-
Decimal('24'),
295-
Decimal('2000'),
296-
Decimal('0'),
297-
[Decimal('0'), Decimal('1'), 'end', 'begin']
298-
)
299-
desired = [
300-
Decimal('-75.62318601'),
301-
Decimal('-75.62318601'),
302-
Decimal('-76.88882405'),
303-
Decimal('-76.88882405')
304-
]
305-
assert_almost_equal(result, desired, decimal=4)
306-
307193

308194
class TestNper:
309195
def test_basic_values(self):
@@ -329,6 +215,129 @@ def test_no_interest(self):
329215
assert_(npf.nper(0, -100, 1000) == 10)
330216

331217

218+
class TestPpmt:
219+
def test_float(self):
220+
assert_allclose(
221+
npf.ppmt(0.1 / 12, 1, 60, 55000),
222+
-710.25,
223+
rtol=1e-4
224+
)
225+
226+
def test_decimal(self):
227+
result = npf.ppmt(
228+
Decimal('0.1') / Decimal('12'),
229+
Decimal('1'),
230+
Decimal('60'),
231+
Decimal('55000')
232+
)
233+
assert_equal(
234+
result,
235+
Decimal('-710.2541257864217612489830917'),
236+
)
237+
238+
@pytest.mark.parametrize('when', [1, 'begin'])
239+
def test_when_is_begin(self, when):
240+
assert_allclose(
241+
npf.ppmt(0.1 / 12, 1, 60, 55000, 0, when),
242+
-1158.929712, # Computed using Google Sheet's PPMT
243+
rtol=1e-9,
244+
)
245+
246+
@pytest.mark.parametrize('when', [None, 0, 'end'])
247+
def test_when_is_end(self, when):
248+
args = (0.1 / 12, 1, 60, 55000, 0)
249+
result = npf.ppmt(*args) if when is None else npf.ppmt(*args, when)
250+
assert_allclose(
251+
result,
252+
-710.254126, # Computed using Google Sheet's PPMT
253+
rtol=1e-9,
254+
)
255+
256+
@pytest.mark.parametrize('when', [Decimal('1'), 'begin'])
257+
def test_when_is_begin_decimal(self, when):
258+
result = npf.ppmt(
259+
Decimal('0.08') / Decimal('12'),
260+
Decimal('1'),
261+
Decimal('60'),
262+
Decimal('15000.'),
263+
Decimal('0'),
264+
when
265+
)
266+
assert_almost_equal(
267+
result,
268+
Decimal('-302.131703'), # Computed using Google Sheet's PPMT
269+
decimal=5,
270+
)
271+
272+
@pytest.mark.parametrize('when', [None, Decimal('0'), 'end'])
273+
def test_when_is_end_decimal(self, when):
274+
args = (
275+
Decimal('0.08') / Decimal('12'),
276+
Decimal('1'),
277+
Decimal('60'),
278+
Decimal('15000.'),
279+
Decimal('0')
280+
)
281+
result = npf.ppmt(*args) if when is None else npf.ppmt(*args, when)
282+
assert_almost_equal(
283+
result,
284+
Decimal('-204.145914'), # Computed using Google Sheet's PPMT
285+
decimal=5,
286+
)
287+
288+
@pytest.mark.parametrize('args', [
289+
(0.1 / 12, 0, 60, 15000),
290+
(Decimal('0.012'), Decimal('0'), Decimal('60'), Decimal('15000'))
291+
])
292+
def test_invalid_per(self, args):
293+
# Note that math.isnan() handles Decimal NaN correctly.
294+
assert math.isnan(npf.ppmt(*args))
295+
296+
@pytest.mark.parametrize('when, desired', [
297+
(
298+
None,
299+
[-75.62318601, -76.25337923, -76.88882405, -77.52956425],
300+
), (
301+
[0, 1, 'end', 'begin'],
302+
[-75.62318601, -75.62318601, -76.88882405, -76.88882405],
303+
)
304+
])
305+
def test_broadcast(self, when, desired):
306+
args = (0.1 / 12, numpy.arange(1, 5), 24, 2000, 0)
307+
result = npf.ppmt(*args) if when is None else npf.ppmt(*args, when)
308+
assert_allclose(result, desired, rtol=1e-5)
309+
310+
@pytest.mark.parametrize('when, desired', [
311+
(
312+
None,
313+
[
314+
Decimal('-75.62318601'),
315+
Decimal('-76.25337923'),
316+
Decimal('-76.88882405'),
317+
Decimal('-77.52956425')
318+
],
319+
), (
320+
[Decimal('0'), Decimal('1'), 'end', 'begin'],
321+
[
322+
Decimal('-75.62318601'),
323+
Decimal('-75.62318601'),
324+
Decimal('-76.88882405'),
325+
Decimal('-76.88882405')
326+
]
327+
)
328+
])
329+
def test_broadcast_decimal(self, when, desired):
330+
args = (
331+
Decimal('0.1') / Decimal('12'),
332+
numpy.arange(1, 5),
333+
Decimal('24'),
334+
Decimal('2000'),
335+
Decimal('0')
336+
)
337+
result = npf.ppmt(*args) if when is None else npf.ppmt(*args, when)
338+
assert_almost_equal(result, desired, decimal=8)
339+
340+
332341
class TestIpmt:
333342
def test_float(self):
334343
assert_allclose(
@@ -353,7 +362,6 @@ def test_when_is_end(self, when):
353362
result = npf.ipmt(0.1 / 12, 1, 24, 2000, 0, when)
354363
assert_allclose(result, -16.666667, rtol=1e-6)
355364

356-
357365
@pytest.mark.parametrize('when', [Decimal('1'), 'begin'])
358366
def test_when_is_begin_decimal(self, when):
359367
result = npf.ipmt(

0 commit comments

Comments
 (0)