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 code
import 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.