chris1610 / pbpython

Code, Notebooks and Examples from Practical Business Python
https://pbpython.com
BSD 3-Clause "New" or "Revised" License
1.99k stars 987 forks source link

df.ix need to replace with df.loc #19

Closed rayuduak closed 4 years ago

rayuduak commented 4 years ago

I changed the df.ix to df.loc the function when we pass additional principal fails I am using pandas 0.24.2

def amortization_table(interest_rate, years, payments_year, principal, addl_principal=0, start_date=date.today()): """ Calculate the amortization schedule given the loan details

 Args:
    interest_rate: The annual interest rate for this loan
    years: Number of years for the loan
    payments_year: Number of payments in a year
    principal: Amount borrowed
    addl_principal (optional): Additional payments to be made each period. Assume 0 if nothing provided.
                               must be a value less then 0, the function will convert a positive value to
                               negative
    start_date (optional): Start date. Will start on first of next month if none provided

Returns:
    schedule: Amortization schedule as a pandas dataframe
    summary: Pandas dataframe that summarizes the payoff information
"""
# Ensure the additional payments are negative
if addl_principal > 0:
    addl_principal = -addl_principal

# Create an index of the payment dates
rng = pd.date_range(start_date, periods=years * payments_year, freq='MS')
rng.name = "Payment_Date"

# Build up the Amortization schedule as a DataFrame
df = pd.DataFrame(index=rng,columns=['Payment', 'Principal', 'Interest', 
                                     'Addl_Principal', 'Curr_Balance'], dtype='float')

# Add index by period (start at 1 not 0)
df.reset_index(inplace=True)
df.index += 1
df.index.name = "Period"

# Calculate the payment, principal and interests amounts using built in Numpy functions
per_payment = np.pmt(interest_rate/payments_year, years*payments_year, principal)
df["Payment"] = per_payment
df["Principal"] = np.ppmt(interest_rate/payments_year, df.index, years*payments_year, principal)
df["Interest"] = np.ipmt(interest_rate/payments_year, df.index, years*payments_year, principal)

# Round the values
df = df.round(2) 

# Add in the additional principal payments
df["Addl_Principal"] = addl_principal

# Store the Cumulative Principal Payments and ensure it never gets larger than the original principal
df["Cumulative_Principal"] = (df["Principal"] + df["Addl_Principal"]).cumsum()
df["Cumulative_Principal"] = df["Cumulative_Principal"].clip(lower=-principal)

# Calculate the current balance for each period
df["Curr_Balance"] = principal + df["Cumulative_Principal"]

# Determine the last payment date
try:
    last_payment = df.query("Curr_Balance <= 0")["Curr_Balance"].idxmax(axis=1, skipna=True)
except ValueError:
    last_payment = df.last_valid_index()

last_payment_date = "{:%m-%d-%Y}".format(df.loc[last_payment, "Payment_Date"])

# Truncate the data frame if we have additional principal payments:

if addl_principal != 0:

    # Remove the extra payment periods
    df = df.loc[0:last_payment].copy()

    # Calculate the principal for the last row
    df.loc[last_payment, "Principal"] = -(df.loc[last_payment-1, "Curr_Balance"])

    # Calculate the total payment for the last row
    df.loc[last_payment, "Payment"] = df.loc[last_payment, ["Principal", "Interest"]].sum()

    # Zero out the additional principal
    df.loc[last_payment, "Addl_Principal"] = 0

# Get the payment info into a DataFrame in column order
payment_info = (df[["Payment", "Principal", "Addl_Principal", "Interest"]]
                .sum().to_frame().T)

# Format the Date DataFrame
payment_details = pd.DataFrame.from_dict(dict([('payoff_date', [last_payment_date]),
                                           ('Interest Rate', [interest_rate]),
                                               ('Number of years', [years])
                                          ]))
# Add a column showing how much we pay each period.
# Combine addl principal with principal for total payment
payment_details["Period_Payment"] = round(per_payment, 2) + addl_principal

payment_summary = pd.concat([payment_details, payment_info], axis=1)
return df, payment_summary
chris1610 commented 4 years ago

Thank you for pointing this out. I have updated the notebook and the original article to remove .ix and fix another deprecation warning with more recent versions of pandas.