如何用Numpy计算含提前还款的贷款摊销?
Absolutely! You can absolutely handle amortization with extra principal payments using NumPy—you just need to adjust your approach to account for mid-term changes to the loan balance, since np.ipmt and np.ppmt are designed to work with a fixed initial principal and full loan term.
Your original function works great for standard amortization, but it assumes the loan balance only decreases via regular monthly principal payments. When you make an extra principal payment, you're effectively resetting the loan's remaining balance and term—something those built-in NumPy functions can't account for in a single pass. The fix is to split the loan into discrete segments where the balance is constant, calculate amortization for each segment, and then combine the results.
Pure NumPy Implementation
Here's a function that handles multiple extra principal payments while leaning on NumPy's vectorized calculations for efficiency:
import numpy as np def amort_with_extra_payments(principal, interest_rate, years, extra_payments): total_months = years * 12 remaining_principal = principal total_interest = 0.0 total_principal_paid = 0.0 current_month = 1 # Sort extra payments by month to process them in chronological order sorted_extra_payments = sorted(extra_payments.items(), key=lambda x: x[0]) # Add a sentinel to handle the final segment after the last extra payment sorted_extra_payments.append((total_months + 1, 0)) for pay_month, extra_amount in sorted_extra_payments: # Calculate the number of months in this repayment segment segment_months = pay_month - current_month if segment_months <= 0: # Handle same-month extra payments or invalid entries if extra_amount > 0: remaining_principal -= extra_amount total_principal_paid += extra_amount current_month = pay_month + 1 continue # Create 1-based month array for the current segment per = np.arange(segment_months) + 1 # Calculate interest and principal for the segment using remaining balance ipmt_segment = np.ipmt(interest_rate/12, per, total_months - current_month + 1, remaining_principal) ppmt_segment = np.ppmt(interest_rate/12, per, total_months - current_month + 1, remaining_principal) # Aggregate segment totals segment_interest = -np.sum(ipmt_segment) segment_principal = np.sum(ppmt_segment) # Update overall totals total_interest += segment_interest total_principal_paid += segment_principal # Reduce remaining balance by regular principal payments remaining_principal -= segment_principal # Apply extra principal payment (if not the sentinel) if extra_amount > 0: remaining_principal -= extra_amount total_principal_paid += extra_amount # Move to the start of the next segment current_month = pay_month # Handle final partial payment if loan is paid off early if remaining_principal > 0 and current_month <= total_months: final_interest = remaining_principal * (interest_rate/12) total_interest += final_interest total_principal_paid += remaining_principal remaining_principal = 0 return [total_interest, total_principal_paid]
Key Details
- Segmented Calculation: The loan is split into periods before each extra payment. For each segment, we use
np.ipmtandnp.ppmtwith the current remaining principal and remaining loan term. - Multiple Extra Payments: The function accepts a dictionary of
{month: extra_amount}entries, which are sorted and processed in order. - Early Payoff Handling: If extra payments fully pay off the loan before the original term ends, the function calculates the final partial payment (if needed) and stops.
Usage Example
Let's compare your original loan with an extra $20,000 payment in month 36:
# Original amortization (no extra payments) def amort(principal, interest, number_periods): per = np.arange(number_periods*12) + 1 ipmt = np.ipmt(interest/12, per, number_periods*12, principal) ppmt = np.ppmt(interest/12, per, number_periods*12, principal) return [-sum(ipmt), sum(ppmt)] original = amort(100000, 0.05, 15) print(f"Original total interest: ${original[0]:,.2f}") # ~$42,349.72 # With extra $20k payment in month 36 extra_payments = {36: 20000} with_extra = amort_with_extra_payments(100000, 0.05, 15, extra_payments) print(f"Total interest with extra payment: ${with_extra[0]:,.2f}") # ~$28,792.65 (lower, as expected) print(f"Total principal paid: ${with_extra[1]:,.2f}") # $100,000 (full loan amount)
内容的提问来源于stack exchange,提问作者spitfiredd

