File size: 7,313 Bytes
1158955
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# -*- coding: utf-8 -*-
"""HS_Text_REC_Games_Gradio_Blocks.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/19yJ8RC70IDljwSmPlqtOzWz192gwLAHF
"""

import pandas as pd
import numpy as np
from fuzzywuzzy import fuzz
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import gradio as gr

df = pd.read_csv("Metacritic_Reviews_Only.csv",  error_bad_lines=False, encoding='utf-8')

#Remove title from review
def remove_title(row):
  game_title = row['Game Title']
  body_text = row['Reviews']
  new_doc = body_text.replace(game_title, "")
  return new_doc

df['Reviews'] = df.apply(remove_title, axis=1)
#drop redundant column
df = df.drop(['Unnamed: 0'], axis=1)

df.dropna(inplace=True) #Drop Null Reviews

# Instantiate the vectorizer object to the vectorizer variable
#Minimum word count 2 to be included, words that appear in over 70% of docs should not be included
vectorizer = TfidfVectorizer(min_df=2, max_df=0.7)

# Fit and transform the plot column
vectorized_data = vectorizer.fit_transform(df['Reviews'])

# Create Dataframe from TF-IDFarray
tfidf_df = pd.DataFrame(vectorized_data.toarray(), columns=vectorizer.get_feature_names())

# Assign the game titles to the index
tfidf_df.index = df['Game Title']

# Find the cosine similarity measures between all game and assign the results to cosine_similarity_array.
cosine_similarity_array = cosine_similarity(tfidf_df)

# Create a DataFrame from the cosine_similarity_array with tfidf_df.index as its rows and columns.
cosine_similarity_df = pd.DataFrame(cosine_similarity_array, index=tfidf_df.index, columns=tfidf_df.index)

# Find the values for the game Batman: Arkham City
cosine_similarity_series = cosine_similarity_df.loc['Batman: Arkham City']

# Sort these values highest to lowest
ordered_similarities = cosine_similarity_series.sort_values(ascending=False)

# Print the results
print(ordered_similarities)

# create a function to find the closest title
def matching_score(a,b):
  #fuzz.ratio(a,b) calculates the Levenshtein Distance between a and b, and returns the score for the distance
   return fuzz.ratio(a,b)
   # exactly the same, the score becomes 100

#Convert index to title_year
def get_title_from_index(index):
   return df[df.index == index]['Game Title'].values[0]

# A function to return the most similar title to the words a user type
# Without this, the recommender only works when a user enters the exact title which the data has.
def find_closest_title(title):
  #matching_score(a,b) > a is the current row, b is the title we're trying to match
   leven_scores = list(enumerate(df['Game Title'].apply(matching_score, b=title))) #[(0, 30), (1,95), (2, 19)~~] A tuple of distances per index
   sorted_leven_scores = sorted(leven_scores, key=lambda x: x[1], reverse=True) #Sorts list of tuples by distance [(1, 95), (3, 49), (0, 30)~~]
   closest_title = get_title_from_index(sorted_leven_scores[0][0])
   distance_score = sorted_leven_scores[0][1]
   return closest_title, distance_score
   # Bejeweled Twist, 100

def find_closest_titles(title):
   leven_scores = list(enumerate(df['Game Title'].apply(matching_score, b=title))) #[(0, 30), (1,95), (2, 19)~~] A tuple of distances per index
   sorted_leven_scores = sorted(leven_scores, key=lambda x: x[1], reverse=True) #Sorts list of tuples by distance [(1, 95), (3, 49), (0, 30)~~]
   closest_titles = [get_title_from_index(sorted_leven_scores[i][0]) for i in range(5)]
   distance_scores = [sorted_leven_scores[i][1] for i in range(5)]
   return closest_titles, distance_scores
   # Bejeweled Twist, 100

def recommend_games_v1(game1, game2, game3, max_results):
  #Counter for Ranking
  number = 1
  print('Recommended because you played {}, {} and {}:\n'.format(game1, game2, game3))

  list_of_games_enjoyed = [game1, game2, game3]
  games_enjoyed_df = tfidf_df.reindex(list_of_games_enjoyed)
  user_prof = games_enjoyed_df.mean()
  
  tfidf_subset_df = tfidf_df.drop([game1, game2, game3], axis=0)
  similarity_array = cosine_similarity(user_prof.values.reshape(1, -1), tfidf_subset_df)
  similarity_df = pd.DataFrame(similarity_array.T, index=tfidf_subset_df.index, columns=["similarity_score"])

  # Sort the values from high to low by the values in the similarity_score
  sorted_similarity_df = similarity_df.sort_values(by="similarity_score", ascending=False)

  number = 0
  rank = 1
  rank_range = []
  name_list = []
  score_list = []
  for n in sorted_similarity_df.index:
    if rank <= max_results:
      rank_range.append(rank)
      name_list.append(n)
      score_list.append(str(round(sorted_similarity_df.iloc[number]['similarity_score']*100,2)) + "% ") #format score as a percentage
      number+=1
      rank +=1
  #Turn lists into a dictionary
  data = {'Rank': rank_range, 'Game Title': name_list, '% Match': score_list}
  rec_table = pd.DataFrame.from_dict(data) #Convert dictionary into dataframe
  rec_table.set_index('Rank', inplace=True) #Make Rank column the index
  return rec_table

demo = gr.Blocks()

with demo:
  gr.Markdown(
          """
      # Game Recommendations
      Input 3 games you enjoyed playing and use the dropdown to confirm your selections. Hopefully they are registered in the database. Once all 3 have been chosen, please generate your recommendations.
      """
      )
  
  options = ['Dragonball', 'Batman', 'Tekken']
  def Dropdown_list(x):
    new_options =  [*options, x + " Remastered", x + ": The Remake", x + ": Game of the Year Edition", x + " Steelbook Edition"]
    return gr.Dropdown.update(choices=new_options)

  with gr.Column(visible=True):
    first_entry = gr.Textbox(label="Game Title 1")
    first_dropdown = gr.Dropdown(choices=[], label="Closest Matches")
    update_first = gr.Button("Match Closest Title 1")

  with gr.Column(visible=True):
    second_entry = gr.Textbox(label="Game Title 2")
    second_dropdown = gr.Dropdown(label="Closest Matches")
    update_second = gr.Button("Match Closest Title 2")

  with gr.Column(visible=True):
    third_entry = gr.Textbox(label="Game Title 3")
    third_dropdown = gr.Dropdown(label="Closest Matches")
    update_third = gr.Button("Match Closest Title 3")
  
  with gr.Row():
    slider = gr.Slider(1, 20, step=1)

  with gr.Row():
    generate = gr.Button("Generate")
    results = gr.Dataframe(label="Top Results")

  def filter_matches(entry):
    top_matches = find_closest_titles(entry)
    top_matches = list(top_matches[0])
    return gr.Dropdown.update(choices=top_matches) #, gr.update(visible=True)

  def new_match(text):
    top_match = find_closest_title(text)
    return text

  first_entry.change(new_match, inputs=first_entry, outputs=first_dropdown)
  update_first.click(filter_matches, inputs=first_dropdown, outputs=first_dropdown)
  second_entry.change(new_match, inputs=second_entry, outputs=second_dropdown)
  update_second.click(filter_matches, inputs=second_dropdown, outputs=second_dropdown)
  third_entry.change(new_match, inputs=third_entry, outputs=third_dropdown)
  update_third.click(filter_matches, inputs=third_dropdown, outputs=third_dropdown)

  
  generate.click(recommend_games_v1, inputs=[first_dropdown, second_dropdown, third_dropdown, slider], outputs=results)
demo.launch()