import gradio as gr import pandas as pd import numpy as np from scipy.sparse.linalg import svds from sklearn.metrics.pairwise import cosine_similarity import plotly.express as px import plotly.graph_objects as go from collections import Counter # Global variables for models movies = None ratings = None users = None train_user_item_matrix = None user_similarity_df = None svd_predicted_ratings = None alpha = 0.6 models_loaded = False def load_datasets(): """Load CSV datasets with multiple encoding support""" global movies, ratings, users try: encodings = ['utf-8', 'latin-1', 'iso-8859-1', 'cp1252'] delimiters = [',', '::', '\t', '|', ';'] movies = None ratings = None users = None # Load movies for enc in encodings: for delim in delimiters: try: movies = pd.read_csv('movies.csv', encoding=enc, sep=delim, engine='python', on_bad_lines='skip') if len(movies.columns) >= 2: break except: continue if movies is not None and len(movies.columns) >= 2: break # Load ratings for delim in delimiters: try: ratings = pd.read_csv('ratings.csv', sep=delim, engine='python', on_bad_lines='skip') if len(ratings.columns) >= 3: break except: continue # Load users for delim in delimiters: try: users = pd.read_csv('users.csv', sep=delim, engine='python', on_bad_lines='skip') if len(users.columns) >= 2: break except: continue if movies is None or ratings is None or users is None: return "Failed to load datasets. Check file formats." # Normalize column names movies.columns = movies.columns.str.strip().str.lower() ratings.columns = ratings.columns.str.strip().str.lower() users.columns = users.columns.str.strip().str.lower() if 'genres' in movies.columns: movies['genres'] = movies['genres'].fillna('Unknown') return f"Loaded: {len(movies)} movies, {len(ratings)} ratings, {len(users)} users" except Exception as e: return f"Error: {str(e)}" def train_models(): """Train recommendation models""" global train_user_item_matrix, user_similarity_df, svd_predicted_ratings, models_loaded if movies is None or ratings is None: return "Please load datasets first!" try: # Create train split train_data = [] for user_id in ratings['userid'].unique(): user_ratings = ratings[ratings['userid'] == user_id] if 'timestamp' in ratings.columns: user_ratings = user_ratings.sort_values('timestamp') n_ratings = len(user_ratings) if n_ratings >= 5: split_idx = int(n_ratings * 0.8) train_data.append(user_ratings.iloc[:split_idx]) train_ratings = pd.concat(train_data, ignore_index=True) # Create user-item matrix train_user_item_matrix = train_ratings.pivot_table( index='userid', columns='movieid', values='rating' ).fillna(0) # Train User-Based CF user_similarity = cosine_similarity(train_user_item_matrix) user_similarity_df = pd.DataFrame( user_similarity, index=train_user_item_matrix.index, columns=train_user_item_matrix.index ) # Train SVD n_factors = min(100, min(train_user_item_matrix.shape) - 1) R = train_user_item_matrix.values user_ratings_mean = np.mean(R, axis=1) R_demeaned = R - user_ratings_mean.reshape(-1, 1) U, sigma, Vt = svds(R_demeaned, k=n_factors) sigma = np.diag(sigma) predicted_ratings = np.dot(np.dot(U, sigma), Vt) + user_ratings_mean.reshape(-1, 1) svd_predicted_ratings = pd.DataFrame( predicted_ratings, index=train_user_item_matrix.index, columns=train_user_item_matrix.columns ) models_loaded = True return "Models trained successfully!" except Exception as e: return f"Error training models: {str(e)}" def load_and_train(): """Load datasets and train models""" msg1 = load_datasets() if "Loaded:" not in msg1: return msg1, None, None msg2 = train_models() # Get dataset stats stats_html = f"""

Dataset Statistics

{len(movies):,}
Movies
{len(users):,}
Users
{len(ratings):,}
Ratings
{ratings['rating'].mean():.2f}
Avg Rating
""" # Create rating distribution chart rating_dist = ratings['rating'].value_counts().sort_index() fig = px.bar(x=rating_dist.index, y=rating_dist.values, labels={'x': 'Rating', 'y': 'Count'}, title='Rating Distribution', color=rating_dist.values, color_continuous_scale='Viridis') return f"{msg1}\n{msg2}", stats_html, fig def recommend_movies(user_id, num_recommendations): """Generate movie recommendations""" if not models_loaded: return "Please load and train models first!", None, None try: user_id = int(user_id) num_recommendations = int(num_recommendations) if user_id not in train_user_item_matrix.index: return f"User {user_id} not found in training data", None, None # CF recommendations similar_users = user_similarity_df[user_id].sort_values(ascending=False)[1:51] user_ratings = train_user_item_matrix.loc[user_id] watched_movies = user_ratings[user_ratings > 0].index cf_recommendations = {} for sim_user, similarity in similar_users.items(): sim_user_ratings = train_user_item_matrix.loc[sim_user] for movie_id, rating in sim_user_ratings.items(): if rating > 0 and movie_id not in watched_movies: if movie_id not in cf_recommendations: cf_recommendations[movie_id] = 0 cf_recommendations[movie_id] += similarity * rating cf_top = sorted(cf_recommendations.items(), key=lambda x: x[1], reverse=True)[:num_recommendations*2] cf_movies = [movie_id for movie_id, _ in cf_top] # SVD recommendations user_pred_ratings = svd_predicted_ratings.loc[user_id] unwatched_predictions = user_pred_ratings.drop(watched_movies) svd_movies = unwatched_predictions.sort_values(ascending=False).head(num_recommendations*2).index.tolist() # Combine combined_scores = {} for i, movie_id in enumerate(cf_movies): combined_scores[movie_id] = combined_scores.get(movie_id, 0) + alpha * (len(cf_movies) - i) for i, movie_id in enumerate(svd_movies): combined_scores[movie_id] = combined_scores.get(movie_id, 0) + (1 - alpha) * (len(svd_movies) - i) top_movies = sorted(combined_scores.items(), key=lambda x: x[1], reverse=True)[:num_recommendations] movie_ids = [movie_id for movie_id, _ in top_movies] # Get movie details recommendations = [] for i, movie_id in enumerate(movie_ids, 1): movie_info = movies[movies['movieid'] == movie_id] if not movie_info.empty: title = movie_info.iloc[0]['title'] genres = movie_info.iloc[0].get('genres', 'Unknown') recommendations.append({ 'Rank': i, 'Title': title, 'Genres': genres }) # Create HTML output html_output = f"""

Top {num_recommendations} Recommendations for User {user_id}

""" for rec in recommendations: html_output += f"""

{rec['Rank']}. {rec['Title']}

Genres: {rec['Genres']}

""" html_output += "
" # Create visualizations user_ratings_data = ratings[ratings['userid'] == user_id] # Rating distribution rating_dist = user_ratings_data['rating'].value_counts().sort_index() fig1 = px.bar(x=rating_dist.index, y=rating_dist.values, labels={'x': 'Rating', 'y': 'Count'}, title=f'User {user_id} Rating Distribution', color=rating_dist.values, color_continuous_scale='Blues') # Genre preferences user_movies = user_ratings_data.merge(movies[['movieid', 'genres']], on='movieid') genres_list = [] for genres in user_movies['genres']: if pd.notna(genres) and genres != 'Unknown': genres_list.extend(genres.split('|')) if genres_list: genre_counts = Counter(genres_list) top_genres = dict(sorted(genre_counts.items(), key=lambda x: x[1], reverse=True)[:8]) fig2 = px.pie(values=list(top_genres.values()), names=list(top_genres.keys()), title=f'User {user_id} Genre Preferences', color_discrete_sequence=px.colors.qualitative.Set3) else: fig2 = None return html_output, fig1, fig2 except Exception as e: return f"Error: {str(e)}", None, None def get_dataset_insights(): """Generate dataset insights""" if movies is None or ratings is None: return "Please load datasets first!", None, None # Genre analysis all_genres = [] for genres in movies['genres']: if pd.notna(genres) and genres != 'Unknown': all_genres.extend(genres.split('|')) genre_counts = Counter(all_genres) top_genres = dict(sorted(genre_counts.items(), key=lambda x: x[1], reverse=True)[:15]) fig1 = px.bar(x=list(top_genres.values()), y=list(top_genres.keys()), orientation='h', labels={'x': 'Number of Movies', 'y': 'Genre'}, title='Top 15 Genres by Movie Count', color=list(top_genres.values()), color_continuous_scale='Teal') # User activity user_activity = ratings.groupby('userid').size() fig2 = px.histogram(user_activity, nbins=50, labels={'value': 'Number of Ratings', 'count': 'Number of Users'}, title='User Activity Distribution', color_discrete_sequence=['coral']) stats = f"""

Insights

Most Popular Genre: {list(top_genres.keys())[0]}

Average User Activity: {user_activity.mean():.1f} ratings

Most Active User: {user_activity.max()} ratings

Total Unique Movies Rated: {ratings['movieid'].nunique()}

""" return stats, fig1, fig2 # Create Gradio Interface with gr.Blocks(title="DataSynthis Movie Recommender", theme=gr.themes.Soft()) as app: gr.Markdown(""" # DataSynthis Movie Recommendation System ### Powered by Hybrid Collaborative Filtering & Matrix Factorization """) with gr.Tabs(): # Tab 1: Setup with gr.Tab("Setup & Load Data"): gr.Markdown("### Step 1: Load Datasets and Train Models") gr.Markdown("Click the button below to load your CSV files and train the recommendation models.") load_btn = gr.Button("Load Datasets & Train Models", variant="primary", size="lg") status_output = gr.Textbox(label="Status", lines=2) stats_output = gr.HTML(label="Dataset Statistics") chart_output = gr.Plot(label="Rating Distribution") load_btn.click( fn=load_and_train, outputs=[status_output, stats_output, chart_output] ) # Tab 2: Recommendations with gr.Tab("Get Recommendations"): gr.Markdown("### Generate Personalized Movie Recommendations") with gr.Row(): with gr.Column(scale=2): user_id_input = gr.Number(label="Enter User ID", value=1, precision=0) with gr.Column(scale=1): num_recs_input = gr.Slider(minimum=5, maximum=20, value=10, step=1, label="Number of Recommendations") recommend_btn = gr.Button("Generate Recommendations", variant="primary", size="lg") recommendations_output = gr.HTML(label="Recommendations") with gr.Row(): rating_chart = gr.Plot(label="User Rating Distribution") genre_chart = gr.Plot(label="Genre Preferences") recommend_btn.click( fn=recommend_movies, inputs=[user_id_input, num_recs_input], outputs=[recommendations_output, rating_chart, genre_chart] ) # Tab 3: Insights with gr.Tab("Dataset Insights"): gr.Markdown("### Explore Dataset Analytics") insights_btn = gr.Button("Generate Insights", variant="primary") insights_stats = gr.HTML(label="Statistics") with gr.Row(): genre_plot = gr.Plot(label="Popular Genres") activity_plot = gr.Plot(label="User Activity") insights_btn.click( fn=get_dataset_insights, outputs=[insights_stats, genre_plot, activity_plot] ) # Tab 4: About with gr.Tab("About"): gr.Markdown(""" ## DataSynthis Movie Recommendation System This intelligent recommendation system uses advanced machine learning algorithms to provide personalized movie suggestions based on user preferences and viewing history. ### Features: - **Hybrid Approach**: Combines User-Based Collaborative Filtering and SVD Matrix Factorization - **High Accuracy**: Trained on comprehensive movie rating datasets - **Real-Time Predictions**: Instant recommendations for any user - **Interactive Visualizations**: Understand user behavior and preferences ### Algorithms Used: 1. **User-Based Collaborative Filtering**: Finds similar users and recommends movies they enjoyed 2. **SVD Matrix Factorization**: Discovers latent patterns in rating data 3. **Hybrid Ensemble**: Weighted combination (60% CF, 40% SVD) for optimal results ### Technology Stack: - Python, Gradio, Scikit-learn, Pandas, NumPy, Plotly --- **Developed for DataSynthis ML Job Task** """) gr.Markdown(""" ---

DataSynthis Movie Recommendation System | Deployed on Hugging Face Spaces

Built with Gradio

""") # Launch the app if __name__ == "__main__": app.launch()