Bnava13's picture
Update app.py
bb0078d verified
Raw
History Blame
10.9 kB
import pandas as pd
import difflib
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import gradio as gr
import numpy as np
import re
from PIL import Image
from io import BytesIO
import requests
# Load the data - when deploying, adjust the path to where your dataset will be stored
def load_data():
try:
# For Hugging Face Spaces deployment, you might need to adjust this path
data = pd.read_csv('games_march2025_cleaned.csv', nrows=20000, on_bad_lines='skip', engine='python')
return data
except Exception as e:
print(f"Error loading data: {e}")
return None
# Prepare the feature vectors for similarity calculation
def prepare_features(data):
selected_features = ['genres', 'price', 'average_playtime_2weeks', 'tags', 'average_playtime_forever']
for feature in selected_features:
data[feature] = data[feature].fillna('')
combined_features = (
data['genres'] + ' ' +
data['price'].astype(str) + ' ' +
data['average_playtime_2weeks'].astype(str) + ' ' +
data['tags'].astype(str)
)
vectorizer = TfidfVectorizer()
feature_vectors = vectorizer.fit_transform(combined_features)
return feature_vectors
# Function to get game recommendations
def get_recommendations(game_name, data, feature_vectors):
list_of_all_titles = data['name'].tolist()
find_close_match = difflib.get_close_matches(game_name, list_of_all_titles)
if not find_close_match:
return "No match found for the game name. Please try another title."
closest_match = find_close_match[0]
index_of_the_game = data.loc[data['name'] == closest_match].index[0]
game_similarity = cosine_similarity(feature_vectors)
similarity_scores = list(enumerate(game_similarity[index_of_the_game]))
sorted_similar_games = sorted(similarity_scores, key=lambda x: x[1], reverse=True)
results = []
for i, game in enumerate(sorted_similar_games[1:10], 1): # Skip the first one as it's the game itself
index = game[0]
name = data.loc[index, 'name']
# Get additional information
about = data.loc[index, 'about_the_game'] if 'about_the_game' in data.columns else "No description available"
# Try to get an image - first check screenshots, then header_image
image_url = None
if 'screenshots' in data.columns and pd.notna(data.loc[index, 'screenshots']):
# Try to extract the first screenshot URL
screenshots = data.loc[index, 'screenshots']
if isinstance(screenshots, str):
# Handle potential JSON format
if screenshots.startswith('[') and ']' in screenshots:
try:
import json
screenshot_list = json.loads(screenshots)
if screenshot_list and isinstance(screenshot_list, list) and len(screenshot_list) > 0:
if isinstance(screenshot_list[0], dict) and 'path_full' in screenshot_list[0]:
image_url = screenshot_list[0]['path_full']
elif isinstance(screenshot_list[0], str):
image_url = screenshot_list[0]
except:
# If JSON parsing fails, try regex
url_match = re.search(r'https?://[^\s,\'"]+\.(jpg|jpeg|png|gif)', screenshots)
if url_match:
image_url = url_match.group(0)
# If no screenshot, try header image
if (image_url is None or image_url == '') and 'header_image' in data.columns:
image_url = data.loc[index, 'header_image'] if pd.notna(data.loc[index, 'header_image']) else None
# Get platform information
platforms = []
if 'windows' in data.columns and data.loc[index, 'windows'] == 1:
platforms.append("Windows")
if 'mac' in data.columns and data.loc[index, 'mac'] == 1:
platforms.append("Mac")
if 'linux' in data.columns and data.loc[index, 'linux'] == 1:
platforms.append("Linux")
platforms_str = ", ".join(platforms) if platforms else "Unknown"
# Get price information
price = data.loc[index, 'price'] if 'price' in data.columns else None
price_str = f"${price}" if pd.notna(price) and price != '' else "Price not available"
# Format the result
result = f"**{name}**\n\n"
result += f"**Price:** {price_str}\n"
result += f"**Platforms:** {platforms_str}\n\n"
# Add genres if available
if 'genres' in data.columns and pd.notna(data.loc[index, 'genres']):
genres = data.loc[index, 'genres']
if genres and genres != '':
# Clean up genres format
if isinstance(genres, str):
# Handle potential JSON format
if genres.startswith('[') and ']' in genres:
try:
import json
genres_list = json.loads(genres)
if isinstance(genres_list, list):
genres = ", ".join(genres_list)
except:
pass
result += f"**Genres:** {genres}\n\n"
# Truncate and clean the about text
if about and about != "":
# Remove HTML tags
about_clean = re.sub(r'<.*?>', '', about)
about_truncated = about_clean[:300] + "..." if len(about_clean) > 300 else about_clean
result += f"**About the Game:** {about_truncated}\n"
else:
result += "**About the Game:** No description available\n"
results.append((result, image_url))
return results
# Function to safely load image from URL
def load_image_safely(url):
if not url or str(url).lower() == 'nan':
return None
try:
response = requests.get(url, timeout=5)
if response.status_code == 200:
return Image.open(BytesIO(response.content))
else:
return None
except:
return None
# Gradio interface function
def recommend_games(game_name):
data = load_data()
if data is None:
return "Failed to load data. Please check the data file."
feature_vectors = prepare_features(data)
recommendations = get_recommendations(game_name, data, feature_vectors)
if isinstance(recommendations, str):
return recommendations
# Format the output for Gradio
result_texts = []
result_images = []
for result, image_url in recommendations:
result_texts.append(result)
# Add similarity score if available
if image_url and str(image_url) != 'nan':
# For Hugging Face Spaces, use the URL directly
# The image loading will happen through the browser
result_images.append(image_url)
else:
# Use a placeholder image if no image URL is available
result_images.append(None)
# Return list of recommendations with their info
return result_texts, result_images
# Create the Gradio interface with individual game cards
def create_recommendation_ui(game_name):
data = load_data()
if data is None:
return [gr.Markdown("Failed to load data. Please check the data file.")]
feature_vectors = prepare_features(data)
recommendations = get_recommendations(game_name, data, feature_vectors)
if isinstance(recommendations, str):
return [gr.Markdown(recommendations)]
result_texts, result_images = recommendations
# Create output components dynamically
output_components = []
for i, (text, img_url) in enumerate(zip(result_texts, result_images)):
with gr.Group():
with gr.Row():
with gr.Column(scale=1):
if img_url and str(img_url) != 'nan':
output_components.append(gr.Image(value=img_url, label=f"Game {i+1}"))
else:
output_components.append(gr.Markdown("*No image available*"))
with gr.Column(scale=2):
output_components.append(gr.Markdown(text))
output_components.append(gr.Markdown("---"))
return output_components
with gr.Blocks(title="Steam Game Recommender") as demo:
gr.Markdown("# Steam Game Recommender")
gr.Markdown("Enter your favorite game to get recommendations for similar games.")
with gr.Row():
input_text = gr.Textbox(label="Enter your favorite game:", placeholder="e.g., Half-Life 2")
submit_btn = gr.Button("Get Recommendations", variant="primary")
output_container = gr.Group(visible=False)
with output_container:
gr.Markdown("## Your Recommendations")
recommendation_outputs = []
for i in range(9): # For 9 recommendations
with gr.Group():
with gr.Row():
with gr.Column(scale=1):
recommendation_outputs.append(gr.Image(label=f"Game {i+1}"))
with gr.Column(scale=2):
recommendation_outputs.append(gr.Markdown())
recommendation_outputs.append(gr.Markdown("---"))
def process_recommendations(game_name):
data = load_data()
if data is None:
return [gr.update(visible=True), gr.update(value="Failed to load data. Please check the data file.")]
feature_vectors = prepare_features(data)
recommendations = get_recommendations(game_name, data, feature_vectors)
if isinstance(recommendations, str):
return [gr.update(visible=True), gr.update(value=recommendations)]
result_texts, result_images = recommendations
updates = [gr.update(visible=True)]
for i, (text, img_url) in enumerate(zip(result_texts, result_images)):
updates.append(gr.update(value=img_url if img_url and str(img_url) != 'nan' else None))
updates.append(gr.update(value=text))
updates.append(gr.update())
# Fill any remaining slots with empty updates
while len(updates) < len(recommendation_outputs) + 1:
updates.append(gr.update(visible=False))
return updates
submit_btn.click(
fn=process_recommendations,
inputs=input_text,
outputs=[output_container] + recommendation_outputs
)
# Launch the app
if __name__ == "__main__":
demo.launch()