Bnava13 commited on
Commit
d4cf16c
·
verified ·
1 Parent(s): 111fffd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -33
app.py CHANGED
@@ -32,33 +32,57 @@ if len(data) > 0:
32
  else:
33
  data[feature] = data[feature].fillna(0) # Numeric columns
34
 
35
- # Combine features
36
- data['combined_features'] = (
37
- data['genres'].astype(str) + ' ' +
38
- data['categories'].astype(str) + ' ' +
39
- data['steamspy_tags'].astype(str) + ' ' +
40
- data['platforms'].astype(str)
41
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- # Vectorize
44
  try:
45
- vectorizer = TfidfVectorizer(stop_words='english', ngram_range=(1, 2), max_features=5000)
 
 
 
 
 
 
 
46
  feature_vectors = vectorizer.fit_transform(data['combined_features'])
47
  print(f"Vectorization complete. Shape: {feature_vectors.shape}")
48
  except Exception as e:
49
  print(f"Vectorization error: {e}")
50
  feature_vectors = np.zeros((len(data), 1))
51
 
52
- # Normalize positive ratings
53
  if 'positive_ratings' in data.columns and len(data) > 0:
 
 
54
  scaler = MinMaxScaler()
55
- data['positive_ratings_scaled'] = scaler.fit_transform(
56
- data[['positive_ratings']].clip(lower=0)
57
- )
58
  else:
59
  data['positive_ratings_scaled'] = 0
60
 
61
- # Compute similarity matrix
62
  if feature_vectors.shape[0] > 1:
63
  try:
64
  if len(data) > 5000:
@@ -88,7 +112,7 @@ else:
88
  game_similarity = np.zeros((0, 0))
89
  list_of_all_titles = []
90
 
91
- # Platform detection function without emojis
92
  def detect_platforms(platforms_str):
93
  platforms_str = str(platforms_str).lower()
94
  platforms = []
@@ -99,6 +123,8 @@ def detect_platforms(platforms_str):
99
  platforms.append("macOS")
100
  if 'linux' in platforms_str:
101
  platforms.append("Linux")
 
 
102
 
103
  return platforms if platforms else ["Unknown"]
104
 
@@ -141,7 +167,7 @@ def create_price_gauge(game_price, similar_games_prices):
141
 
142
  return fig
143
 
144
- # Function to get game recommendations
145
  def recommend_games(user_game_name_input):
146
  if not user_game_name_input or not list_of_all_titles:
147
  return "Please enter a game name and ensure the dataset is loaded.", [], None
@@ -149,17 +175,32 @@ def recommend_games(user_game_name_input):
149
  # Normalize input for better matching
150
  user_input_cleaned = user_game_name_input.strip().lower()
151
 
152
- # First try exact match
153
  exact_matches = [title for title in list_of_all_titles if title.lower() == user_input_cleaned]
154
 
155
  if exact_matches:
156
  closest_match = exact_matches[0]
157
  else:
158
- # Try fuzzy matching if no exact match
159
- find_close_match = difflib.get_close_matches(user_game_name_input, list_of_all_titles, n=1, cutoff=0.6)
160
- if not find_close_match:
161
- return f"No match found for '{user_game_name_input}'. Please try another game name.", [], None
162
- closest_match = find_close_match[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  try:
165
  index_of_the_game = data.loc[data['name'] == closest_match].index[0]
@@ -170,12 +211,34 @@ def recommend_games(user_game_name_input):
170
 
171
  similarity_scores = list(enumerate(game_similarity[index_of_the_game]))
172
 
173
- # Sort by similarity and then by ratings as a tiebreaker
174
- sorted_similar_games = sorted(
175
- similarity_scores,
176
- key=lambda x: (x[1], data.iloc[x[0]]['positive_ratings_scaled']),
177
- reverse=True
178
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  recommendations = []
181
  game_list = []
@@ -203,10 +266,25 @@ def recommend_games(user_game_name_input):
203
  # Get prices for similar games (for gauge visualization)
204
  similar_games_prices = []
205
 
 
 
 
 
 
 
 
 
206
  # Process recommendations
207
- for i, (index, score) in enumerate(sorted_similar_games[1:6]): # Get top 5 recommendations
208
- if score < 0.2: # Minimum threshold for quality
209
  continue
 
 
 
 
 
 
 
210
 
211
  game_name = data.iloc[index]['name']
212
 
@@ -225,10 +303,10 @@ def recommend_games(user_game_name_input):
225
  genres_display = ", ".join([g for g in genres if g])
226
 
227
  # Calculate match percentage
228
- match_percentage = int(score * 100)
229
 
230
  # Format recommendation with clean styling
231
- position = i + 1
232
  recommendation = (
233
  f"### {position}. {game_name}\n" +
234
  f"**Match:** {match_percentage}%\n" +
@@ -239,8 +317,9 @@ def recommend_games(user_game_name_input):
239
 
240
  recommendations.append(recommendation)
241
  game_list.append(game_name)
 
242
 
243
- if len(recommendations) >= 7: # searched game + divider + 5 recommendations
244
  break
245
 
246
  # Create price gauge visualization
 
32
  else:
33
  data[feature] = data[feature].fillna(0) # Numeric columns
34
 
35
+ # Add derived features for better recommendations
36
+ if 'positive_ratings' in data.columns and 'negative_ratings' in data.columns:
37
+ data['rating_ratio'] = data['positive_ratings'] / (data['positive_ratings'] + data['negative_ratings'] + 1)
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
+
44
+ # Add name with higher weight for better keyword matching
45
+ if 'name' in data.columns:
46
+ data['combined_features'] += data['name'].astype(str) + ' ' + data['name'].astype(str) + ' '
47
+
48
+ # Add genres with higher weight (repeat to increase importance)
49
+ if 'genres' in data.columns:
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:
62
+ # Use more n-grams and increased max_features for better semantic understanding
63
+ vectorizer = TfidfVectorizer(
64
+ stop_words='english',
65
+ ngram_range=(1, 3), # Capture phrases up to 3 words
66
+ max_features=10000, # Increase features for more nuanced relationships
67
+ min_df=2, # Ignore very rare terms
68
+ max_df=0.9 # Ignore very common terms
69
+ )
70
  feature_vectors = vectorizer.fit_transform(data['combined_features'])
71
  print(f"Vectorization complete. Shape: {feature_vectors.shape}")
72
  except Exception as e:
73
  print(f"Vectorization error: {e}")
74
  feature_vectors = np.zeros((len(data), 1))
75
 
76
+ # Normalize ratings with sigmoid-like scaling for better differentiation
77
  if 'positive_ratings' in data.columns and len(data) > 0:
78
+ # Log transform to handle skewed distribution of ratings
79
+ data['log_ratings'] = np.log1p(data['positive_ratings'])
80
  scaler = MinMaxScaler()
81
+ data['positive_ratings_scaled'] = scaler.fit_transform(data[['log_ratings']])
 
 
82
  else:
83
  data['positive_ratings_scaled'] = 0
84
 
85
+ # Compute similarity matrix with optimizations
86
  if feature_vectors.shape[0] > 1:
87
  try:
88
  if len(data) > 5000:
 
112
  game_similarity = np.zeros((0, 0))
113
  list_of_all_titles = []
114
 
115
+ # Improved platform detection function
116
  def detect_platforms(platforms_str):
117
  platforms_str = str(platforms_str).lower()
118
  platforms = []
 
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
 
 
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
 
175
  # Normalize input for better matching
176
  user_input_cleaned = user_game_name_input.strip().lower()
177
 
178
+ # First try exact match (case insensitive)
179
  exact_matches = [title for title in list_of_all_titles if title.lower() == user_input_cleaned]
180
 
181
  if exact_matches:
182
  closest_match = exact_matches[0]
183
  else:
184
+ # Try partial match before fuzzy matching
185
+ partial_matches = [title for title in list_of_all_titles if user_input_cleaned in title.lower()]
186
+
187
+ if partial_matches:
188
+ # Sort by length to prefer shorter (more exact) matches
189
+ closest_match = sorted(partial_matches, key=len)[0]
190
+ else:
191
+ # Try fuzzy matching with improved parameters
192
+ find_close_match = difflib.get_close_matches(
193
+ user_game_name_input,
194
+ list_of_all_titles,
195
+ n=5, # Get more candidates
196
+ cutoff=0.5 # Lower threshold for more possibilities
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]
204
 
205
  try:
206
  index_of_the_game = data.loc[data['name'] == closest_match].index[0]
 
211
 
212
  similarity_scores = list(enumerate(game_similarity[index_of_the_game]))
213
 
214
+ # Enhanced ranking with hybrid scoring
215
+ game_rankings = []
216
+ for idx, sim_score in similarity_scores:
217
+ if idx == index_of_the_game: # Skip the game itself
218
+ continue
219
+
220
+ # Get additional factors for hybrid scoring
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))
239
+
240
+ # Sort by the hybrid score
241
+ sorted_similar_games = sorted(game_rankings, key=lambda x: x[1], reverse=True)
242
 
243
  recommendations = []
244
  game_list = []
 
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)
288
 
289
  game_name = data.iloc[index]['name']
290
 
 
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
 
308
  # Format recommendation with clean styling
309
+ position = recommended_count + 1
310
  recommendation = (
311
  f"### {position}. {game_name}\n" +
312
  f"**Match:** {match_percentage}%\n" +
 
317
 
318
  recommendations.append(recommendation)
319
  game_list.append(game_name)
320
+ recommended_count += 1
321
 
322
+ if recommended_count >= 5: # Stop after 5 recommendations
323
  break
324
 
325
  # Create price gauge visualization