Bnava13 commited on
Commit
6a655a9
·
verified ·
1 Parent(s): f3c54bd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +224 -49
app.py CHANGED
@@ -8,7 +8,7 @@ from sklearn.metrics.pairwise import cosine_similarity
8
  from sklearn.preprocessing import MinMaxScaler
9
 
10
  # Load dataset with proper error handling
11
- def load_data(file_path='steam.csv', max_rows=27075):
12
  try:
13
  data = pd.read_csv(file_path, quotechar='"', on_bad_lines='skip', nrows=max_rows)
14
  print(f"Successfully loaded {len(data)} games from {file_path}")
@@ -16,7 +16,7 @@ def load_data(file_path='steam.csv', max_rows=27075):
16
  except Exception as e:
17
  print(f"Error loading data: {e}")
18
  # Return empty DataFrame with expected columns to avoid crashing
19
- return pd.DataFrame(columns=['name', 'genres', 'categories', 'steamspy_tags', 'platforms', 'positive_ratings', 'price'])
20
 
21
  # Load and preprocess data
22
  data = load_data()
@@ -24,7 +24,7 @@ data = load_data()
24
  # Only proceed if we have data
25
  if len(data) > 0:
26
  # Handle missing values
27
- for feature in ['genres', 'categories', 'steamspy_tags', 'platforms', 'positive_ratings', 'price']:
28
  if feature not in data.columns:
29
  data[feature] = ''
30
  elif data[feature].dtype == object: # String columns
@@ -38,6 +38,21 @@ if len(data) > 0:
38
  else:
39
  data['rating_ratio'] = 0.5 # Default neutral rating
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  # Create a more comprehensive combined feature set with weighted components
42
  data['combined_features'] = ''
43
 
@@ -50,12 +65,17 @@ if len(data) > 0:
50
  data['combined_features'] += data['genres'].astype(str) + ' ' + data['genres'].astype(str) + ' '
51
 
52
  # Add other features
53
- for feature in ['categories', 'steamspy_tags', 'platforms']:
 
 
 
 
 
54
  if feature in data.columns:
55
  data['combined_features'] += data[feature].astype(str) + ' '
56
 
57
  # Clean the combined features
58
- data['combined_features'] = data['combined_features'].str.replace(';', ' ').str.lower()
59
 
60
  # Vectorize with improved parameters
61
  try:
@@ -114,20 +134,63 @@ else:
114
 
115
  # Improved platform detection function
116
  def detect_platforms(platforms_str):
117
- platforms_str = str(platforms_str).lower()
118
  platforms = []
119
 
120
- if 'windows' in platforms_str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  platforms.append("Windows")
122
- if any(mac_term in platforms_str for mac_term in ['mac', 'macos', 'osx']):
123
  platforms.append("macOS")
124
- if 'linux' in platforms_str:
125
  platforms.append("Linux")
126
- if any(mobile_term in platforms_str for mobile_term in ['android', 'ios', 'mobile']):
127
- platforms.append("Mobile")
 
 
128
 
129
  return platforms if platforms else ["Unknown"]
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  # Create price gauge visualization
132
  def create_price_gauge(game_price, similar_games_prices):
133
  # Add the main game price to the list
@@ -167,10 +230,50 @@ def create_price_gauge(game_price, similar_games_prices):
167
 
168
  return fig
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  # Enhanced game recommendation function
171
  def recommend_games(user_game_name_input):
172
  if not user_game_name_input or not list_of_all_titles:
173
- return "Please enter a game name and ensure the dataset is loaded.", [], None
174
 
175
  # Normalize input for better matching
176
  user_input_cleaned = user_game_name_input.strip().lower()
@@ -197,7 +300,7 @@ def recommend_games(user_game_name_input):
197
  )
198
 
199
  if not find_close_match:
200
- return f"No match found for '{user_game_name_input}'. Please try another game name.", [], None
201
 
202
  # Take the closest match
203
  closest_match = find_close_match[0]
@@ -207,7 +310,7 @@ def recommend_games(user_game_name_input):
207
 
208
  # Check for valid index
209
  if index_of_the_game >= len(game_similarity):
210
- return f"Found match '{closest_match}' but encountered an indexing error.", [], None
211
 
212
  similarity_scores = list(enumerate(game_similarity[index_of_the_game]))
213
 
@@ -221,18 +324,22 @@ def recommend_games(user_game_name_input):
221
  rating_factor = data.iloc[idx]['positive_ratings_scaled']
222
 
223
  # Calculate genre similarity separately
224
- searched_game_genres = str(data.iloc[index_of_the_game].get('genres', '')).lower().split(';')
225
- current_game_genres = str(data.iloc[idx].get('genres', '')).lower().split(';')
226
 
227
  # Count matching genres
228
  matching_genres = len(set(searched_game_genres) & set(current_game_genres))
229
- genre_factor = matching_genres / max(len(searched_game_genres), 1)
 
 
 
230
 
231
  # Create hybrid score with weights
232
  hybrid_score = (
233
- 0.65 * sim_score + # Base similarity from TF-IDF vectors
234
- 0.20 * rating_factor + # Rating popularity
235
- 0.15 * genre_factor # Genre match
 
236
  )
237
 
238
  game_rankings.append((idx, hybrid_score))
@@ -245,43 +352,75 @@ def recommend_games(user_game_name_input):
245
 
246
  # Get searched game details
247
  searched_game = data.iloc[index_of_the_game]
248
- searched_game_genres = str(searched_game.get('genres', '')).split(';')
 
 
249
  searched_game_genres_display = ", ".join([g for g in searched_game_genres if g])
250
- searched_game_platforms = detect_platforms(searched_game.get('platforms', ''))
 
 
251
  searched_game_platform_display = ", ".join(searched_game_platforms)
 
 
252
  searched_game_price = searched_game.get('price', 0)
253
  searched_game_price_display = f"${searched_game_price:.2f}" if isinstance(searched_game_price, (int, float)) else "N/A"
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  # Format the searched game with clean styling
256
  recommendations.append(f"## You searched for: {closest_match}\n" +
257
  f"**Genres:** {searched_game_genres_display}\n" +
258
  f"**Platforms:** {searched_game_platform_display}\n" +
259
- f"**Price:** {searched_game_price_display}\n")
 
 
 
260
 
261
  game_list.append(closest_match)
262
 
263
  # Add a divider
264
  recommendations.append("---\n## Top Recommendations\n")
265
 
266
- # Get prices for similar games (for gauge visualization)
267
  similar_games_prices = []
268
 
 
 
 
 
 
 
 
269
  # Process recommendations with diversity enforcement
270
  seen_publishers = set()
271
- if 'publisher' in data.columns:
272
- searched_game_publisher = str(searched_game.get('publisher', '')).lower()
273
  seen_publishers.add(searched_game_publisher)
274
 
275
  recommended_count = 0
276
 
277
  # Process recommendations
278
  for i, (index, score) in enumerate(sorted_similar_games):
279
- if score < 0.15: # Minimum threshold for quality
280
  continue
281
 
282
  # Enforce diversity by limiting games from same publisher
283
- if 'publisher' in data.columns:
284
- current_publisher = str(data.iloc[index].get('publisher', '')).lower()
285
  if current_publisher in seen_publishers and len(seen_publishers) > 2:
286
  continue
287
  seen_publishers.add(current_publisher)
@@ -289,8 +428,7 @@ def recommend_games(user_game_name_input):
289
  game_name = data.iloc[index]['name']
290
 
291
  # Get platform info
292
- platforms = data.iloc[index].get('platforms', '')
293
- platform_list = detect_platforms(platforms)
294
  platform_display = ", ".join(platform_list)
295
 
296
  # Get price info
@@ -299,9 +437,17 @@ def recommend_games(user_game_name_input):
299
  price_display = f"${price:.2f}" if isinstance(price, (int, float)) else "N/A"
300
 
301
  # Get genre info
302
- genres = str(data.iloc[index].get('genres', '')).split(';')
303
  genres_display = ", ".join([g for g in genres if g])
304
 
 
 
 
 
 
 
 
 
305
  # Calculate match percentage
306
  match_percentage = min(int(score * 100), 100) # Cap at 100%
307
 
@@ -312,7 +458,9 @@ def recommend_games(user_game_name_input):
312
  f"**Match:** {match_percentage}%\n" +
313
  f"**Genres:** {genres_display}\n" +
314
  f"**Platforms:** {platform_display}\n" +
315
- f"**Price:** {price_display}\n"
 
 
316
  )
317
 
318
  recommendations.append(recommendation)
@@ -325,43 +473,70 @@ def recommend_games(user_game_name_input):
325
  # Create price gauge visualization
326
  price_gauge = create_price_gauge(searched_game_price, similar_games_prices)
327
 
328
- return "\n".join(recommendations), game_list, price_gauge
 
 
 
329
 
330
  except Exception as e:
331
- return f"Error while finding recommendations: {str(e)}", [], None
332
 
333
- # Gradio UI with simplified design
334
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
335
  gr.Markdown("# Steam Game Recommender")
336
- gr.Markdown("Enter the name of a game you like and get recommendations based on similarity.")
337
 
338
  with gr.Row():
339
  with gr.Column(scale=4):
340
  input_box = gr.Textbox(
341
  label="Your Favorite Game",
342
- placeholder="e.g., Portal 2, Half-Life 2, Skyrim",
343
  info="Type a game name that exists in the Steam dataset"
344
  )
345
  with gr.Column(scale=1):
346
  run_button = gr.Button("Find Recommendations", variant="primary")
347
 
348
- with gr.Row():
349
- with gr.Column(scale=3):
350
- # Recommendations output
351
- output_text = gr.Markdown(label="Recommendations")
352
- with gr.Column(scale=2):
353
- # Price gauge visualization
354
- price_gauge = gr.Plot(label="Price Comparison")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
  # Register event
357
  def on_submit(user_input):
358
- rec_text, game_list, gauge = recommend_games(user_input)
359
- return rec_text, gauge
360
 
361
  run_button.click(
362
  fn=on_submit,
363
  inputs=input_box,
364
- outputs=[output_text, price_gauge],
365
  show_progress=True
366
  )
367
 
@@ -369,7 +544,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
369
  input_box.submit(
370
  fn=on_submit,
371
  inputs=input_box,
372
- outputs=[output_text, price_gauge],
373
  show_progress=True
374
  )
375
 
 
8
  from sklearn.preprocessing import MinMaxScaler
9
 
10
  # Load dataset with proper error handling
11
+ def load_data(file_path='games_march2025_cleaned.csv', max_rows=88899):
12
  try:
13
  data = pd.read_csv(file_path, quotechar='"', on_bad_lines='skip', nrows=max_rows)
14
  print(f"Successfully loaded {len(data)} games from {file_path}")
 
16
  except Exception as e:
17
  print(f"Error loading data: {e}")
18
  # Return empty DataFrame with expected columns to avoid crashing
19
+ return pd.DataFrame(columns=['name', 'genres', 'categories', 'tags', 'platforms', 'positive_ratings', 'price'])
20
 
21
  # Load and preprocess data
22
  data = load_data()
 
24
  # Only proceed if we have data
25
  if len(data) > 0:
26
  # Handle missing values
27
+ for feature in ['genres', 'categories', 'tags', 'platforms', 'positive_ratings', 'negative_ratings', 'price']:
28
  if feature not in data.columns:
29
  data[feature] = ''
30
  elif data[feature].dtype == object: # String columns
 
38
  else:
39
  data['rating_ratio'] = 0.5 # Default neutral rating
40
 
41
+ # Add playtime features if available
42
+ if 'average_playtime_forever' in data.columns:
43
+ # Log transform to handle skewed distribution
44
+ data['log_playtime'] = np.log1p(data['average_playtime_forever'])
45
+ scaler = MinMaxScaler()
46
+ data['playtime_scaled'] = scaler.fit_transform(data[['log_playtime']])
47
+ else:
48
+ data['playtime_scaled'] = 0.5
49
+
50
+ # Add user score features if available
51
+ if 'user_score' in data.columns:
52
+ data['user_score_scaled'] = data['user_score'] / 100.0 # Assuming user_score is out of 100
53
+ else:
54
+ data['user_score_scaled'] = 0.5
55
+
56
  # Create a more comprehensive combined feature set with weighted components
57
  data['combined_features'] = ''
58
 
 
65
  data['combined_features'] += data['genres'].astype(str) + ' ' + data['genres'].astype(str) + ' '
66
 
67
  # Add other features
68
+ for feature in ['categories', 'tags', 'platforms']:
69
+ if feature in data.columns:
70
+ data['combined_features'] += data[feature].astype(str) + ' '
71
+
72
+ # Add developers and publishers if available
73
+ for feature in ['developers', 'publishers']:
74
  if feature in data.columns:
75
  data['combined_features'] += data[feature].astype(str) + ' '
76
 
77
  # Clean the combined features
78
+ data['combined_features'] = data['combined_features'].str.replace(';', ' ').str.replace("'", '').str.replace('[', '').str.replace(']', '').str.replace('{', '').str.replace('}', '').str.lower()
79
 
80
  # Vectorize with improved parameters
81
  try:
 
134
 
135
  # Improved platform detection function
136
  def detect_platforms(platforms_str):
 
137
  platforms = []
138
 
139
+ if isinstance(platforms_str, str):
140
+ platforms_str = platforms_str.lower()
141
+
142
+ if 'windows' in platforms_str or 'true' in platforms_str:
143
+ platforms.append("Windows")
144
+ if any(mac_term in platforms_str for mac_term in ['mac', 'macos', 'osx']):
145
+ platforms.append("macOS")
146
+ if 'linux' in platforms_str:
147
+ platforms.append("Linux")
148
+ if any(mobile_term in platforms_str for mobile_term in ['android', 'ios', 'mobile']):
149
+ platforms.append("Mobile")
150
+ elif isinstance(platforms_str, bool) and platforms_str:
151
+ # Handle boolean True values
152
+ platforms.append("Windows") # Assuming Windows by default if boolean True
153
+
154
+ return platforms if platforms else ["Unknown"]
155
+
156
+ # Extract platform information from dataset columns
157
+ def get_platforms(row):
158
+ platforms = []
159
+
160
+ # Check for platform columns from the screenshots (windows, mac, linux)
161
+ if 'windows' in row and row['windows']:
162
  platforms.append("Windows")
163
+ if 'mac' in row and row['mac']:
164
  platforms.append("macOS")
165
+ if 'linux' in row and row['linux']:
166
  platforms.append("Linux")
167
+
168
+ # If no platforms detected but there's a platforms field, try that
169
+ if not platforms and 'platforms' in row:
170
+ platforms = detect_platforms(row['platforms'])
171
 
172
  return platforms if platforms else ["Unknown"]
173
 
174
+ # Extract genre information
175
+ def extract_genres(genres_str):
176
+ if not genres_str or pd.isna(genres_str):
177
+ return []
178
+
179
+ # Handle different formats that might be in the data
180
+ if isinstance(genres_str, str):
181
+ # Remove common formatting characters
182
+ clean_str = genres_str.replace("'", "").replace("[", "").replace("]", "").replace("{", "").replace("}", "")
183
+
184
+ # Try different delimiters
185
+ if ',' in clean_str:
186
+ return [g.strip() for g in clean_str.split(',') if g.strip()]
187
+ elif ';' in clean_str:
188
+ return [g.strip() for g in clean_str.split(';') if g.strip()]
189
+ else:
190
+ return [clean_str]
191
+
192
+ return []
193
+
194
  # Create price gauge visualization
195
  def create_price_gauge(game_price, similar_games_prices):
196
  # Add the main game price to the list
 
230
 
231
  return fig
232
 
233
+ # Create user ratings visualization
234
+ def create_ratings_chart(game_data):
235
+ if not isinstance(game_data, dict):
236
+ return None
237
+
238
+ # Extract ratings data
239
+ game_name = game_data.get('name', 'Unknown')
240
+ positive = game_data.get('positive', 0)
241
+ negative = game_data.get('negative', 0)
242
+
243
+ # Calculate percentages
244
+ total = positive + negative
245
+ if total == 0:
246
+ positive_pct = 0
247
+ negative_pct = 0
248
+ else:
249
+ positive_pct = (positive / total) * 100
250
+ negative_pct = (negative / total) * 100
251
+
252
+ # Create bar chart
253
+ fig = go.Figure()
254
+
255
+ fig.add_trace(go.Bar(
256
+ x=['Positive', 'Negative'],
257
+ y=[positive, negative],
258
+ text=[f"{positive:,} ({positive_pct:.1f}%)", f"{negative:,} ({negative_pct:.1f}%)"],
259
+ textposition='auto',
260
+ marker_color=['#66c0f4', '#ff7b7b'] # Steam-like colors
261
+ ))
262
+
263
+ fig.update_layout(
264
+ title=f"User Ratings for {game_name}",
265
+ xaxis_title="Rating Type",
266
+ yaxis_title="Number of Ratings",
267
+ height=300,
268
+ margin=dict(l=20, r=20, t=50, b=20),
269
+ )
270
+
271
+ return fig
272
+
273
  # Enhanced game recommendation function
274
  def recommend_games(user_game_name_input):
275
  if not user_game_name_input or not list_of_all_titles:
276
+ return "Please enter a game name and ensure the dataset is loaded.", [], None, None
277
 
278
  # Normalize input for better matching
279
  user_input_cleaned = user_game_name_input.strip().lower()
 
300
  )
301
 
302
  if not find_close_match:
303
+ return f"No match found for '{user_game_name_input}'. Please try another game name.", [], None, None
304
 
305
  # Take the closest match
306
  closest_match = find_close_match[0]
 
310
 
311
  # Check for valid index
312
  if index_of_the_game >= len(game_similarity):
313
+ return f"Found match '{closest_match}' but encountered an indexing error.", [], None, None
314
 
315
  similarity_scores = list(enumerate(game_similarity[index_of_the_game]))
316
 
 
324
  rating_factor = data.iloc[idx]['positive_ratings_scaled']
325
 
326
  # Calculate genre similarity separately
327
+ searched_game_genres = extract_genres(data.iloc[index_of_the_game].get('genres', ''))
328
+ current_game_genres = extract_genres(data.iloc[idx].get('genres', ''))
329
 
330
  # Count matching genres
331
  matching_genres = len(set(searched_game_genres) & set(current_game_genres))
332
+ genre_factor = matching_genres / max(len(searched_game_genres), 1) if searched_game_genres else 0
333
+
334
+ # Add playtime score if available
335
+ playtime_factor = data.iloc[idx].get('playtime_scaled', 0)
336
 
337
  # Create hybrid score with weights
338
  hybrid_score = (
339
+ 0.60 * sim_score + # Base similarity from TF-IDF vectors
340
+ 0.15 * rating_factor + # Rating popularity
341
+ 0.15 * genre_factor + # Genre match
342
+ 0.10 * playtime_factor # Playtime popularity
343
  )
344
 
345
  game_rankings.append((idx, hybrid_score))
 
352
 
353
  # Get searched game details
354
  searched_game = data.iloc[index_of_the_game]
355
+
356
+ # Extract genres
357
+ searched_game_genres = extract_genres(searched_game.get('genres', ''))
358
  searched_game_genres_display = ", ".join([g for g in searched_game_genres if g])
359
+
360
+ # Extract platforms
361
+ searched_game_platforms = get_platforms(searched_game)
362
  searched_game_platform_display = ", ".join(searched_game_platforms)
363
+
364
+ # Get price
365
  searched_game_price = searched_game.get('price', 0)
366
  searched_game_price_display = f"${searched_game_price:.2f}" if isinstance(searched_game_price, (int, float)) else "N/A"
367
 
368
+ # Get ratings
369
+ searched_game_positive = searched_game.get('positive_ratings', searched_game.get('positive', 0))
370
+ searched_game_negative = searched_game.get('negative_ratings', searched_game.get('negative', 0))
371
+
372
+ # Get metacritic score if available
373
+ metacritic_score = searched_game.get('metacritic_score', 'N/A')
374
+ metacritic_display = f"{metacritic_score}/100" if metacritic_score != 'N/A' else "N/A"
375
+
376
+ # Get user score if available
377
+ user_score = searched_game.get('user_score', 'N/A')
378
+ user_score_display = f"{user_score}/100" if user_score != 'N/A' else "N/A"
379
+
380
+ # Get playtime if available
381
+ avg_playtime = searched_game.get('average_playtime_forever', 0)
382
+ playtime_display = f"{avg_playtime} minutes" if avg_playtime > 0 else "N/A"
383
+
384
  # Format the searched game with clean styling
385
  recommendations.append(f"## You searched for: {closest_match}\n" +
386
  f"**Genres:** {searched_game_genres_display}\n" +
387
  f"**Platforms:** {searched_game_platform_display}\n" +
388
+ f"**Price:** {searched_game_price_display}\n" +
389
+ f"**Metacritic Score:** {metacritic_display}\n" +
390
+ f"**User Score:** {user_score_display}\n" +
391
+ f"**Average Playtime:** {playtime_display}\n")
392
 
393
  game_list.append(closest_match)
394
 
395
  # Add a divider
396
  recommendations.append("---\n## Top Recommendations\n")
397
 
398
+ # Get prices and ratings for similar games (for gauge visualization)
399
  similar_games_prices = []
400
 
401
+ # Create ratings data for visualization
402
+ ratings_data = {
403
+ 'name': closest_match,
404
+ 'positive': searched_game_positive,
405
+ 'negative': searched_game_negative
406
+ }
407
+
408
  # Process recommendations with diversity enforcement
409
  seen_publishers = set()
410
+ if 'publishers' in data.columns:
411
+ searched_game_publisher = str(searched_game.get('publishers', '')).lower()
412
  seen_publishers.add(searched_game_publisher)
413
 
414
  recommended_count = 0
415
 
416
  # Process recommendations
417
  for i, (index, score) in enumerate(sorted_similar_games):
418
+ if score < 0.10: # Minimum threshold for quality
419
  continue
420
 
421
  # Enforce diversity by limiting games from same publisher
422
+ if 'publishers' in data.columns:
423
+ current_publisher = str(data.iloc[index].get('publishers', '')).lower()
424
  if current_publisher in seen_publishers and len(seen_publishers) > 2:
425
  continue
426
  seen_publishers.add(current_publisher)
 
428
  game_name = data.iloc[index]['name']
429
 
430
  # Get platform info
431
+ platform_list = get_platforms(data.iloc[index])
 
432
  platform_display = ", ".join(platform_list)
433
 
434
  # Get price info
 
437
  price_display = f"${price:.2f}" if isinstance(price, (int, float)) else "N/A"
438
 
439
  # Get genre info
440
+ genres = extract_genres(data.iloc[index].get('genres', ''))
441
  genres_display = ", ".join([g for g in genres if g])
442
 
443
+ # Get metacritic score if available
444
+ rec_metacritic_score = data.iloc[index].get('metacritic_score', 'N/A')
445
+ rec_metacritic_display = f"{rec_metacritic_score}/100" if rec_metacritic_score != 'N/A' else "N/A"
446
+
447
+ # Get user score if available
448
+ rec_user_score = data.iloc[index].get('user_score', 'N/A')
449
+ rec_user_score_display = f"{rec_user_score}/100" if rec_user_score != 'N/A' else "N/A"
450
+
451
  # Calculate match percentage
452
  match_percentage = min(int(score * 100), 100) # Cap at 100%
453
 
 
458
  f"**Match:** {match_percentage}%\n" +
459
  f"**Genres:** {genres_display}\n" +
460
  f"**Platforms:** {platform_display}\n" +
461
+ f"**Price:** {price_display}\n" +
462
+ f"**Metacritic Score:** {rec_metacritic_display}\n" +
463
+ f"**User Score:** {rec_user_score_display}\n"
464
  )
465
 
466
  recommendations.append(recommendation)
 
473
  # Create price gauge visualization
474
  price_gauge = create_price_gauge(searched_game_price, similar_games_prices)
475
 
476
+ # Create ratings chart
477
+ ratings_chart = create_ratings_chart(ratings_data)
478
+
479
+ return "\n".join(recommendations), game_list, price_gauge, ratings_chart
480
 
481
  except Exception as e:
482
+ return f"Error while finding recommendations: {str(e)}", [], None, None
483
 
484
+ # Gradio UI with improved design
485
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
486
  gr.Markdown("# Steam Game Recommender")
487
+ gr.Markdown("Enter the name of a game you like and get recommendations based on similarity analysis of our Steam games dataset.")
488
 
489
  with gr.Row():
490
  with gr.Column(scale=4):
491
  input_box = gr.Textbox(
492
  label="Your Favorite Game",
493
+ placeholder="e.g., Counter-Strike, PUBG, Dota 2, Grand Theft Auto V",
494
  info="Type a game name that exists in the Steam dataset"
495
  )
496
  with gr.Column(scale=1):
497
  run_button = gr.Button("Find Recommendations", variant="primary")
498
 
499
+ with gr.Tabs():
500
+ with gr.TabItem("Recommendations"):
501
+ with gr.Row():
502
+ with gr.Column(scale=3):
503
+ # Recommendations output
504
+ output_text = gr.Markdown(label="Recommendations")
505
+ with gr.Column(scale=2):
506
+ with gr.Row():
507
+ # Price gauge visualization
508
+ price_gauge = gr.Plot(label="Price Comparison")
509
+ with gr.Row():
510
+ # Ratings chart
511
+ ratings_chart = gr.Plot(label="User Ratings")
512
+
513
+ with gr.TabItem("About"):
514
+ gr.Markdown("""
515
+ ## About This Recommender
516
+
517
+ This Steam game recommender system uses machine learning to find games similar to your favorites. It analyzes:
518
+
519
+ - Game genres and categories
520
+ - User ratings and reviews
521
+ - Platform availability
522
+ - Tags and game descriptions
523
+ - Price points
524
+ - Player statistics
525
+
526
+ The recommendations are based on a hybrid scoring system that combines content similarity, user ratings, and gameplay metrics.
527
+
528
+ For best results, enter the exact name of a game that exists in the Steam database.
529
+ """)
530
 
531
  # Register event
532
  def on_submit(user_input):
533
+ rec_text, game_list, gauge, ratings = recommend_games(user_input)
534
+ return rec_text, gauge, ratings
535
 
536
  run_button.click(
537
  fn=on_submit,
538
  inputs=input_box,
539
+ outputs=[output_text, price_gauge, ratings_chart],
540
  show_progress=True
541
  )
542
 
 
544
  input_box.submit(
545
  fn=on_submit,
546
  inputs=input_box,
547
+ outputs=[output_text, price_gauge, ratings_chart],
548
  show_progress=True
549
  )
550