In [1]:
Pràctiques de Nous Usos de la Informàtica
ENTREGA: El dia límit per a l'entrega d'aquesta pràctica és el dia 18 d'Octubre a les 23.55h
Format de l'entrega L'entrega s'efecturà mitjançant el campus virtual. S'ha de penjar un arxiu per grup. El nom del fitxer ha de seguir el següent patro: NUI_1_PrimeralletranomCognomMembre1_PrimeralletranomCognomMembre2.iypnb
Exemple:
Membre 1: Maria del Carme Vilà
Membre 2: Francesc Castell
Nom de l'arxiu: NUI_1_MVila_FCastell.ipynb
In [13]:
import pandas as pd
unames = ['user_id', 'gender', 'age', 'occupation', 'zip']
#users = pd.read_table('I:\\p1\\users.dat', sep='::', header=None, names=unames)
users = pd.read_table('users.dat', sep='::', header=None, names=unames)
rnames = ['user_id', 'movie_id', 'rating', 'timestamp']
#ratings = pd.read_table('I:\\p1\\ratings.dat', sep='::', header=None, names=rnames)
ratings = pd.read_table('ratings.dat', sep='::', header=None, names=rnames)
mnames = ['movie_id', 'title', 'genres']
#movies = pd.read_table('I:\\p1\\movies.dat', sep='::', header=None, names=mnames)
movies = pd.read_table('movies.dat', sep='::', header=None, names=mnames)
In [2]:
print users[:10]
In [3]:
users[:10]
Out[3]:
In [4]:
ratings[:10]
Out[4]:
In [5]:
ratings
Out[5]:
In [6]:
ratings.sort_index(by='movie_id')[:8]
Out[6]:
In [7]:
movies[:5]
Out[7]:
In [8]:
ratings[0:5]
Out[8]:
In [14]:
data = pd.merge(pd.merge(ratings, users), movies)
#print data[2000:2200][['user_id','title','rating']]
In [15]:
print data.ix[1]
In [12]:
mean_ratings = data.pivot_table('rating', rows='title',cols='gender', aggfunc='mean')
mean_ratings[:10]
Out[12]:
In [13]:
ratings_by_title = data.groupby('title').size()
active_titles = ratings_by_title.index[ratings_by_title >= 250]
In [14]:
mean_ratings = mean_ratings.ix[active_titles]
mean_ratings[:10]
Out[14]:
In [15]:
top_female_ratings = mean_ratings.sort_index(by='F', ascending=False)
top_female_ratings[:10]
Out[15]:
In [16]:
mean_ratings['diff'] = mean_ratings['M'] - mean_ratings['F']
In [17]:
sorted_by_diff = mean_ratings.sort_index(by='diff')
sorted_by_diff[:15]
Out[17]:
In [18]:
sorted_by_diff[::-1][:15]
Out[18]:
In [19]:
# Standard deviation of rating grouped by title
rating_std_by_title = data.groupby('title')['rating'].std()
# Filter down to active_titles
rating_std_by_title = rating_std_by_title.ix[active_titles]
rating_std_by_title.order(ascending=False)[:10]
Out[19]:
EXERCICI 1: Calcula la puntuació mitjana de cada usuari. Quina és la pel·lícula més ben puntuada?
In [20]:
# Dos possibles formes:
# mean_rating_by_user ha de ser un dataframe que conté el nom del usuari i la mitja de les seves puntiacions
mean_rating_by_user = data.groupby('user_id')['rating'].mean()
print mean_rating_by_user[:5]
print '\n',data.pivot_table(values='rating',rows='user_id',aggfunc='mean')[:5]
# critic_higher_mean ha de ser el registre de l'usuari amb la mitja més elevada
critic_higher_mean = mean_rating_by_user[mean_rating_by_user.argmax()+1]
print '\n',critic_higher_mean
# Pel·lícula millor puntuada:
mean_rating_by_movie = data.groupby('title')['rating'].mean()
movie_higher_mean = mean_rating_by_movie.index[mean_rating_by_movie.argmax()]
print movie_higher_mean
EXERCICI 2: Defineix una funció anomenada top_movie que donat un usuari ens retorni quina és la pel·lícula millor puntuada.
def top_movie(user)
In [21]:
def top_movie(dataFrame,usr):
#Obtenim les pel·lícules valorades del usuari 'usr'.
data_usr = dataFrame[ dataFrame['user_id'] == usr]
return data_usr.ix[data_usr.index[data_usr['rating'].argmax()]]['title']
print top_movie(data,1)
Exercici 3:
Construeix dues funcions, distEuclid(x,y) i coefPearson(x,y), que implementin la distància Euclidiana i el coeficient de correlació de Pearson entre dos vectors. Escriu les funcions que calculin la semblança entre dos usuaris segons aquesta estructura:
def SimEuclid (DataFrame, User1, User2)
Calcular els vectors representatius de cada usuari, C1 i C2, amb les puntuacions dels ítems comuns que han puntuat el dos usuaris.
Si no hi ha puntuacions en comú, retornar 0.
Retornar 1/(1+distEuclid(C1, C2))
def SimPearson (DataFrame, User1, User2)
Calcular els vectors representatius de cada usuari, C1 i C2, amb les puntuacions dels ítems comuns que han puntuat el dos usuaris.
Si no hi ha puntuacions en comú, retornar 0.>
Retornar coefPearson(C1,C2)
Utilizeu el panda per a realització d'aquest exercici.
In [16]:
import math
import time
import numpy as np
import pandas as pd
# Returns the euclidean distance of to vectors
def distEuclid(x, y):
if len(x)!=len(y):
return "Error: vectors size does not match."
return np.sqrt(sum((x[i]-y[i])**2 for i in range(len(x))))
# Returns the euclidean pearson of to vectors
def coefPearson(x, y):
'''Calcula el coeficient de correlacio de Pearson donats els vectors x e y.'''
if len(x)!=len(y):
return "Error: vectors size does not match."
mx = np.mean(x)
my = np.mean(y)
num = den_x = den_y = 0
for i in range(len(x)): #obtenim el sumatori per el numerador i denomidaor
num += (x[i]-mx)*(y[i]-my)
den_x += (x[i]-mx)**2
den_y += (y[i]-my)**2
if den_x == 0 or den_y == 0: #evitam dividir per 0 (nan)
return 0
else:
return num/(np.sqrt(den_x)*np.sqrt(den_y))
# Returns a distance-based similarity score for person1 and person2 based on euclidean distance
def SimEuclid(DataFrame,User1,User2):
#Obtenim el dataFrames amb les pelicules i votació de cada usuari
data_usr1 = DataFrame[ DataFrame['user_id'] == User1][['movie_id','rating']]
data_usr2 = DataFrame[ DataFrame['user_id'] == User2][['movie_id','rating']]
#Finalment obtenim únicament les votacions de les pel·lícules evaluades per els dos usuaris
ints = pd.merge(data_usr1, data_usr2, how='inner',on='movie_id', suffixes=('_usr1', '_usr2'))
return 1/(1+distEuclid(ints['rating_usr1'], ints['rating_usr2']))
# Returns a distance-based similarity score for person1 and person2 based on pearson distance
def SimPearson(DataFrame,User1,User2):
#Obtenim el dataFrames amb les pelicules i votació de cada usuari
data_usr1 = DataFrame[ DataFrame['user_id'] == User1][['movie_id','rating']]
data_usr2 = DataFrame[ DataFrame['user_id'] == User2][['movie_id','rating']]
#Finalment obtenim únicament les votacions de les pel·lícules evaluades per els dos usuaris
ints = pd.merge(data_usr1, data_usr2, how='inner',on='movie_id', suffixes=('_usr1', '_usr2'))
return coefPearson(ints['rating_usr1'], ints['rating_usr2'])
# ============= PROVES ===================
#print "distEulid([1,2,8],[1,2,8]) --> " + str(distEuclid([1,2,8],[1,2,8]))
#print "\n---- Proves Pearson ----"
#print coefPearson([1,2,3],[3,2,1]) #---> -1
#print coefPearson([1,2,3],[1,2,3]) #---> 1
#print coefPearson([1,2,3],[2,3,4]) #---> 1
#print coefPearson([1,1,1,1],[1,2,3,4]) #---> 0
In [23]:
# Execute functions
print SimEuclid(data,1,2)
print SimPearson(data,1,2)
Exercici 4:
Feu dues funcions, getNBestEuclid(DataFrame, user,n) i getNBestPearson(DataFrame, user,n), que retornin els n usuaris més semblants segons aquestes dues mesures de similitud.
In [24]:
import time
# return the N most similar users to given used based on euclidean distance
def getNBestEuclid(DataFrame,user,n):
# Per tal de fer-ho més eficient reduim el DataFrame a unicament les pel·licules votades per 'user'
data2 = pd.merge(DataFrame,DataFrame[DataFrame['user_id']==user][['movie_id','title']],how='inner',on='movie_id')[['user_id','movie_id','rating']]
# Obtenim una llista dels usuaris excepte el propi a comparar
users = set(data2[data2['user_id'] != user]['user_id'])
# Calculem la dist. Euclid. entre usuaris (i,user) per tots els users necessaris i els ordenem.
rank = [(SimEuclid(data2,user,i),i) for i in users]
rank = sorted(rank, key=lambda pearson_rtg: pearson_rtg[0], reverse=True)
return [ rank[i] for i in range(n) ]
# return the N most similar users to given used based on pearson distance
def getNBestPearson(DataFrame,user,n):
# Per tal de fer-ho més eficient reduim el DataFrame a unicament les pel·licules votades per 'user'
data2 = pd.merge(DataFrame,DataFrame[DataFrame['user_id']==user][['movie_id','title']],how='inner',on='movie_id')[['user_id','movie_id','rating']]
# Obtenim una llista dels usuaris excepte el propi a comparar
users = set(data2[data2['user_id'] != user]['user_id'])
# Calculem el coef entre usuaris (i,user) per tots els users.
rank = [(SimPearson(data2,user,i),i) for i in users]
rank = sorted(rank, key=lambda pearson_rtg: pearson_rtg[0], reverse=True)
return [ rank[i] for i in range(n) ]
In [25]:
# ======= EXECUTE FUNCTIONS ========
t0=time.clock()
print getNBestEuclid(data,1,5)
t1=time.clock()
print "Time ",(t1-t0),'\n'
t0=time.clock()
print getNBestPearson(data,1,5)
t1=time.clock()
print "Time ",(t1-t0)
Exercici 5:
Desenvolupa un sistema de recomanació col·laboratiu basat en usuaris. La funció principal, getRecommendationsUser, ha de tenir com a entrada una taula de puntuacions, un "user_id", el tipus de mesura de semblança (Euclidiana o Pearson) que volem usar i el nombre n màxim de recomanacions que volem. Com a sortida ha de donar la llista de les n millors pel·lícules que li podriem recomanar segons la seva semblança amb altres usuaris.
Nota: S'ha d'evitar comparar "user_id" a ell mateix.
In [34]:
# Gets recommendations for a person by using a weighted average
# of every other user's rankings
def getRecommendationsUser(DataFrame,person,n,similarity=SimPearson):
#1) Obtenim un DataFrame de user_id - similituts, mitjançant la creació d'un diccionari:
users = list(set(DataFrame[DataFrame['user_id']!=person]['user_id']))
sim_d = {'similar':[similarity(DataFrame,person,i) for i in users], 'user_id':users }
sim_df = pd.DataFrame(sim_d)
#print sim_df[:100]
#2) Trobem les pel·lícules no vistes per 'person'
vistes = set(DataFrame[DataFrame['user_id']==person]['movie_id'])
totes = set(DataFrame['movie_id'])
no_vistes = list(totes.difference(vistes))
#print "==================== VISTES ==================\n",list(vistes)
#print "================== NO VISTES =================\n",no_vistes[:50]
#3) Obtenim totes les valoracions per cada una de les pel. no_vistes per tal de fer intersecció amb el dataFrame de similituts (sim_df).
# A partir d'aquest, podem calcular el valor de recomanació i guardar-ho junt al seu 'movie_id' a un array 'resu' (posterioriment ordenat
# per treure les N millors).
resu = [] #resu contindra tuples '(valoracio, movie_id)'
for i in no_vistes:
mov_df = DataFrame[DataFrame['movie_id']==i][['user_id','rating']]
# Eliminam les similituts dels usuaris que no ens interesen (a la matriu serien els que tenen '?')
m_df = pd.merge(mov_df,sim_df, on='user_id', how='inner')
#print '\n======== Movies MERGE_DataFrame =======\n',m_df[:10]
# Comprovam si el denominador és 0 --> Evitar NaN
den = sum(m_df['similar'])
if den != 0:
resu = resu + [(sum(m_df['rating'] * m_df['similar'])/den, i )]
#print "\n\nEl màxim:", max([i[0] for i in resu])
resu = sorted(resu, key=lambda rtg:rtg[0], reverse=True)[:n]
#print resu
return [ list(set(DataFrame[DataFrame['movie_id']==i[1]]['title']))[0] for i in resu ]
In [36]:
t1 = time.clock()
print getRecommendationsUser(data, 1, 10, SimEuclid)
t2 = time.clock()
print 'Time: ',t2-t1
# EUCLIDES OUTPUT getRecommendationsUser(data, 1, 10, SimEuclid):
#['Schlafes Bruder (Brother of Sleep) (1995)', 'Follow the Bitch (1998)', 'Ulysses (Ulisse) (1954)', 'Smashing Time (1967)', 'Baby, The (1973)', 'Song of Freedom (1936)', 'One Little Indian (1973)', 'Lured (1947)', 'Bittersweet Motel (2000)', 'Gate of Heavenly Peace, The (1995)']
#Time: 127.41
# PEARSON OUTPUT getRecommendationsUser(data, 1, 10, SimPearson):
#['Wing Commander (1999)', 'Badlands (1973)', 'American Dream (1990)', 'First Kid (1996)', 'Come See the Paradise (1990)', 'Stardust Memories (1980)', 'Newsies (1992)', "Squanto: A Warrior's Tale (1994)", 'Hotel de Love (1996)', 'How I Won the War (1967)']
#Time: 129.28
Exercici 6:
Desenvolupa un sistema de recomanació col·laboratiu basat en ítems.
Primer, escriu una funció CalcSimItems(DataFrame), que construeixi i retorni una taula, itemsim, amb les semblances entre els ítems. Després escriu la funció principal, getRecommendationsItem(DataFrame, itemsim, user, n), ha de tenir com a entrada les puntuacions dels usuaris, la taula de semblança entre ítems, un "user_id" i el nombre n màxim de recomanacions que volem. Com a sortida ha de donar les n millors pel·lícules.
In [79]:
def CalcSimItems(DataFrame):
movs = list(set(DataFrame['movie_id']))
mida = len(movs)
#Creació de Matriu de similituts
matrix = [ [0] * mida for i in range(mida) ]
#Anem Omplim de la matriu de similituts entre Items considerant la simetria respecte la diagonal:
for i in range(mida):
for j in range(i,mida):
#obtenim els usuaris que han votat la peli 'i' i la 'j'
users1 = DataFrame[DataFrame['movie_id']==i+1][['user_id','rating']]
users2 = DataFrame[DataFrame['movie_id']==j+1][['user_id','rating']]
sim = 0
#Ens quedem amb les valoracions dels usuaris que han votat les dues pel·licules
ratings = pd.merge(users1,users2,on='user_id',how='inner')
#print ratings[:10]
if len(ratings['rating_x']) != 0:
#Calculam la similitut d'una pel·licula en funció de les valoracions dels usuaris que han votat les dues:
sim = 1/(1+distEuclid(ratings['rating_x'],ratings['rating_y']))
matrix[i][j] = sim
matrix[-i-1][-j-1]=sim
return matrix
def getRecommendationsItem(DataFrame, itemsim, usr, n):
#Primerament trobem les pel·lícules NO vistes per 'usr' (pel·lícules a recomanar)
vistes = set(DataFrame[DataFrame['user_id']==usr]['movie_id'])
totes = set(DataFrame['movie_id'])
no_vistes = list(totes.difference(vistes))
#print "==================== VISTES ==================\n",list(vistes)
#print "================== NO VISTES =================\n",no_vistes[:50]
# Obtenim les puntuacions de totes les pelicules vistes per l'usuari
mov_ratings = DataFrame[DataFrame['user_id']==usr][['movie_id','rating']]
resu = []
for i in no_vistes:
estim = 0
#print '\n===Movie i:',i,'=='
for j in vistes:
rating = list(mov_ratings[mov_ratings['movie_id']== j]['rating'])[0]
similarity = itemsim[i-1][j-1]
#print '>>rating:',rating,'\n>>similarity:',similarity
estim = estim + (rating*similarity )
#print '>>ESTIMACIO:',estim
resu = resu + [(estim, i )]
print "\n\nEl màxim:", max([i[0] for i in resu])
resu = sorted(resu, key=lambda rtg:rtg[0], reverse=True)[:n]
#print resu
return [ list(set(DataFrame[DataFrame['movie_id']==i[1]]['title']))[0] for i in resu ]
In [80]:
#Executions
data_aux = data[data['movie_id']<=50]
t0 = time.clock()
itemsim=CalcSimItems(data_aux)
t1 = time.clock()
print 'Matriu Similituts__Time: ',(t1-t0)
print getRecommendationsItem(data_aux, itemsim, 1, 10)
t2 = time.clock()
print 'Recomanacions__Time: ',(t2-t1)
Exercici 7: Creu una funcio, EvaluateRecommendationsUser(DataFrame,DataFrameTest,similarity=SimPearson), que donat un conjunt de dades d'entrenament i un conjunt de dades de test ens avalua la precisió dels sistema.
Per a cadascun dels elements del conjunt de test haurem de pronosticar el seu valor i comparar-lo amb el valor real que l'usuari li ha asignat.
Els mesura que utilizarem per avaluar el sistema és la següent:
$$accuracy = 1/N\sum_{i=0}^N abs(rating_i - rating_i^*) $$ on rating és la puntaució real que l'usuari va asginar a la pel·lícula i rating* és el valor pronoticat pel sistema de recomanacio desenvolupat.
In [41]:
# evaluate the recomender given a training and a testing set. Return the accuracy of the system
def EvaluateRecommendationsUser(DataFrame,DataFrameTest,similarity=SimPearson):
test_usr = list(DataFrameTest['user_id'])
resu = 0
for i in range(len(DataFrameTest)):
usr = DataFrameTest.ix[i]['user_id']
users = list(set(DataFrame[DataFrame['user_id']!=usr]['user_id']))
sim_d = {'similar':[similarity(DataFrame,usr,u) for u in users], 'user_id':users }
sim_df = pd.DataFrame(sim_d)
peli = DataFrameTest.ix[i]['movie_id']
mov_df = DataFrame[DataFrame['movie_id']==peli][['user_id','rating']]
# Eliminam les similituts dels usuaris que no ens interesen
m_df = pd.merge(mov_df,sim_df, on='user_id', how='inner')
# Comprovam si el denominador és 0
den = sum(m_df['similar'])
if den != 0:
estimacio = sum(m_df['rating'] * m_df['similar'])/den
real = DataFrameTest.ix[i]['rating']
print '>>> Real:',real,'\n>>> Estimació:',estimacio,'\n'
resu += abs(estimacio - real) #la valoracio generada
return resu/len(DataFrameTest)
In [44]:
t0=time.clock()
print EvaluateRecommendationsUser(data[5:],data[:5],SimEuclid)
t1=time.clock()
print 'Time: ',(t1-t0)
In [79]:
In [88]:
len(data)
Out[88]: