Hi,
I was doing some calculations for MAP_AT_K off SAR example: https://github.com/Microsoft/Recommenders/blob/master/notebooks/00_quick_start/sar_movielens.ipynb
and noticed the difference between MAP and Precision@K: 0.105815 and 0.326617.
The Precision@K is re-creatable at 0.326617
new_df = pd.merge(top_k, test, how='left', on=['UserId','MovieId'])
from dplython import (DplyFrame, X, select, sift, head, arrange, mutate, group_by, summarize, DelayFunction)
new_df = DplyFrame(new_df)
where = DelayFunction(np.where)
new_df = new_df >> mutate(Relevant = where(X.Rating > 0, 1, 0))
dd2 = new_df >> group_by(X.UserId) >> summarize(mm = X.Relevant.sum()/10)
dd2['mm'].mean()
But MAP might open for a discussion:
new_df = pd.merge(top_k, test, how='left', on=['UserId','MovieId'])
from dplython import (DplyFrame, X, select, sift, head, arrange, mutate, group_by, summarize, DelayFunction)
new_df = DplyFrame(new_df)
where = DelayFunction(np.where)
new_df = new_df >> mutate(Relevant = where(X.Rating > 0, 1, 0))
new_df = new_df >> arrange(X.UserId, -X.prediction)
new_df['ForCount'] = 1
new_df['cumsum'] = new_df.groupby('UserId')['Relevant'].transform(pd.Series.cumsum)
new_df['cumsum2'] = new_df.groupby('UserId')['ForCount'].transform(pd.Series.cumsum)
new_df['prec'] = new_df['cumsum'] / new_df['cumsum2']
new_df['prec'] = new_df['prec'] * new_df['Relevant']
dd2 = new_df >> group_by(X.UserId) >> summarize(ss = X.prec.sum())
dd2['prec2'] = dd2['ss'] / 10
dd2['prec2'].mean()
The above lines had MAP calculated as 0.2271
Instead of dividing the precision rates by 10, if we divide them by total Movies rated by user in test set like below:
new_df = pd.merge(top_k, test, how='left', on=['UserId','MovieId'])
from dplython import (DplyFrame, X, select, sift, head, arrange, mutate, group_by, summarize, DelayFunction)
new_df = DplyFrame(new_df)
where = DelayFunction(np.where)
new_df = new_df >> mutate(Relevant = where(X.Rating > 0, 1, 0))
new_df = new_df >> arrange(X.UserId, -X.prediction)
new_df['ForCount'] = 1
new_df['cumsum'] = new_df.groupby('UserId')['Relevant'].transform(pd.Series.cumsum)
new_df['cumsum2'] = new_df.groupby('UserId')['ForCount'].transform(pd.Series.cumsum)
new_df['prec'] = new_df['cumsum'] / new_df['cumsum2']
new_df['prec'] = new_df['prec'] * new_df['Relevant']
dd2 = new_df >> group_by(X.UserId) >> summarize(ss = X.prec.sum())
dd3 = test >> group_by(X.UserId) >> mutate(nn = 1) >> summarize(m = X.nn.sum())
dd4 = pd.merge(dd2, dd3, how='left', on = ['UserId'])
dd4['prec2'] = dd4['ss'] / dd4['m']
dd4['prec2'].mean()
Then the MAP rate is the same as given in the example: 0.1058
Question is why do we divide by total rated Movies in test set only? For UserId = 1, this from test set is 69 but in total he/she rated 272 movies. Why do we divide by 69 and not 272?
Reference: I couldn't get to your advised reference https://people.cs.umass.edu/~jpjiang/cs646/03_eval_basics.pdf so tried a different source here:
http://sdsawtelle.github.io/blog/output/mean-average-precision-MAP-for-recommender-systems.html
which seems to agree with one from Stanfor Uni at:
http://web.stanford.edu/class/cs276/handouts/EvaluationNew-handout-6-per.pdf
I may miss out some important details too.
Thanks,
CPU
I have pasted the codes using Dplython module.
thanks @seancaodo, we will take a look
@seancaodo Thanks for the ask and discussion. Greatly appreciate it. 馃憤
dd2['prec2'] = dd2['ss'] / 10 where your calculation is based on a division by k, whilst our implementation is based on a division by the number of ground truth items in the testing data. So, when I changed line 588 in this from df_sum_all["map"] = df_sum_all.apply(lambda x: (x.precision / x.actual), axis=1) to df_sum_all["map"] = df_sum_all.apply(lambda x: (x.precision / k), axis=1) it gave me 0.2212.@yueguoguo Hello, I just figured it out too. We were posting in parallel 馃憤. Thanks very much for the fast response.
I think we can close the threat. A minor question left for me to think is why we divide by number of ground truth for each user as number split by test, train sets. If we scale up or down the size of these two sets, the MAP rates will differ I suppose. It's minor I think as we mainly use these for comparisons between algorithms anyway.
Welldone too, thank you guys very much for sharing this great piece of work.
Most helpful comment
@seancaodo Thanks for the ask and discussion. Greatly appreciate it. 馃憤
dd2['prec2'] = dd2['ss'] / 10where your calculation is based on a division byk, whilst our implementation is based on a division by the number of ground truth items in the testing data. So, when I changed line 588 in this fromdf_sum_all["map"] = df_sum_all.apply(lambda x: (x.precision / x.actual), axis=1)todf_sum_all["map"] = df_sum_all.apply(lambda x: (x.precision / k), axis=1)it gave me 0.2212.