I created a peer-to-peer lending simulation in spyder when I run my main output I get an error in creating one of my plots and Im not sure why. The error comes up as "Unable to create Lender Funds Visualisation" in a message box
Output:runfile('C:/Users/Emma Robinson/OneDrive/Documents/FIN3028 Python/Developer Showcase/main.py', wdir='C:/Users/Emma Robinson/OneDrive/Documents/FIN3028 Python/Developer Showcase')
Lenders loaded: [{'id': 1, 'name': 'Lender A', 'available_funds': 5000, 'min_interest_rate_%': 5, 'max_risk': 2}, {'id': 2, 'name': 'Lender B', 'available_funds': 3000, 'min_interest_rate_%': 4, 'max_risk': 1}, {'id': 3, 'name': 'Lender C', 'available_funds': 7000, 'min_interest_rate_%': 6, 'max_risk': 3}]
Borrowers loaded: [{'id': 1, 'name': 'Borrower X', 'amount_needed': 2000, 'interest_rate_%': 6, 'risk': 1, 'duration_years': 3}, {'id': 2, 'name': 'Borrower Y', 'amount_needed': 4000, 'interest_rate_%': 5, 'risk': 2, 'duration_years': 2}, {'id': 3, 'name': 'Borrower Z', 'amount_needed': 1500, 'interest_rate_%': 7, 'risk': 3, 'duration_years': 1}]
ERROR:root:Missing 'name' of 'funds' key in lender: {'id': 1, 'name': 'Lender A', 'available_funds': 3000, 'min_interest_rate_%': 5, 'max_risk': 2}
ERROR:root:Missing 'name' of 'funds' key in lender: {'id': 2, 'name': 'Lender B', 'available_funds': 3000, 'min_interest_rate_%': 4, 'max_risk': 1}
ERROR:root:Missing 'name' of 'funds' key in lender: {'id': 3, 'name': 'Lender C', 'available_funds': 5500, 'min_interest_rate_%': 6, 'max_risk': 3}
ERROR:root:No valid lender data available for plotting.
Repayment Summary:
borrower_id total_payment ... total_interest remaining_balance
0 1 2190.24 ... 190.37 0.0
1 3 1557.48 ... 57.47 0.0
[2 rows x 5 columns]
Repayment summary saved as 'repayment_summary.csv'.This is my main function :import tkinter as tk
from tkinter import messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from input_output_data import load_json_data, select_file, validate_json_file, save_results
from matching_generator import match_lenders_borrowers
from repayment_simulator import simulate_repayment_schedule, generate_summary
from visualisation import plot_lender_funds, plot_repayment_schedules
import logging
import sys
import os
# Ensure correct working directory
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)
sys.path.append(script_dir)
logging.basicConfig(level=logging.INFO)
def show_messagebox(title, message, error=False):
"""
Display a messagebox for success or error messages.
Parameters:
- title (str): Title of the messagebox.
- message (str): Message content.
- error (bool): True for an error message, False for an info message.
"""
if error:
messagebox.showerror(title, message)
else:
messagebox.showinfo(title, message)
def display_plot(fig, root, title="Visualisation"):
"""
Display a matplotlib figure in a tkinter window.
Parameters:
- fig: Matplotlib figure object.
- root: Tkinter root window.
- title: Title of the plot window.
"""
if fig:
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack()
canvas.draw()
else:
show_messagebox("Error", f"Unable to generate {title}.", error=True)
# Main function
def main():
"""
Main function to orchestrate the Peer-to-Peer Lending Simulator.
"""
# Set up tkinter root window for file dialogs and plots
#file:///C:/Users/Emma%20Robinson/Downloads/FIN3028_Companion_Notes.pdf
root = tk.Tk()
root.title("Peer-to-Peer Lending Simulator")
root.geometry("800x600") #Set window size
logging.info("Starting Peer-to-Peer Lending Simulator.")
# File selection
lenders_file = select_file("Lenders")
borrowers_file = select_file("Borrowers")
if not lenders_file or not borrowers_file:
show_messagebox("Error", "Both lenders and borrowers files are required.", error=True)
return
try:
validate_json_file(lenders_file)
validate_json_file(borrowers_file)
except ValueError as e:
show_messagebox("Error", str(e), error=True)
return
# Load data
lenders = load_json_data(lenders_file)
borrowers = load_json_data(borrowers_file)
if not lenders:
show_messagebox("Error", "Lenders data is empty or invalid.", error = True)
return
if not borrowers:
show_messagebox("Error", "Borrowers data is empty or invalid.", error = True)
return
# Debug prints to verify structure
print("Lenders loaded:", lenders)
print("Borrowers loaded:", borrowers)
# Process matches
matches, unmatched_borrowers = match_lenders_borrowers(lenders, borrowers)
if not matches:
logging.info(f"Lenders: {lenders}")
logging.info(f"Borrowers: {borrowers}")
show_messagebox("Info", "No matches were found. Check logs for details.")
return
# Simulate repayment schedules
repayment_schedule = simulate_repayment_schedule(matches, borrowers)
if not repayment_schedule:
show_messagebox("Error", "Failed to generate repayment schedule.", error=True)
return
# Save results
save_results(matches, repayment_schedule)
# Generate and display summary
summary = generate_summary(repayment_schedule)
if not summary.empty:
print("\nRepayment Summary:\n")
print(summary)
# Optional: Save the summary to a CSV file
summary.to_csv("repayment_summary.csv")
print("Repayment summary saved as 'repayment_summary.csv'.")
#Visualise lender funds and repayment schedules
fig1 = plot_lender_funds(lenders)
display_plot(fig1, root, title="Lender Funds Visualisation")
fig2 = plot_repayment_schedules(repayment_schedule)
display_plot(fig2, root, title="Repayment Schedules Visualisation")
root.mainloop() # Keep the plots open
if __name__ == "__main__":
main()This is my visualisation codeimport matplotlib.pyplot as plt
import logging
import matplotlib.ticker as mticker
import os
logging.basicConfig(level=logging.ERROR)
#Plot amount of funds lenders have remaining after matching loans
def plot_lender_funds(lenders, save_path = None):
"""
Creates a bar chart showing remaining funds each lender has
Parameters:
- lenders (list of dict): List of lender details
Returns:
- matplotlib.figure.Figure: The figure object containing the bar chart.
"""
if not lenders:
logging.error("No lender data available to plot.")
return None
filtered_lenders = []
for lender in lenders:
if 'name' not in lender or 'funds' not in lender:
logging.error(f"Missing 'name' of 'funds' key in lender: {lender}")
continue
filtered_lenders.append(lender)
if not filtered_lenders:
logging.error("No valid lender data available for plotting.")
return None
# Sort and limit to top 10 lenders for large datasets
filtered_lenders = sorted(filtered_lenders, key=lambda x: x['available_funds'], reverse=True)[:10]
lender_names = [lender['name'] for lender in lenders]
funds_remaining = [lender['funds'] for lender in lenders]
fig, ax = plt.subplots(figsize = (8,6))
ax.bar(lender_names, funds_remaining)
ax.set_title("Lender Funds Remaining")
ax.set_xlabel("Lender")
ax.set_ylabel("Funds Remaining (GBP)")
#Format Y-axis for currency
ax.yaxis.set_major_formatter(mticker.StrMethodFormatter('£{x:,.0f}'))
plt.xticks(rotation = 45)
plt.tight_layout()
if save_path:
save_path = os.path.abspath(save_path)
os.makedirs(os.path.dirname(save_path), exist_ok=True) # Ensure directory exists
fig.savefig(save_path)
return fig
#Plot repayment schedules over time for each borrower - line plot
def plot_repayment_schedules(repayment_schedule, save_path = None):
"""
Plots repayment schedules for loan over time for each borrower
Parameters:
- repayment_schdeule (list of dict): The repayment schedule
Returns:
- matplotlib.figure.Figure: The figure object containing the repayment schedule plot.
"""
if not repayment_schedule:
logging.error("No repayment schedule data to plot.")
return None
#Group repayments by borrower
borrowers = {entry['borrower_id'] for entry in repayment_schedule}
fig, ax = plt.subplots(figsize = (10, 6))
#Loop through each borrower - extract monthly payments and months
for borrower_id in borrowers:
borrower_payments = [entry['payment'] for entry in repayment_schedule if entry['borrower_id'] == borrower_id]
months = [entry['month'] for entry in repayment_schedule if entry['borrower_id'] == borrower_id]
ax.plot(months, borrower_payments, label = f"Borrower {borrower_id}")
ax.set_title("Repayment Schedules")
ax.set_xlabel("Month")
ax.set_ylabel("Monthly Payment (GBP)")
ax.legend(title="Borrower ID")
ax.grid(True)
plt.tight_layout()
if save_path:
save_path = os.path.abspath(save_path)
os.makedirs(os.path.dirname(save_path), exist_ok=True) # Ensure directory exists
fig.savefig(save_path)
return figThis is my borrower data and lender data i used to test:Output:[
{
"id": 1,
"name": "Lender A",
"available_funds": 5000,
"min_interest_rate_%": 5,
"max_risk": 2
},
{
"id": 2,
"name": "Lender B",
"available_funds": 3000,
"min_interest_rate_%": 4,
"max_risk": 1
},
{
"id": 3,
"name": "Lender C",
"available_funds": 7000,
"min_interest_rate_%": 6,
"max_risk": 3
}
]
[
{
"id": 1,
"name": "Borrower X",
"amount_needed": 2000,
"interest_rate_%": 6,
"risk": 1,
"duration_years": 3
},
{
"id": 2,
"name": "Borrower Y",
"amount_needed": 4000,
"interest_rate_%": 5,
"risk": 2,
"duration_years": 2
},
{
"id": 3,
"name": "Borrower Z",
"amount_needed": 1500,
"interest_rate_%": 7,
"risk": 3,
"duration_years": 1
}
]Not sure if my mistake is easy found, just have been looking for ages and would appreciate any help. Thanks
buran write Dec-12-2024, 11:58 AM:
Please, use proper tags when post code, traceback, output, etc. This time I have added tags for you.
See BBcode help for more info.
Please, use proper tags when post code, traceback, output, etc. This time I have added tags for you.
See BBcode help for more info.
