Spaces:
Sleeping
Sleeping
| 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. | |
| 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]) |