import streamlit as st import pandas as pd import joblib import numpy as np from sklearn.neighbors import NearestNeighbors from sklearn.preprocessing import MinMaxScaler import os # --- Load Assets (Model and Data) --- # Uses caching to load heavy files (model, idlist, scaler, and large DataFrame) only once. @st.cache_resource def load_assets(): # File Names MODEL_FILENAME = 'src/book_recommendation_model.jobjob' IDLIST_FILENAME = 'src/knn_idlist.jobjob' SCALER_FILENAME = 'src/min_max_scaler.jobjob' DATA_FILENAME = 'src/books.csv' try: # 1. Load Saved Objects model = joblib.load(MODEL_FILENAME) idlist = joblib.load(IDLIST_FILENAME) scaler = joblib.load(SCALER_FILENAME) # 2. Load the DataFrame df = pd.read_csv(DATA_FILENAME, on_bad_lines='skip') df.columns = df.columns.str.strip() # Clean column names # 3. Check for essential column if 'title' not in df.columns: st.error("The DataFrame must contain a 'title' column.") return None, None, None, None # Successful return of 4 objects return model, df, idlist, scaler except FileNotFoundError: st.error(f"Required files not found. Ensure '{MODEL_FILENAME}', '{IDLIST_FILENAME}', '{SCALER_FILENAME}', and '{DATA_FILENAME}' are in the same directory.") return None, None, None, None # Load assets upon application start loaded_model, df, idlist, scaler = load_assets() # --- Recommendation Function --- def get_recommendations(book_name, df_data, id_list_data): """ Retrieves the titles of books similar to the given book name. """ # Find the index of the input book try: book_idx = df_data[df_data['title'] == book_name].index[0] except IndexError: return ["Error: The specified book title was not found in the dataset."], False # Get neighbor indices from the pre-calculated idlist # Note: idlist[book_idx] contains the indices of the neighbors neighbor_indices = id_list_data[book_idx] # Retrieve the titles of the neighboring books # .unique() helps handle potential duplicates in the dataset recommended_titles = df_data.loc[neighbor_indices, 'title'].unique().tolist() # Remove the queried book itself from the list of recommendations if book_name in recommended_titles: recommended_titles.remove(book_name) # Return the top 5 (or N-1 if N neighbors were used) recommendations return recommended_titles[:5], True # --- Streamlit Interface --- if loaded_model is not None and df is not None: st.title("📚 Book Recommendation System") st.markdown("This application uses a pre-trained K-Nearest Neighbors model to suggest similar books.") # Select box options book_options = df['title'].unique() selected_book = st.selectbox( "Please select a book:", options=book_options, index=0 ) st.markdown("---") if st.button("Generate Recommendations"): with st.spinner('Searching for similar books...'): recommendations, success = get_recommendations(selected_book, df, idlist) if success: st.subheader(f"Recommendations Similar to '{selected_book}':") # Display recommendations as a numbered list for i, title in enumerate(recommendations, 1): st.success(f"{i}. **{title}**") else: st.error(recommendations[0])