\n",
"Netflix is all about connecting people to the movies they love. To help customers find those movies, they developed world-class movie recommendation system: CinematchSM. Its job is to predict whether someone will enjoy a movie based on how much they liked or disliked other movies. Netflix use those predictions to make personal movie recommendations based on each customer’s unique tastes. And while Cinematch is doing pretty well, it can always be made better.\n",
"
\n",
"
Now there are a lot of interesting alternative approaches to how Cinematch works that netflix haven’t tried. Some are described in the literature, some aren’t. We’re curious whether any of these can beat Cinematch by making better predictions. Because, frankly, if there is a much better approach it could make a big difference to our customers and our business.
\n",
"Netflix provided a lot of anonymous rating data, and a prediction accuracy bar that is 10% better than what Cinematch can do on the same training data set. (Accuracy is a measurement of how closely predicted ratings of movies match subsequent actual ratings.) \n",
"
1.4 Real world/Business Objectives and constraints
"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "e0mcbCxNGHIT"
},
"source": [
"Objectives:\n",
"1. Predict the rating that a user would give to a movie that he ahs not yet rated.\n",
"2. Minimize the difference between predicted and actual rating (RMSE and MAPE)\n",
" \n",
"\n",
"Constraints:\n",
"1. Some form of interpretability."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "y0xC5pTnGHIU"
},
"source": [
"
Get the data from : https://www.kaggle.com/netflix-inc/netflix-prize-data/data
\n",
"
Data files : \n",
"
\n",
"
combined_data_1.txt
\n",
"
combined_data_2.txt
\n",
"
combined_data_3.txt
\n",
"
combined_data_4.txt
\n",
"
movie_titles.csv
\n",
"
\n",
"
\n",
"The first line of each file [combined_data_1.txt, combined_data_2.txt, combined_data_3.txt, combined_data_4.txt] contains the movie id followed by a colon. Each subsequent line in the file corresponds to a rating from a customer and its date in the following format:\n",
"\n",
"CustomerID,Rating,Date\n",
"\n",
"MovieIDs range from 1 to 17770 sequentially.\n",
"CustomerIDs range from 1 to 2649429, with gaps. There are 480189 users.\n",
"Ratings are on a five star (integral) scale from 1 to 5.\n",
"Dates have the format YYYY-MM-DD.\n",
"
\n",
"For a given movie and user we need to predict the rating would be given by him/her to the movie. \n",
"The given problem is a Recommendation problem \n",
"It can also seen as a Regression problem \n",
"
3.1.4 Basic Statistics (#Ratings, #Users, and #Movies)
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "a4bc67cd-6a7a-41c0-d75c-adc4bb80be98",
"id": "vQVNTCjzGHJJ",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 119
}
},
"source": [
"print(\"Total data \")\n",
"print(\"-\"*50)\n",
"print(\"\\nTotal no of ratings :\",df.shape[0])\n",
"print(\"Total No of Users :\", len(np.unique(df.user)))\n",
"print(\"Total No of movies :\", len(np.unique(df.movie)))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Total data \n",
"--------------------------------------------------\n",
"\n",
"Total no of ratings : 100480507\n",
"Total No of Users : 480189\n",
"Total No of movies : 17770\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "xEnrVuKDGHJM"
},
"source": [
"
3.2 Spliting data into Train and Test(80:20)
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "hBRN_GB2GHJN",
"colab": {}
},
"source": [
"if not os.path.isfile('/content/drive/My Drive/Netflix_recommender/data_folder/train.csv'):\n",
" # create the dataframe and store it in the disk for offline purposes..\n",
" df.iloc[:int(df.shape[0]*0.80)].to_csv(\"train.csv\", index=False)\n",
"\n",
"if not os.path.isfile('/content/drive/My Drive/Netflix_recommender/data_folder/test.csv'):\n",
" # create the dataframe and store it in the disk for offline purposes..\n",
" df.iloc[int(df.shape[0]*0.80):].to_csv(\"test.csv\", index=False)\n",
"\n",
"train_df = pd.read_csv(\"/content/drive/My Drive/Netflix_recommender/data_folder/train.csv\", parse_dates=['date'])\n",
"test_df = pd.read_csv(\"/content/drive/My Drive/Netflix_recommender/data_folder/test.csv\")"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "FjI2Bwr5GHJV"
},
"source": [
"
3.2.1 Basic Statistics in Train data (#Ratings, #Users, and #Movies)
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "d248bf9c-e7d2-48dd-a538-d12c53f32091",
"id": "kUEUofOvGHJV",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 119
}
},
"source": [
"# movies = train_df.movie.value_counts()\n",
"# users = train_df.user.value_counts()\n",
"print(\"Training data \")\n",
"print(\"-\"*50)\n",
"print(\"\\nTotal no of ratings :\",train_df.shape[0])\n",
"print(\"Total No of Users :\", len(np.unique(train_df.user)))\n",
"print(\"Total No of movies :\", len(np.unique(train_df.movie)))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Training data \n",
"--------------------------------------------------\n",
"\n",
"Total no of ratings : 80384405\n",
"Total No of Users : 405041\n",
"Total No of movies : 17424\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "kNzpnzXDGHJX"
},
"source": [
"
3.2.2 Basic Statistics in Test data (#Ratings, #Users, and #Movies)
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "d8b4e924-7c3b-4138-c254-2f33768b7024",
"id": "KYo7TX2JGHJX",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 119
}
},
"source": [
"print(\"Test data \")\n",
"print(\"-\"*50)\n",
"print(\"\\nTotal no of ratings :\",test_df.shape[0])\n",
"print(\"Total No of Users :\", len(np.unique(test_df.user)))\n",
"print(\"Total No of movies :\", len(np.unique(test_df.movie)))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Test data \n",
"--------------------------------------------------\n",
"\n",
"Total no of ratings : 20096102\n",
"Total No of Users : 349312\n",
"Total No of movies : 17757\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "N0tlZ5jIGHJZ"
},
"source": [
"
3.3.6.1 Creating sparse matrix from train data frame
"
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "cf2444aa-18be-4e29-95a3-693b1c22e027",
"id": "SfWUsCnLGHNL",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 68
}
},
"source": [
"\n",
"start = datetime.now()\n",
"if os.path.isfile('/content/drive/My Drive/Netflix_recommender/data_folder/train_sparse_matrix.npz'):\n",
" print(\"It is present in your pwd, getting it from disk....\")\n",
" # just get it from the disk instead of computing it\n",
" train_sparse_matrix = sparse.load_npz('/content/drive/My Drive/Netflix_recommender/data_folder/train_sparse_matrix.npz')\n",
" print(\"DONE..\")\n",
"else: \n",
" print(\"We are creating sparse_matrix from the dataframe..\")\n",
" # create sparse_matrix and store it for after usage.\n",
" # csr_matrix(data_values, (row_index, col_index), shape_of_matrix)\n",
" # It should be in such a way that, MATRIX[row, col] = data\n",
" train_sparse_matrix = sparse.csr_matrix((train_df.rating.values, (train_df.user.values,\n",
" train_df.movie.values)),)\n",
" \n",
" print('Done. It\\'s shape is : (user, movie) : ',train_sparse_matrix.shape)\n",
" print('Saving it into disk for furthur usage..')\n",
" # save it into disk\n",
" sparse.save_npz(\"train_sparse_matrix.npz\", train_sparse_matrix)\n",
" print('Done..\\n')\n",
"\n",
"print(datetime.now() - start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"It is present in your pwd, getting it from disk....\n",
"DONE..\n",
"0:00:04.955848\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "XFbTLnY3GHNO"
},
"source": [
"
3.3.6.2 Creating sparse matrix from test data frame
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "80f3aabe-a508-45d4-a5db-93a50e22dadb",
"id": "5VMK9kIMGHNR",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 68
}
},
"source": [
"start = datetime.now()\n",
"if os.path.isfile('/content/drive/My Drive/Netflix_recommender/data_folder/test_sparse_matrix.npz'):\n",
" print(\"It is present in your pwd, getting it from disk....\")\n",
" # just get it from the disk instead of computing it\n",
" test_sparse_matrix = sparse.load_npz('/content/drive/My Drive/Netflix_recommender/data_folder/test_sparse_matrix.npz')\n",
" print(\"DONE..\")\n",
"else: \n",
" print(\"We are creating sparse_matrix from the dataframe..\")\n",
" # create sparse_matrix and store it for after usage.\n",
" # csr_matrix(data_values, (row_index, col_index), shape_of_matrix)\n",
" # It should be in such a way that, MATRIX[row, col] = data\n",
" test_sparse_matrix = sparse.csr_matrix((test_df.rating.values, (test_df.user.values,\n",
" test_df.movie.values)))\n",
" \n",
" print('Done. It\\'s shape is : (user, movie) : ',test_sparse_matrix.shape)\n",
" print('Saving it into disk for furthur usage..')\n",
" # save it into disk\n",
" sparse.save_npz(\"test_sparse_matrix.npz\", test_sparse_matrix)\n",
" print('Done..\\n')\n",
" \n",
"print(datetime.now() - start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"It is present in your pwd, getting it from disk....\n",
"DONE..\n",
"0:00:02.357853\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "t3xebt2ZGHNT"
},
"source": [
"
3.3.7 Finding Global average of all movie ratings, Average rating per user, and Average rating per movie
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "wHOby1-ZGHNe",
"colab": {}
},
"source": [
"# get the user averages in dictionary (key: user_id/movie_id, value: avg rating)\n",
"\n",
"def get_average_ratings(sparse_matrix, of_users):\n",
" \n",
" # average ratings of user/axes\n",
" ax = 1 if of_users else 0 # 1 - User axes,0 - Movie axes\n",
"\n",
" # \".A1\" is for converting Column_Matrix to 1-D numpy array \n",
" sum_of_ratings = sparse_matrix.sum(axis=ax).A1\n",
" # Boolean matrix of ratings ( whether a user rated that movie or not)\n",
" is_rated = sparse_matrix!=0\n",
" # no of ratings that each user OR movie..\n",
" no_of_ratings = is_rated.sum(axis=ax).A1\n",
" \n",
" # max_user and max_movie ids in sparse matrix \n",
" u,m = sparse_matrix.shape\n",
" # creae a dictonary of users and their average ratigns..\n",
" average_ratings = { i : sum_of_ratings[i]/no_of_ratings[i]\n",
" for i in range(u if of_users else m) \n",
" if no_of_ratings[i] !=0}\n",
"\n",
" # return that dictionary of average ratings\n",
" return average_ratings"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "lKKAvkjWGHNh"
},
"source": [
"
3.3.7.1 finding global average of all movie ratings
3.3.7.4 PDF's & CDF's of Avg.Ratings of Users & Movies (In Train Data)
"
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"colab_type": "code",
"outputId": "c20f89fb-1639-4d71-d5d6-1bdc331b060e",
"id": "Brg-5Lf8GHN5",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 311
}
},
"source": [
"start = datetime.now()\n",
"# draw pdfs for average rating per user and average\n",
"fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=plt.figaspect(.5))\n",
"fig.suptitle('Avg Ratings per User and per Movie', fontsize=15)\n",
"\n",
"ax1.set_title('Users-Avg-Ratings')\n",
"# get the list of average user ratings from the averages dictionary..\n",
"user_averages = [rat for rat in train_averages['user'].values()]\n",
"sns.distplot(user_averages, ax=ax1, hist=False, \n",
" kde_kws=dict(cumulative=True), label='Cdf')\n",
"sns.distplot(user_averages, ax=ax1, hist=False,label='Pdf')\n",
"\n",
"ax2.set_title('Movies-Avg-Rating')\n",
"# get the list of movie_average_ratings from the dictionary..\n",
"movie_averages = [rat for rat in train_averages['movie'].values()]\n",
"sns.distplot(movie_averages, ax=ax2, hist=False, \n",
" kde_kws=dict(cumulative=True), label='Cdf')\n",
"sns.distplot(movie_averages, ax=ax2, hist=False, label='Pdf')\n",
"\n",
"plt.show()\n",
"print(datetime.now() - start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\n0zRN8zD/DzK4xFoJKDILAAAAAElFTkSuQmCC\n",
"text/plain": [
"
"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"colab_type": "text",
"id": "bq_qbJCkGHOO"
},
"source": [
"1. Calculating User User Similarity_Matrix is __not very easy__(_unless you have huge Computing Power and lots of time_) because of number of. usersbeing lare.\n",
"\n",
" * You can try if you want to. Your system could crash or the program stops with **Memory Error**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "Eefgxw-ZGHOP"
},
"source": [
"
3.4.1.1 Trying with all dimensions (17k dimensions per user)
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "ChJI9dHJGHOQ",
"colab": {}
},
"source": [
"from sklearn.metrics.pairwise import cosine_similarity\n",
"\n",
"\n",
"def compute_user_similarity(sparse_matrix, compute_for_few=False, top = 100, verbose=False, verb_for_n_rows = 20,\n",
" draw_time_taken=True):\n",
" no_of_users, _ = sparse_matrix.shape\n",
" # get the indices of non zero rows(users) from our sparse matrix\n",
" row_ind, col_ind = sparse_matrix.nonzero()\n",
" row_ind = sorted(set(row_ind)) # we don't have to\n",
" time_taken = list() # time taken for finding similar users for an user..\n",
" \n",
" # we create rows, cols, and data lists.., which can be used to create sparse matrices\n",
" rows, cols, data = list(), list(), list()\n",
" if verbose: print(\"Computing top\",top,\"similarities for each user..\")\n",
" \n",
" start = datetime.now()\n",
" temp = 0\n",
" \n",
" for row in row_ind[:top] if compute_for_few else row_ind:\n",
" temp = temp+1\n",
" prev = datetime.now()\n",
" \n",
" # get the similarity row for this user with all other users\n",
" sim = cosine_similarity(sparse_matrix.getrow(row), sparse_matrix).ravel()\n",
" # We will get only the top ''top'' most similar users and ignore rest of them..\n",
" top_sim_ind = sim.argsort()[-top:]\n",
" top_sim_val = sim[top_sim_ind]\n",
" \n",
" # add them to our rows, cols and data\n",
" rows.extend([row]*top)\n",
" cols.extend(top_sim_ind)\n",
" data.extend(top_sim_val)\n",
" time_taken.append(datetime.now().timestamp() - prev.timestamp())\n",
" if verbose:\n",
" if temp%verb_for_n_rows == 0:\n",
" print(\"computing done for {} users [ time elapsed : {} ]\"\n",
" .format(temp, datetime.now()-start))\n",
" \n",
" \n",
" # lets create sparse matrix out of these and return it\n",
" if verbose: print('Creating Sparse matrix from the computed similarities')\n",
" #return rows, cols, data\n",
" \n",
" if draw_time_taken:\n",
" plt.plot(time_taken, label = 'time taken for each user')\n",
" plt.plot(np.cumsum(time_taken), label='Total time')\n",
" plt.legend(loc='best')\n",
" plt.xlabel('User')\n",
" plt.ylabel('Time (seconds)')\n",
" plt.show()\n",
" \n",
" return sparse.csr_matrix((data, (rows, cols)), shape=(no_of_users, no_of_users)), time_taken "
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"colab_type": "code",
"id": "RNLuKop0GHOT",
"colab": {}
},
"source": [
"start = datetime.now()\n",
"u_u_sim_sparse, _ = compute_user_similarity(train_sparse_matrix, compute_for_few=True, top = 100,\n",
" verbose=True)\n",
"print(\"-\"*100)\n",
"print(\"Time taken :\",datetime.now()-start)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "QsRzdbEUGHOU"
},
"source": [
"
3.4.1.2 Trying with reduced dimensions (Using TruncatedSVD for dimensionality reduction of user vector)
"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "WIDu_I28GHOV"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "6h5654MXGHOV"
},
"source": [
"* We have **405,041 users** in out training set and computing similarities between them..( **17K dimensional vector..**) is time consuming..\n",
"\n",
"\n",
"- From above plot, It took roughly __8.88 sec__ for computing simlilar users for __one user__\n",
" \n",
" \n",
"- We have __405,041 users__ with us in training set.\n",
"\n",
"\n",
"- ${ 405041 \\times 8.88 = 3596764.08 \\sec } = 59946.068 \\min = 999.101133333 \\text{ hours}\n",
"= 41.629213889 \\text{ days}...$\n",
"\n",
" - Even if we run on 4 cores parallelly (a typical system now a days), It will still take almost __10 and 1/2__ days.\n",
" \n",
" IDEA: Instead, we will try to reduce the dimentsions using SVD, so that __it might__ speed up the process..."
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "bf53b3a5-828f-457c-d354-4d52b6559767",
"id": "ntGRZKe8GHOW",
"colab": {}
},
"source": [
"from datetime import datetime\n",
"from sklearn.decomposition import TruncatedSVD\n",
"\n",
"start = datetime.now()\n",
"\n",
"# initilaize the algorithm with some parameters..\n",
"# All of them are default except n_components. n_itr is for Randomized SVD solver.\n",
"netflix_svd = TruncatedSVD(n_components=500, algorithm='randomized', random_state=15)\n",
"trunc_svd = netflix_svd.fit_transform(train_sparse_matrix)\n",
"\n",
"print(datetime.now()-start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"0:29:07.069783\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "T5F0rmM1GHOX"
},
"source": [
"Here,\n",
"\n",
"\n",
"- $\\sum \\longleftarrow$ (netflix\\_svd.**singular\\_values\\_** )\n",
"\n",
"\n",
"- $\\bigvee^T \\longleftarrow$ (netflix\\_svd.**components_**)\n",
"\n",
"\n",
"- $\\bigcup$ is not returned. instead **Projection_of_X** onto the new vectorspace is returned. \n",
"\n",
"\n",
"- It uses **randomized svd** internally, which returns **All 3 of them saperately**. Use that instead.. "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "xw5xEQ6MGHOX",
"colab": {}
},
"source": [
"expl_var = np.cumsum(netflix_svd.explained_variance_ratio_)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"colab_type": "code",
"outputId": "237de9aa-848a-47b8-ffcf-e7ebacce3905",
"id": "KUps11F8GHOZ",
"colab": {}
},
"source": [
"fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=plt.figaspect(.5))\n",
"\n",
"ax1.set_ylabel(\"Variance Explained\", fontsize=15)\n",
"ax1.set_xlabel(\"# Latent Facors\", fontsize=15)\n",
"ax1.plot(expl_var)\n",
"# annote some (latentfactors, expl_var) to make it clear\n",
"ind = [1, 2,4,8,20, 60, 100, 200, 300, 400, 500]\n",
"ax1.scatter(x = [i-1 for i in ind], y = expl_var[[i-1 for i in ind]], c='#ff3300')\n",
"for i in ind:\n",
" ax1.annotate(s =\"({}, {})\".format(i, np.round(expl_var[i-1], 2)), xy=(i-1, expl_var[i-1]),\n",
" xytext = ( i+20, expl_var[i-1] - 0.01), fontweight='bold')\n",
"\n",
"change_in_expl_var = [expl_var[i+1] - expl_var[i] for i in range(len(expl_var)-1)]\n",
"ax2.plot(change_in_expl_var)\n",
"\n",
"\n",
"\n",
"ax2.set_ylabel(\"Gain in Var_Expl with One Additional LF\", fontsize=10)\n",
"ax2.yaxis.set_label_position(\"right\")\n",
"ax2.set_xlabel(\"# Latent Facors\", fontsize=20)\n",
"\n",
"plt.show()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "display_data",
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '');\n",
" var titletext = $(\n",
" '');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('');\n",
"\n",
" var fmt_picker = $('');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('');\n",
" var button = $('');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
}
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "2f18f097-5405-42ff-9a09-20b8ea1e559e",
"id": "jB0mn3L9GHOb",
"colab": {}
},
"source": [
"for i in ind:\n",
" print(\"({}, {})\".format(i, np.round(expl_var[i-1], 2)))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"(1, 0.23)\n",
"(2, 0.26)\n",
"(4, 0.3)\n",
"(8, 0.34)\n",
"(20, 0.38)\n",
"(60, 0.44)\n",
"(100, 0.47)\n",
"(200, 0.53)\n",
"(300, 0.57)\n",
"(400, 0.61)\n",
"(500, 0.64)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "LQ7ZH-6qGHOe"
},
"source": [
"\n",
"> I think 500 dimensions is good enough \n",
"\n",
"---------\n",
"\n",
"- By just taking __(20 to 30)__ latent factors, explained variance that we could get is __20 %__. \n",
"\n",
"- To take it to __60%__, we have to take __almost 400 latent factors__. It is not fare.\n",
"\n",
"\n",
"\n",
"- It basically is the __gain of variance explained__, if we ___add one additional latent factor to it.___\n",
"\n",
"\n",
"- By adding one by one latent factore too it, the ___gain in expained variance__ with that addition is decreasing. (Obviously, because they are sorted that way).\n",
"- ___LHS Graph___:\n",
" - __x__ --- ( No of latent factos ),\n",
" - __y__ --- ( The variance explained by taking x latent factors)\n",
"\n",
"\n",
"\n",
"- __More decrease in the line (RHS graph) __:\n",
" - We are getting more expained variance than before.\n",
"- __Less decrease in that line (RHS graph)__ :\n",
" - We are not getting benifitted from adding latent factor furthur. This is what is shown in the plots.\n",
"\n",
"\n",
"- ___RHS Graph___:\n",
" - __x__ --- ( No of latent factors ),\n",
" - __y__ --- ( Gain n Expl_Var by taking one additional latent factor) "
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "5f7de336-1593-41ee-8d5e-522ed4b6555d",
"id": "NQfBtVUrGHOe",
"colab": {}
},
"source": [
"# Let's project our Original U_M matrix into into 500 Dimensional space...\n",
"start = datetime.now()\n",
"trunc_matrix = train_sparse_matrix.dot(netflix_svd.components_.T)\n",
"print(datetime.now()- start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"0:00:45.670265\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "a5194761-c6da-4f25-a31e-89cc55033f45",
"id": "RFFxNjL0GHOh",
"colab": {}
},
"source": [
"type(trunc_matrix), trunc_matrix.shape"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"(numpy.ndarray, (2649430, 500))"
]
},
"metadata": {
"tags": []
},
"execution_count": 53
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "3R5wziskGHOj"
},
"source": [
"* Let's convert this to actual sparse matrix and store it for future purposes"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "v0DQr_zLGHOj",
"colab": {}
},
"source": [
"if not os.path.isfile('/content/drive/My Drive/Netflix_recommender/data_folder/trunc_sparse_matrix.npz'):\n",
" # create that sparse sparse matrix\n",
" trunc_sparse_matrix = sparse.csr_matrix(trunc_matrix)\n",
" # Save this truncated sparse matrix for later usage..\n",
" sparse.save_npz('trunc_sparse_matrix', trunc_sparse_matrix)\n",
"else:\n",
" trunc_sparse_matrix = sparse.load_npz('/content/drive/My Drive/Netflix_recommender/data_folder/trunc_sparse_matrix.npz')"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "0bb095f5-eb23-417e-febf-1038260dd6a4",
"id": "8Qq5w0tYGHOk",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"trunc_sparse_matrix.shape"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"(2649430, 500)"
]
},
"metadata": {
"tags": []
},
"execution_count": 25
}
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "b3a387d8-3fb5-4a3d-e0de-9a5f02a7926f",
"id": "7ymbMKtKGHOm",
"colab": {}
},
"source": [
"start = datetime.now()\n",
"trunc_u_u_sim_matrix, _ = compute_user_similarity(trunc_sparse_matrix, compute_for_few=True, top=50, verbose=True, \n",
" verb_for_n_rows=10)\n",
"print(\"-\"*50)\n",
"print(\"time:\",datetime.now()-start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Computing top 50 similarities for each user..\n",
"computing done for 10 users [ time elapsed : 0:02:09.746324 ]\n",
"computing done for 20 users [ time elapsed : 0:04:16.017768 ]\n",
"computing done for 30 users [ time elapsed : 0:06:20.861163 ]\n",
"computing done for 40 users [ time elapsed : 0:08:24.933316 ]\n",
"computing done for 50 users [ time elapsed : 0:10:28.861485 ]\n",
"Creating Sparse matrix from the computed similarities\n"
],
"name": "stdout"
},
{
"output_type": "display_data",
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '');\n",
" var titletext = $(\n",
" '');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('');\n",
"\n",
" var fmt_picker = $('');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('');\n",
" var button = $('');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "stream",
"text": [
"--------------------------------------------------\n",
"time: 0:10:52.658092\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "FHEcJOfrGHOn"
},
"source": [
"**: This is taking more time for each user than Original one.**"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "eMLuVBD5GHOn"
},
"source": [
"- from above plot, It took almost __12.18__ for computing simlilar users for __one user__\n",
" \n",
" \n",
"- We have __405041 users__ with us in training set.\n",
"\n",
"\n",
"- ${ 405041 \\times 12.18 ==== 4933399.38 \\sec } ==== 82223.323 \\min ==== 1370.388716667 \\text{ hours}\n",
"==== 57.099529861 \\text{ days}...$\n",
"\n",
" - Even we run on 4 cores parallelly (a typical system now a days), It will still take almost __(14 - 15) __ days.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "_v7kZ4IFGHOo"
},
"source": [
"- __Why did this happen...??__\n",
"\n",
"\n",
" - Just think about it. It's not that difficult.\n",
"\n",
"---------------------------------_( sparse & dense..................get it ?? )_-----------------------------------"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "ASXSsaGmGHOo"
},
"source": [
"__Is there any other way to compute user user similarity..??__"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "7ehSsq3gGHOq"
},
"source": [
"-An alternative is to compute similar users for a particular user, whenenver required (**ie., Run time**)\n",
" - We maintain a binary Vector for users, which tells us whether we already computed or not..\n",
" - ***If not*** : \n",
" - Compute top (let's just say, 1000) most similar users for this given user, and add this to our datastructure, so that we can just access it(similar users) without recomputing it again.\n",
" - \n",
" - ***If It is already Computed***:\n",
" - Just get it directly from our datastructure, which has that information.\n",
" - In production time, We might have to recompute similarities, if it is computed a long time ago. Because user preferences changes over time. If we could maintain some kind of Timer, which when expires, we have to update it ( recompute it ). \n",
" - \n",
" - ***Which datastructure to use:***\n",
" - It is purely implementation dependant. \n",
" - One simple method is to maintain a **Dictionary Of Dictionaries**.\n",
" - \n",
" - **key :** _userid_ \n",
" - __value__: _Again a dictionary_\n",
" - __key__ : _Similar User_\n",
" - __value__: _Similarity Value_"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"colab_type": "text",
"id": "oYouSYH0GHOr"
},
"source": [
"
3.4.2 Computing Movie-Movie Similarity matrix
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "7_72kq4YGHOs",
"colab": {}
},
"source": [
"start = datetime.now()\n",
"if not os.path.isfile('/content/drive/My Drive/Netflix_recommender/data_folder/m_m_sim_sparse.npz'):\n",
" print(\"It seems you don't have that file. Computing movie_movie similarity...\")\n",
" start = datetime.now()\n",
" m_m_sim_sparse = cosine_similarity(X=train_sparse_matrix.T, dense_output=False)\n",
" print(\"Done..\")\n",
" # store this sparse matrix in disk before using it. For future purposes.\n",
" print(\"Saving it to disk without the need of re-computing it again.. \")\n",
" sparse.save_npz(\"m_m_sim_sparse.npz\", m_m_sim_sparse)\n",
" print(\"Done..\")\n",
"else:\n",
" print(\"It is there, We will get it.\")\n",
" m_m_sim_sparse = sparse.load_npz(\"/content/drive/My Drive/Netflix_recommender/data_folder/m_m_sim_sparse.npz\")\n",
" print(\"Done ...\")\n",
"\n",
"print(\"It's a \",m_m_sim_sparse.shape,\" dimensional matrix\")\n",
"\n",
"print(datetime.now() - start)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "cd481df2-f6ea-4f01-d4d1-b500ab74f838",
"id": "TXIu4Xx1GHOv",
"colab": {}
},
"source": [
"m_m_sim_sparse.shape"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"(17771, 17771)"
]
},
"metadata": {
"tags": []
},
"execution_count": 59
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "FwunajS_GHOy"
},
"source": [
"- Even though we have similarity measure of each movie, with all other movies, We generally don't care much about least similar movies.\n",
"\n",
"\n",
"- Most of the times, only top_xxx similar items matters. It may be 10 or 100.\n",
"\n",
"\n",
"- We take only those top similar movie ratings and store them in a saperate dictionary."
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "am27Q4cNGHOy",
"colab": {}
},
"source": [
"movie_ids = np.unique(m_m_sim_sparse.nonzero()[1])"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "7e9d1c69-1ceb-4c26-e81f-83291f44aaa5",
"id": "9b8fpLlxGHO0",
"colab": {}
},
"source": [
"start = datetime.now()\n",
"similar_movies = dict()\n",
"for movie in movie_ids:\n",
" # get the top similar movies and store them in the dictionary\n",
" sim_movies = m_m_sim_sparse[movie].toarray().ravel().argsort()[::-1][1:]\n",
" similar_movies[movie] = sim_movies[:100]\n",
"print(datetime.now() - start)\n",
"\n",
"# just testing similar movies for movie_15\n",
"similar_movies[15]"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"0:00:33.411700\n"
],
"name": "stdout"
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"array([ 8279, 8013, 16528, 5927, 13105, 12049, 4424, 10193, 17590,\n",
" 4549, 3755, 590, 14059, 15144, 15054, 9584, 9071, 6349,\n",
" 16402, 3973, 1720, 5370, 16309, 9376, 6116, 4706, 2818,\n",
" 778, 15331, 1416, 12979, 17139, 17710, 5452, 2534, 164,\n",
" 15188, 8323, 2450, 16331, 9566, 15301, 13213, 14308, 15984,\n",
" 10597, 6426, 5500, 7068, 7328, 5720, 9802, 376, 13013,\n",
" 8003, 10199, 3338, 15390, 9688, 16455, 11730, 4513, 598,\n",
" 12762, 2187, 509, 5865, 9166, 17115, 16334, 1942, 7282,\n",
" 17584, 4376, 8988, 8873, 5921, 2716, 14679, 11947, 11981,\n",
" 4649, 565, 12954, 10788, 10220, 10963, 9427, 1690, 5107,\n",
" 7859, 5969, 1510, 2429, 847, 7845, 6410, 13931, 9840,\n",
" 3706])"
]
},
"metadata": {
"tags": []
},
"execution_count": 62
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "X4Uf1_uZGHO2"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "XXdHrIN7GHO3"
},
"source": [
"
3.4.3 Finding most similar movies using similarity matrix
"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "g2S87T-0GHO4"
},
"source": [
"__ Does Similarity really works as the way we expected...? __ \n",
"_Let's pick some random movie and check for its similar movies...._"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "2d737e9e-a444-4ec5-aba0-6ad6363aa24d",
"id": "VVQVMv5ZGHO4",
"colab": {}
},
"source": [
"# First Let's load the movie details into soe dataframe..\n",
"# movie details are in 'netflix/movie_titles.csv'\n",
"\n",
"movie_titles = pd.read_csv(\"data_folder/movie_titles.csv\", sep=',', header = None,\n",
" names=['movie_id', 'year_of_release', 'title'], verbose=True,\n",
" index_col = 'movie_id', encoding = \"ISO-8859-1\")\n",
"\n",
"movie_titles.head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Tokenization took: 4.50 ms\n",
"Type conversion took: 165.72 ms\n",
"Parser memory cleanup took: 0.01 ms\n"
],
"name": "stdout"
},
{
"output_type": "execute_result",
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
year_of_release
\n",
"
title
\n",
"
\n",
"
\n",
"
movie_id
\n",
"
\n",
"
\n",
"
\n",
" \n",
" \n",
"
\n",
"
1
\n",
"
2003.0
\n",
"
Dinosaur Planet
\n",
"
\n",
"
\n",
"
2
\n",
"
2004.0
\n",
"
Isle of Man TT 2004 Review
\n",
"
\n",
"
\n",
"
3
\n",
"
1997.0
\n",
"
Character
\n",
"
\n",
"
\n",
"
4
\n",
"
1994.0
\n",
"
Paula Abdul's Get Up & Dance
\n",
"
\n",
"
\n",
"
5
\n",
"
2004.0
\n",
"
The Rise and Fall of ECW
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" year_of_release title\n",
"movie_id \n",
"1 2003.0 Dinosaur Planet\n",
"2 2004.0 Isle of Man TT 2004 Review\n",
"3 1997.0 Character\n",
"4 1994.0 Paula Abdul's Get Up & Dance\n",
"5 2004.0 The Rise and Fall of ECW"
]
},
"metadata": {
"tags": []
},
"execution_count": 64
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "x_dc-9T1GHO6"
},
"source": [
"
Similar Movies for 'Vampire Journals'
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "1665280e-124d-4e73-f508-808853ebba2d",
"id": "hJNsE740GHO7",
"colab": {}
},
"source": [
"mv_id = 67\n",
"\n",
"print(\"\\nMovie ----->\",movie_titles.loc[mv_id].values[1])\n",
"\n",
"print(\"\\nIt has {} Ratings from users.\".format(train_sparse_matrix[:,mv_id].getnnz()))\n",
"\n",
"print(\"\\nWe have {} movies which are similarto this and we will get only top most..\".format(m_m_sim_sparse[:,mv_id].getnnz()))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"\n",
"Movie -----> Vampire Journals\n",
"\n",
"It has 270 Ratings from users.\n",
"\n",
"We have 17284 movies which are similarto this and we will get only top most..\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "GXMvP8xyGHO-",
"colab": {}
},
"source": [
"similarities = m_m_sim_sparse[mv_id].toarray().ravel()\n",
"\n",
"similar_indices = similarities.argsort()[::-1][1:]\n",
"\n",
"similarities[similar_indices]\n",
"\n",
"sim_indices = similarities.argsort()[::-1][1:] # It will sort and reverse the array and ignore its similarity (ie.,1)\n",
" # and return its indices(movie_ids)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "2e929724-0612-4d54-cab4-e8a4b6081b37",
"id": "igXbZvu4GHPA",
"colab": {}
},
"source": [
"plt.plot(similarities[sim_indices], label='All the ratings')\n",
"plt.plot(similarities[sim_indices[:100]], label='top 100 similar movies')\n",
"plt.title(\"Similar Movies of {}(movie_id)\".format(mv_id), fontsize=20)\n",
"plt.xlabel(\"Movies (Not Movie_Ids)\", fontsize=15)\n",
"plt.ylabel(\"Cosine Similarity\",fontsize=15)\n",
"plt.legend()\n",
"plt.show()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "display_data",
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '');\n",
" var titletext = $(\n",
" '');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('');\n",
"\n",
" var fmt_picker = $('');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('');\n",
" var button = $('');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "eODAeoKbGHPE"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "YhHN7FLwGHPF"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "4FxioJYTGHPF"
},
"source": [
"__Top 10 similar movies__"
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "eaebe922-cee7-4ef5-a41a-c7f6de5732d6",
"id": "JxYjiCF2GHPF",
"colab": {}
},
"source": [
"movie_titles.loc[sim_indices[:10]]"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
year_of_release
\n",
"
title
\n",
"
\n",
"
\n",
"
movie_id
\n",
"
\n",
"
\n",
"
\n",
" \n",
" \n",
"
\n",
"
323
\n",
"
1999.0
\n",
"
Modern Vampires
\n",
"
\n",
"
\n",
"
4044
\n",
"
1998.0
\n",
"
Subspecies 4: Bloodstorm
\n",
"
\n",
"
\n",
"
1688
\n",
"
1993.0
\n",
"
To Sleep With a Vampire
\n",
"
\n",
"
\n",
"
13962
\n",
"
2001.0
\n",
"
Dracula: The Dark Prince
\n",
"
\n",
"
\n",
"
12053
\n",
"
1993.0
\n",
"
Dracula Rising
\n",
"
\n",
"
\n",
"
16279
\n",
"
2002.0
\n",
"
Vampires: Los Muertos
\n",
"
\n",
"
\n",
"
4667
\n",
"
1996.0
\n",
"
Vampirella
\n",
"
\n",
"
\n",
"
1900
\n",
"
1997.0
\n",
"
Club Vampire
\n",
"
\n",
"
\n",
"
13873
\n",
"
2001.0
\n",
"
The Breed
\n",
"
\n",
"
\n",
"
15867
\n",
"
2003.0
\n",
"
Dracula II: Ascension
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" year_of_release title\n",
"movie_id \n",
"323 1999.0 Modern Vampires\n",
"4044 1998.0 Subspecies 4: Bloodstorm\n",
"1688 1993.0 To Sleep With a Vampire\n",
"13962 2001.0 Dracula: The Dark Prince\n",
"12053 1993.0 Dracula Rising\n",
"16279 2002.0 Vampires: Los Muertos\n",
"4667 1996.0 Vampirella\n",
"1900 1997.0 Club Vampire\n",
"13873 2001.0 The Breed\n",
"15867 2003.0 Dracula II: Ascension"
]
},
"metadata": {
"tags": []
},
"execution_count": 68
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "Jz4p_iYqGHPJ"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "hZXYanT2GHPJ"
},
"source": [
" > Similarly, we can ___find similar users___ and compare how similar they are. "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "Nx6WfTBfGHPK"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "ZfMOhLKQGHPN"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "4pFk8qDmGHPO"
},
"source": [
"
4. Machine Learning Models
"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "7oIEoF0WGHPQ"
},
"source": [
""
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "UGjGNEqdGHPQ",
"colab": {}
},
"source": [
"def get_sample_sparse_matrix(sparse_matrix, no_users, no_movies, path, verbose = True):\n",
" \"\"\"\n",
" It will get it from the ''path'' if it is present or It will create \n",
" and store the sampled sparse matrix in the path specified.\n",
" \"\"\"\n",
"\n",
" # get (row, col) and (rating) tuple from sparse_matrix...\n",
" row_ind, col_ind, ratings = sparse.find(sparse_matrix)\n",
" users = np.unique(row_ind)\n",
" movies = np.unique(col_ind)\n",
"\n",
" print(\"Original Matrix : (users, movies) -- ({} {})\".format(len(users), len(movies)))\n",
" print(\"Original Matrix : Ratings -- {}\\n\".format(len(ratings)))\n",
"\n",
" # It just to make sure to get same sample everytime we run this program..\n",
" # and pick without replacement....\n",
" np.random.seed(15)\n",
" sample_users = np.random.choice(users, no_users, replace=False)\n",
" sample_movies = np.random.choice(movies, no_movies, replace=False)\n",
" # get the boolean mask or these sampled_items in originl row/col_inds..\n",
" mask = np.logical_and( np.isin(row_ind, sample_users),\n",
" np.isin(col_ind, sample_movies) )\n",
" \n",
" sample_sparse_matrix = sparse.csr_matrix((ratings[mask], (row_ind[mask], col_ind[mask])),\n",
" shape=(max(sample_users)+1, max(sample_movies)+1))\n",
"\n",
" if verbose:\n",
" print(\"Sampled Matrix : (users, movies) -- ({} {})\".format(len(sample_users), len(sample_movies)))\n",
" print(\"Sampled Matrix : Ratings --\", format(ratings[mask].shape[0]))\n",
"\n",
" print('Saving it into disk for furthur usage..')\n",
" # save it into disk\n",
" sparse.save_npz(path, sample_sparse_matrix)\n",
" if verbose:\n",
" print('Done..\\n')\n",
" \n",
" return sample_sparse_matrix"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "xWynlzd4GHPT"
},
"source": [
"
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "6S69hvjAGHPx",
"colab": {}
},
"source": [
"# get users, movies and ratings from our samples train sparse matrix\n",
"sample_train_users, sample_train_movies, sample_train_ratings = sparse.find(sample_train_sparse_matrix)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "51abb095-433c-4404-be97-bbe8311b02f4",
"id": "eA3eLkgVGHP0",
"colab": {}
},
"source": [
"############################################################\n",
"# It took me almost 10 hours to prepare this train dataset.#\n",
"############################################################\n",
"start = datetime.now()\n",
"if os.path.isfile('sample/small/reg_train.csv'):\n",
" print(\"File already exists you don't have to prepare again...\" )\n",
"else:\n",
" print('preparing {} tuples for the dataset..\\n'.format(len(sample_train_ratings)))\n",
" with open('sample/small/reg_train.csv', mode='w') as reg_data_file:\n",
" count = 0\n",
" for (user, movie, rating) in zip(sample_train_users, sample_train_movies, sample_train_ratings):\n",
" st = datetime.now()\n",
" # print(user, movie) \n",
" #--------------------- Ratings of \"movie\" by similar users of \"user\" ---------------------\n",
" # compute the similar Users of the \"user\" \n",
" user_sim = cosine_similarity(sample_train_sparse_matrix[user], sample_train_sparse_matrix).ravel()\n",
" top_sim_users = user_sim.argsort()[::-1][1:] # we are ignoring 'The User' from its similar users.\n",
" # get the ratings of most similar users for this movie\n",
" top_ratings = sample_train_sparse_matrix[top_sim_users, movie].toarray().ravel()\n",
" # we will make it's length \"5\" by adding movie averages to .\n",
" top_sim_users_ratings = list(top_ratings[top_ratings != 0][:5])\n",
" top_sim_users_ratings.extend([sample_train_averages['movie'][movie]]*(5 - len(top_sim_users_ratings)))\n",
" # print(top_sim_users_ratings, end=\" \") \n",
"\n",
"\n",
" #--------------------- Ratings by \"user\" to similar movies of \"movie\" ---------------------\n",
" # compute the similar movies of the \"movie\" \n",
" movie_sim = cosine_similarity(sample_train_sparse_matrix[:,movie].T, sample_train_sparse_matrix.T).ravel()\n",
" top_sim_movies = movie_sim.argsort()[::-1][1:] # we are ignoring 'The User' from its similar users.\n",
" # get the ratings of most similar movie rated by this user..\n",
" top_ratings = sample_train_sparse_matrix[user, top_sim_movies].toarray().ravel()\n",
" # we will make it's length \"5\" by adding user averages to.\n",
" top_sim_movies_ratings = list(top_ratings[top_ratings != 0][:5])\n",
" top_sim_movies_ratings.extend([sample_train_averages['user'][user]]*(5-len(top_sim_movies_ratings))) \n",
" # print(top_sim_movies_ratings, end=\" : -- \")\n",
"\n",
" #-----------------prepare the row to be stores in a file-----------------#\n",
" row = list()\n",
" row.append(user)\n",
" row.append(movie)\n",
" # Now add the other features to this data...\n",
" row.append(sample_train_averages['global']) # first feature\n",
" # next 5 features are similar_users \"movie\" ratings\n",
" row.extend(top_sim_users_ratings)\n",
" # next 5 features are \"user\" ratings for similar_movies\n",
" row.extend(top_sim_movies_ratings)\n",
" # Avg_user rating\n",
" row.append(sample_train_averages['user'][user])\n",
" # Avg_movie rating\n",
" row.append(sample_train_averages['movie'][movie])\n",
"\n",
" # finalley, The actual Rating of this user-movie pair...\n",
" row.append(rating)\n",
" count = count + 1\n",
"\n",
" # add rows to the file opened..\n",
" reg_data_file.write(','.join(map(str, row)))\n",
" reg_data_file.write('\\n') \n",
" if (count)%10000 == 0:\n",
" # print(','.join(map(str, row)))\n",
" print(\"Done for {} rows----- {}\".format(count, datetime.now() - start))\n",
"\n",
"\n",
"print(datetime.now() - start)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"preparing 129286 tuples for the dataset..\n",
"\n",
"Done for 10000 rows----- 0:53:13.974716\n",
"Done for 20000 rows----- 1:47:58.228942\n",
"Done for 30000 rows----- 2:42:46.963119\n",
"Done for 40000 rows----- 3:36:44.807894\n",
"Done for 50000 rows----- 4:28:55.311500\n",
"Done for 60000 rows----- 5:24:18.493104\n",
"Done for 70000 rows----- 6:17:39.669922\n",
"Done for 80000 rows----- 7:11:23.970879\n",
"Done for 90000 rows----- 8:05:33.787770\n",
"Done for 100000 rows----- 9:00:25.463562\n",
"Done for 110000 rows----- 9:51:28.530010\n",
"Done for 120000 rows----- 10:42:05.382141\n",
"11:30:13.699183\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "6d4OEPwJGHP2"
},
"source": [
"__Reading from the file to make a Train_dataframe__"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "32649084-d35a-4184-ba1c-2faa04c8ab6d",
"id": "GgXpqzZ1GHP3",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 204
}
},
"source": [
"reg_train = pd.read_csv('/content/drive/My Drive/Netflix_recommender/reg_train_25.csv', names = ['user', 'movie', 'GAvg', 'sur1', 'sur2', 'sur3', 'sur4', 'sur5','smr1', 'smr2', 'smr3', 'smr4', 'smr5', 'UAvg', 'MAvg', 'rating'], header=None)\n",
"reg_train.head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
user
\n",
"
movie
\n",
"
GAvg
\n",
"
sur1
\n",
"
sur2
\n",
"
sur3
\n",
"
sur4
\n",
"
sur5
\n",
"
smr1
\n",
"
smr2
\n",
"
smr3
\n",
"
smr4
\n",
"
smr5
\n",
"
UAvg
\n",
"
MAvg
\n",
"
rating
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
174683
\n",
"
10
\n",
"
3.587581
\n",
"
5.0
\n",
"
5.0
\n",
"
3.0
\n",
"
4.0
\n",
"
4.0
\n",
"
3.0
\n",
"
5.0
\n",
"
4.0
\n",
"
3.0
\n",
"
2.0
\n",
"
3.882353
\n",
"
3.611111
\n",
"
5
\n",
"
\n",
"
\n",
"
1
\n",
"
233949
\n",
"
10
\n",
"
3.587581
\n",
"
4.0
\n",
"
4.0
\n",
"
5.0
\n",
"
1.0
\n",
"
3.0
\n",
"
2.0
\n",
"
3.0
\n",
"
2.0
\n",
"
3.0
\n",
"
3.0
\n",
"
2.692308
\n",
"
3.611111
\n",
"
3
\n",
"
\n",
"
\n",
"
2
\n",
"
555770
\n",
"
10
\n",
"
3.587581
\n",
"
4.0
\n",
"
5.0
\n",
"
4.0
\n",
"
4.0
\n",
"
5.0
\n",
"
4.0
\n",
"
2.0
\n",
"
5.0
\n",
"
4.0
\n",
"
4.0
\n",
"
3.795455
\n",
"
3.611111
\n",
"
4
\n",
"
\n",
"
\n",
"
3
\n",
"
767518
\n",
"
10
\n",
"
3.587581
\n",
"
2.0
\n",
"
5.0
\n",
"
4.0
\n",
"
4.0
\n",
"
3.0
\n",
"
5.0
\n",
"
5.0
\n",
"
4.0
\n",
"
4.0
\n",
"
3.0
\n",
"
3.884615
\n",
"
3.611111
\n",
"
5
\n",
"
\n",
"
\n",
"
4
\n",
"
894393
\n",
"
10
\n",
"
3.587581
\n",
"
3.0
\n",
"
5.0
\n",
"
4.0
\n",
"
4.0
\n",
"
3.0
\n",
"
4.0
\n",
"
4.0
\n",
"
4.0
\n",
"
4.0
\n",
"
4.0
\n",
"
4.000000
\n",
"
3.611111
\n",
"
4
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" user movie GAvg sur1 sur2 ... smr4 smr5 UAvg MAvg rating\n",
"0 174683 10 3.587581 5.0 5.0 ... 3.0 2.0 3.882353 3.611111 5\n",
"1 233949 10 3.587581 4.0 4.0 ... 3.0 3.0 2.692308 3.611111 3\n",
"2 555770 10 3.587581 4.0 5.0 ... 4.0 4.0 3.795455 3.611111 4\n",
"3 767518 10 3.587581 2.0 5.0 ... 4.0 3.0 3.884615 3.611111 5\n",
"4 894393 10 3.587581 3.0 5.0 ... 4.0 4.0 4.000000 3.611111 4\n",
"\n",
"[5 rows x 16 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 44
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "-HewozfoGHP6"
},
"source": [
"-----------------------\n",
"\n",
"- __GAvg__ : Average rating of all the ratings \n",
"\n",
"\n",
"- __Similar users rating of this movie__:\n",
" - sur1, sur2, sur3, sur4, sur5 ( top 5 similar users who rated that movie.. )\n",
" \n",
"\n",
"\n",
"- __Similar movies rated by this user__:\n",
" - smr1, smr2, smr3, smr4, smr5 ( top 5 similar movies rated by this movie.. )\n",
"\n",
"\n",
"- __UAvg__ : User's Average rating\n",
"\n",
"\n",
"- __MAvg__ : Average rating of this movie\n",
"\n",
"\n",
"- __rating__ : Rating of this movie by this user.\n",
"\n",
"-----------------------"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "dijPar5jGHP6"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "71zxlWvxGHP7"
},
"source": [
"
4.3.1.2 Featurizing test data
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "kOXRQ442GHP8",
"colab": {}
},
"source": [
"# get users, movies and ratings from the Sampled Test \n",
"sample_test_users, sample_test_movies, sample_test_ratings = sparse.find(sample_test_sparse_matrix)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "bcb3bc92-eaee-4d85-a547-56a6de5ae9fd",
"id": "wqg0S9JcGHP-",
"colab": {}
},
"source": [
"sample_train_averages['global']"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3.581679377504138"
]
},
"metadata": {
"tags": []
},
"execution_count": 21
}
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "767f43c5-ad21-43db-c636-4bc220000fa2",
"id": "jBuDLV9uGHQB",
"colab": {}
},
"source": [
"start = datetime.now()\n",
"\n",
"if os.path.isfile('sample/small/reg_test.csv'):\n",
" print(\"It is already created...\")\n",
"else:\n",
"\n",
" print('preparing {} tuples for the dataset..\\n'.format(len(sample_test_ratings)))\n",
" with open('sample/small/reg_test.csv', mode='w') as reg_data_file:\n",
" count = 0 \n",
" for (user, movie, rating) in zip(sample_test_users, sample_test_movies, sample_test_ratings):\n",
" st = datetime.now()\n",
"\n",
" #--------------------- Ratings of \"movie\" by similar users of \"user\" ---------------------\n",
" #print(user, movie)\n",
" try:\n",
" # compute the similar Users of the \"user\" \n",
" user_sim = cosine_similarity(sample_train_sparse_matrix[user], sample_train_sparse_matrix).ravel()\n",
" top_sim_users = user_sim.argsort()[::-1][1:] # we are ignoring 'The User' from its similar users.\n",
" # get the ratings of most similar users for this movie\n",
" top_ratings = sample_train_sparse_matrix[top_sim_users, movie].toarray().ravel()\n",
" # we will make it's length \"5\" by adding movie averages to .\n",
" top_sim_users_ratings = list(top_ratings[top_ratings != 0][:5])\n",
" top_sim_users_ratings.extend([sample_train_averages['movie'][movie]]*(5 - len(top_sim_users_ratings)))\n",
" # print(top_sim_users_ratings, end=\"--\")\n",
"\n",
" except (IndexError, KeyError):\n",
" # It is a new User or new Movie or there are no ratings for given user for top similar movies...\n",
" ########## Cold STart Problem ##########\n",
" top_sim_users_ratings.extend([sample_train_averages['global']]*(5 - len(top_sim_users_ratings)))\n",
" #print(top_sim_users_ratings)\n",
" except:\n",
" print(user, movie)\n",
" # we just want KeyErrors to be resolved. Not every Exception...\n",
" raise\n",
"\n",
"\n",
"\n",
" #--------------------- Ratings by \"user\" to similar movies of \"movie\" ---------------------\n",
" try:\n",
" # compute the similar movies of the \"movie\" \n",
" movie_sim = cosine_similarity(sample_train_sparse_matrix[:,movie].T, sample_train_sparse_matrix.T).ravel()\n",
" top_sim_movies = movie_sim.argsort()[::-1][1:] # we are ignoring 'The User' from its similar users.\n",
" # get the ratings of most similar movie rated by this user..\n",
" top_ratings = sample_train_sparse_matrix[user, top_sim_movies].toarray().ravel()\n",
" # we will make it's length \"5\" by adding user averages to.\n",
" top_sim_movies_ratings = list(top_ratings[top_ratings != 0][:5])\n",
" top_sim_movies_ratings.extend([sample_train_averages['user'][user]]*(5-len(top_sim_movies_ratings))) \n",
" #print(top_sim_movies_ratings)\n",
" except (IndexError, KeyError):\n",
" #print(top_sim_movies_ratings, end=\" : -- \")\n",
" top_sim_movies_ratings.extend([sample_train_averages['global']]*(5-len(top_sim_movies_ratings)))\n",
" #print(top_sim_movies_ratings)\n",
" except :\n",
" raise\n",
"\n",
" #-----------------prepare the row to be stores in a file-----------------#\n",
" row = list()\n",
" # add usser and movie name first\n",
" row.append(user)\n",
" row.append(movie)\n",
" row.append(sample_train_averages['global']) # first feature\n",
" #print(row)\n",
" # next 5 features are similar_users \"movie\" ratings\n",
" row.extend(top_sim_users_ratings)\n",
" #print(row)\n",
" # next 5 features are \"user\" ratings for similar_movies\n",
" row.extend(top_sim_movies_ratings)\n",
" #print(row)\n",
" # Avg_user rating\n",
" try:\n",
" row.append(sample_train_averages['user'][user])\n",
" except KeyError:\n",
" row.append(sample_train_averages['global'])\n",
" except:\n",
" raise\n",
" #print(row)\n",
" # Avg_movie rating\n",
" try:\n",
" row.append(sample_train_averages['movie'][movie])\n",
" except KeyError:\n",
" row.append(sample_train_averages['global'])\n",
" except:\n",
" raise\n",
" #print(row)\n",
" # finalley, The actual Rating of this user-movie pair...\n",
" row.append(rating)\n",
" #print(row)\n",
" count = count + 1\n",
"\n",
" # add rows to the file opened..\n",
" reg_data_file.write(','.join(map(str, row)))\n",
" #print(','.join(map(str, row)))\n",
" reg_data_file.write('\\n') \n",
" if (count)%1000 == 0:\n",
" #print(','.join(map(str, row)))\n",
" print(\"Done for {} rows----- {}\".format(count, datetime.now() - start))\n",
" print(\"\",datetime.now() - start) "
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"preparing 7333 tuples for the dataset..\n",
"\n",
"Done for 1000 rows----- 0:04:29.293783\n",
"Done for 2000 rows----- 0:08:57.208002\n",
"Done for 3000 rows----- 0:13:30.333223\n",
"Done for 4000 rows----- 0:18:04.050813\n",
"Done for 5000 rows----- 0:22:38.671673\n",
"Done for 6000 rows----- 0:27:09.697009\n",
"Done for 7000 rows----- 0:31:41.933568\n",
" 0:33:12.529731\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "l1OSGIpEGHQF"
},
"source": [
"__Reading from the file to make a test dataframe __"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "c7819189-2f17-428b-be85-e5af5b1a5ed4",
"id": "maMuye0CGHQF",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 173
}
},
"source": [
"reg_test_df = pd.read_csv('/content/drive/My Drive/Netflix_recommender/reg_test_25.csv', names = ['user', 'movie', 'GAvg', 'sur1', 'sur2', 'sur3', 'sur4', 'sur5',\n",
" 'smr1', 'smr2', 'smr3', 'smr4', 'smr5',\n",
" 'UAvg', 'MAvg', 'rating'], header=None)\n",
"reg_test_df.head(4)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
user
\n",
"
movie
\n",
"
GAvg
\n",
"
sur1
\n",
"
sur2
\n",
"
sur3
\n",
"
sur4
\n",
"
sur5
\n",
"
smr1
\n",
"
smr2
\n",
"
smr3
\n",
"
smr4
\n",
"
smr5
\n",
"
UAvg
\n",
"
MAvg
\n",
"
rating
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
808635
\n",
"
71
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
5
\n",
"
\n",
"
\n",
"
1
\n",
"
941866
\n",
"
71
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
4
\n",
"
\n",
"
\n",
"
2
\n",
"
1737912
\n",
"
71
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3
\n",
"
\n",
"
\n",
"
3
\n",
"
1849204
\n",
"
71
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
3.581679
\n",
"
4
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" user movie GAvg sur1 ... smr5 UAvg MAvg rating\n",
"0 808635 71 3.581679 3.581679 ... 3.581679 3.581679 3.581679 5\n",
"1 941866 71 3.581679 3.581679 ... 3.581679 3.581679 3.581679 4\n",
"2 1737912 71 3.581679 3.581679 ... 3.581679 3.581679 3.581679 3\n",
"3 1849204 71 3.581679 3.581679 ... 3.581679 3.581679 3.581679 4\n",
"\n",
"[4 rows x 16 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 48
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "-q9xwxPUGHQH"
},
"source": [
"-----------------------\n",
"\n",
"- __GAvg__ : Average rating of all the ratings \n",
"\n",
"\n",
"- __Similar users rating of this movie__:\n",
" - sur1, sur2, sur3, sur4, sur5 ( top 5 simiular users who rated that movie.. )\n",
" \n",
"\n",
"\n",
"- __Similar movies rated by this user__:\n",
" - smr1, smr2, smr3, smr4, smr5 ( top 5 simiular movies rated by this movie.. )\n",
"\n",
"\n",
"- __UAvg__ : User AVerage rating\n",
"\n",
"\n",
"- __MAvg__ : Average rating of this movie\n",
"\n",
"\n",
"- __rating__ : Rating of this movie by this user.\n",
"\n",
"-----------------------"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "cYhXG7-DGHQI"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "77EkmNItGHQJ"
},
"source": [
"
"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "v3vQZFeDGHQL"
},
"source": [
"- We can't give raw data (movie, user, rating) to train the model in Surprise library.\n",
"\n",
"\n",
"- They have a saperate format for TRAIN and TEST data, which will be useful for training the models like SVD, KNNBaseLineOnly....etc..,in Surprise.\n",
"\n",
"\n",
"- We can form the trainset from a file, or from a Pandas DataFrame. \n",
"http://surprise.readthedocs.io/en/stable/getting_started.html#load-dom-dataframe-py "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "c1FdSfO2GHQM",
"colab": {}
},
"source": [
"# It is to specify how to read the dataframe.\n",
"# for our dataframe, we don't have to specify anything extra..\n",
"reader = Reader(rating_scale=(1,5))\n",
"\n",
"# create the traindata from the dataframe...\n",
"train_data = Dataset.load_from_df(reg_train[['user', 'movie', 'rating']], reader)\n",
"\n",
"# build the trainset from traindata.., It is of dataset format from surprise library..\n",
"trainset = train_data.build_full_trainset() "
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "JxUW5EQqGHQT"
},
"source": [
"
"
],
"text/plain": [
" user movie GAvg sur1 sur2 sur3 sur4 sur5 \\\n",
"0 808635 71 3.581679 3.581679 3.581679 3.581679 3.581679 3.581679 \n",
"1 941866 71 3.581679 3.581679 3.581679 3.581679 3.581679 3.581679 \n",
"\n",
" smr1 smr2 smr3 smr4 smr5 UAvg MAvg \\\n",
"0 3.581679 3.581679 3.581679 3.581679 3.581679 3.581679 3.581679 \n",
"1 3.581679 3.581679 3.581679 3.581679 3.581679 3.581679 3.581679 \n",
"\n",
" rating bslpr \n",
"0 5 3.581679 \n",
"1 4 3.581679 "
]
},
"metadata": {
"tags": []
},
"execution_count": 45
}
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"colab_type": "code",
"outputId": "e51e11b3-f92e-4af9-dc65-f981eabab9c7",
"id": "x7NO8ManGHQ7",
"colab": {}
},
"source": [
"# prepare train data\n",
"x_train = reg_train.drop(['user', 'movie','rating'], axis=1)\n",
"y_train = reg_train['rating']\n",
"\n",
"# Prepare Test data\n",
"x_test = reg_test_df.drop(['user','movie','rating'], axis=1)\n",
"y_test = reg_test_df['rating']\n",
"\n",
"# initialize Our first XGBoost model...\n",
"xgb_bsl = xgb.XGBRegressor(silent=False, n_jobs=13, random_state=15, n_estimators=100)\n",
"train_results, test_results = run_xgboost(xgb_bsl, x_train, y_train, x_test, y_test)\n",
"\n",
"# store the results in models_evaluations dictionaries\n",
"models_evaluation_train['xgb_bsl'] = train_results\n",
"models_evaluation_test['xgb_bsl'] = test_results\n",
"\n",
"xgb.plot_importance(xgb_bsl)\n",
"plt.show()\n"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Training the model..\n",
"Done. Time taken : 0:00:02.388635\n",
"\n",
"Done \n",
"\n",
"Evaluating the model with TRAIN data...\n",
"Evaluating Test data\n",
"\n",
"TEST DATA\n",
"------------------------------\n",
"RMSE : 1.0763419061709816\n",
"MAPE : 34.491235560745295\n"
],
"name": "stdout"
},
{
"output_type": "display_data",
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '');\n",
" var titletext = $(\n",
" '');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('');\n",
"\n",
" var fmt_picker = $('');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('');\n",
" var button = $('');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "FI9B0KtGGHQ-"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "HH8TUxKnGHQ_"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "6bz3TFwUGHRA"
},
"source": [
"
4.4.4 Surprise KNNBaseline predictor
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "BTf7-oX_GHRA",
"colab": {}
},
"source": [
"from surprise import KNNBaseline"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "pWpJHruUGHRD"
},
"source": [
"- KNN BASELINE\n",
" - http://surprise.readthedocs.io/en/stable/knn_inspired.html#surprise.prediction_algorithms.knns.KNNBaseline "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "evfRoMO4GHRJ"
},
"source": [
"- PEARSON_BASELINE SIMILARITY\n",
" - http://surprise.readthedocs.io/en/stable/similarities.html#surprise.similarities.pearson_baseline "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "AsWYa_1vGHRJ"
},
"source": [
"- SHRINKAGE\n",
" - _2.2 Neighborhood Models_ in http://courses.ischool.berkeley.edu/i290-dm/s11/SECURE/a1-koren.pdf "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "1_aY9mirGHRK"
},
"source": [
"- __predicted Rating__ : ( ___ based on User-User similarity ___ )\n",
"\n",
"\\begin{align} \\hat{r}_{ui} = b_{ui} + \\frac{ \\sum\\limits_{v \\in N^k_i(u)}\n",
"\\text{sim}(u, v) \\cdot (r_{vi} - b_{vi})} {\\sum\\limits_{v \\in\n",
"N^k_i(u)} \\text{sim}(u, v)} \\end{align}\n",
"\n",
"- $\\pmb{b_{ui}}$ - _Baseline prediction_ of (user,movie) rating\n",
"\n",
"- $ \\pmb {N_i^k (u)}$ - Set of __K similar__ users (neighbours) of __user (u)__ who rated __movie(i)__ \n",
"\n",
"- _sim (u, v)_ - __Similarity__ between users __u and v__ \n",
" - Generally, it will be cosine similarity or Pearson correlation coefficient. \n",
" - But we use __shrunk Pearson-baseline correlation coefficient__, which is based on the pearsonBaseline similarity ( we take base line predictions instead of mean rating of user/item)\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "-6IOidY5GHRL"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "756ewezkGHRL"
},
"source": [
"- __ Predicted rating __ ( based on Item Item similarity ):\n",
" \\begin{align} \\hat{r}_{ui} = b_{ui} + \\frac{ \\sum\\limits_{j \\in N^k_u(i)}\\text{sim}(i, j) \\cdot (r_{uj} - b_{uj})} {\\sum\\limits_{j \\in N^k_u(j)} \\text{sim}(i, j)} \\end{align}\n",
"\n",
" - ___Notations follows same as above (user user based predicted rating ) ___"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "E7TBmajbGHRM"
},
"source": [
"
4.4.4.1 Surprise KNNBaseline with user user similarities
"
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"colab_type": "code",
"outputId": "3b216a14-66a1-41be-ccdd-586d890a0efd",
"id": "5YxpLxKfGHRM",
"colab": {}
},
"source": [
"# we specify , how to compute similarities and what to consider with sim_options to our algorithm\n",
"sim_options = {'user_based' : True,\n",
" 'name': 'pearson_baseline',\n",
" 'shrinkage': 100,\n",
" 'min_support': 2\n",
" } \n",
"# we keep other parameters like regularization parameter and learning_rate as default values.\n",
"bsl_options = {'method': 'sgd'} \n",
"\n",
"knn_bsl_u = KNNBaseline(k=40, sim_options = sim_options, bsl_options = bsl_options)\n",
"knn_bsl_u_train_results, knn_bsl_u_test_results = run_surprise(knn_bsl_u, trainset, testset, verbose=True)\n",
"\n",
"# Just store these error metrics in our models_evaluation datastructure\n",
"models_evaluation_train['knn_bsl_u'] = knn_bsl_u_train_results \n",
"models_evaluation_test['knn_bsl_u'] = knn_bsl_u_test_results\n"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Training the model...\n",
"Estimating biases using sgd...\n",
"Computing the pearson_baseline similarity matrix...\n",
"Done computing similarity matrix.\n",
"Done. time taken : 0:00:30.173847 \n",
"\n",
"Evaluating the model with train data..\n",
"time taken : 0:01:35.970614\n",
"---------------\n",
"Train Data\n",
"---------------\n",
"RMSE : 0.33642097416508826\n",
"\n",
"MAPE : 9.145093375416348\n",
"\n",
"adding train results in the dictionary..\n",
"\n",
"Evaluating for test data...\n",
"time taken : 0:00:00.075213\n",
"---------------\n",
"Test Data\n",
"---------------\n",
"RMSE : 1.0726493739667242\n",
"\n",
"MAPE : 35.02094499698424\n",
"\n",
"storing the test results in test dictionary...\n",
"\n",
"---------------------------------------------\n",
"Total time taken to run this algorithm : 0:02:06.220108\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "ivjFheRnGHRO"
},
"source": [
"
4.4.4.2 Surprise KNNBaseline with movie movie similarities
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "6e1164ed-6151-49d1-d55a-b6c8e109cf23",
"id": "iN8JIbgvGHRP",
"colab": {}
},
"source": [
"# we specify , how to compute similarities and what to consider with sim_options to our algorithm\n",
"\n",
"# 'user_based' : Fals => this considers the similarities of movies instead of users\n",
"\n",
"sim_options = {'user_based' : False,\n",
" 'name': 'pearson_baseline',\n",
" 'shrinkage': 100,\n",
" 'min_support': 2\n",
" } \n",
"# we keep other parameters like regularization parameter and learning_rate as default values.\n",
"bsl_options = {'method': 'sgd'}\n",
"\n",
"\n",
"knn_bsl_m = KNNBaseline(k=40, sim_options = sim_options, bsl_options = bsl_options)\n",
"\n",
"knn_bsl_m_train_results, knn_bsl_m_test_results = run_surprise(knn_bsl_m, trainset, testset, verbose=True)\n",
"\n",
"# Just store these error metrics in our models_evaluation datastructure\n",
"models_evaluation_train['knn_bsl_m'] = knn_bsl_m_train_results \n",
"models_evaluation_test['knn_bsl_m'] = knn_bsl_m_test_results\n"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Training the model...\n",
"Estimating biases using sgd...\n",
"Computing the pearson_baseline similarity matrix...\n",
"Done computing similarity matrix.\n",
"Done. time taken : 0:00:01.093096 \n",
"\n",
"Evaluating the model with train data..\n",
"time taken : 0:00:07.964272\n",
"---------------\n",
"Train Data\n",
"---------------\n",
"RMSE : 0.32584796251610554\n",
"\n",
"MAPE : 8.447062581998374\n",
"\n",
"adding train results in the dictionary..\n",
"\n",
"Evaluating for test data...\n",
"time taken : 0:00:00.075229\n",
"---------------\n",
"Test Data\n",
"---------------\n",
"RMSE : 1.072758832653683\n",
"\n",
"MAPE : 35.02269653015042\n",
"\n",
"storing the test results in test dictionary...\n",
"\n",
"---------------------------------------------\n",
"Total time taken to run this algorithm : 0:00:09.133017\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "BXnyPGztGHRR"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "4MyrRggKGHRR"
},
"source": [
"
4.4.5 XGBoost with initial 13 features + Surprise Baseline predictor + KNNBaseline predictor
"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "giJoi5O5GHRS"
},
"source": [
"- - - First we will run XGBoost with predictions from both KNN's ( that uses User\\_User and Item\\_Item similarities along with our previous features.\n",
"\n",
" \n",
"- - - Then we will run XGBoost with just predictions form both knn models and preditions from our baseline model. "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "QKnprtpsGHRS"
},
"source": [
"__Preparing Train data __"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "e3025e82-1136-4f0a-8b79-524498c5b46f",
"id": "kKOAq9JcGHRT",
"colab": {}
},
"source": [
"# add the predicted values from both knns to this dataframe\n",
"reg_train['knn_bsl_u'] = models_evaluation_train['knn_bsl_u']['predictions']\n",
"reg_train['knn_bsl_m'] = models_evaluation_train['knn_bsl_m']['predictions']\n",
"\n",
"reg_train.head(2)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"
"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"outputId": "99e75da3-58bf-4b1f-bd65-d8162ee50f20",
"id": "3Q1gteaAGHRv",
"colab": {}
},
"source": [
"# prepare train data\n",
"x_train = reg_train[['knn_bsl_u', 'knn_bsl_m', 'svd', 'svdpp']]\n",
"y_train = reg_train['rating']\n",
"\n",
"# test data\n",
"x_test = reg_test_df[['knn_bsl_u', 'knn_bsl_m', 'svd', 'svdpp']]\n",
"y_test = reg_test_df['rating']\n",
"\n",
"\n",
"xgb_all_models = xgb.XGBRegressor(n_jobs=10, random_state=15)\n",
"train_results, test_results = run_xgboost(xgb_all_models, x_train, y_train, x_test, y_test)\n",
"\n",
"# store the results in models_evaluations dictionaries\n",
"models_evaluation_train['xgb_all_models'] = train_results\n",
"models_evaluation_test['xgb_all_models'] = test_results\n",
"\n",
"xgb.plot_importance(xgb_all_models)\n",
"plt.show()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Training the model..\n",
"Done. Time taken : 0:00:01.292225\n",
"\n",
"Done \n",
"\n",
"Evaluating the model with TRAIN data...\n",
"Evaluating Test data\n",
"\n",
"TEST DATA\n",
"------------------------------\n",
"RMSE : 1.075480663561971\n",
"MAPE : 35.01826709436013\n"
],
"name": "stdout"
},
{
"output_type": "display_data",
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" if (mpl.ratio != 1) {\n",
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
" }\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" fig.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '');\n",
" var titletext = $(\n",
" '');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var backingStore = this.context.backingStorePixelRatio ||\n",
"\tthis.context.webkitBackingStorePixelRatio ||\n",
"\tthis.context.mozBackingStorePixelRatio ||\n",
"\tthis.context.msBackingStorePixelRatio ||\n",
"\tthis.context.oBackingStorePixelRatio ||\n",
"\tthis.context.backingStorePixelRatio || 1;\n",
"\n",
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
"\n",
" var rubberband = $('');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width * mpl.ratio);\n",
" canvas.attr('height', height * mpl.ratio);\n",
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('');\n",
"\n",
" var fmt_picker = $('');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'] / mpl.ratio;\n",
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
" var x1 = msg['x1'] / mpl.ratio;\n",
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x * mpl.ratio;\n",
" var y = canvas_pos.y * mpl.ratio;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" var width = fig.canvas.width/mpl.ratio\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width/mpl.ratio\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('');\n",
" var button = $('');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "display_data",
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {
"tags": []
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "OTH9n3xoGHRw"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "BVDDPizEGHRx"
},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "kw26rJ3SGHRy"
},
"source": [
"
4.5 Comparision between all models
"
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": false,
"colab_type": "code",
"outputId": "9124baa4-ff9f-4ab4-c9f4-4d4af70f46f7",
"id": "vSx1wSCDGHRy",
"colab": {}
},
"source": [
"# Saving our TEST_RESULTS into a dataframe so that you don't have to run it again\n",
"pd.DataFrame(models_evaluation_test).to_csv('sample/small/small_sample_results.csv')\n",
"models = pd.read_csv('sample/small/small_sample_results.csv', index_col=0)\n",
"models.loc['rmse'].sort_values()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"svd 1.0726046873826458\n",
"knn_bsl_u 1.0726493739667242\n",
"knn_bsl_m 1.072758832653683\n",
"svdpp 1.0728491944183447\n",
"bsl_algo 1.0730330260516174\n",
"xgb_knn_bsl_mu 1.0753229281412784\n",
"xgb_all_models 1.075480663561971\n",
"first_algo 1.0761851474385373\n",
"xgb_bsl 1.0763419061709816\n",
"xgb_final 1.0763580984894978\n",
"xgb_knn_bsl 1.0763602465199797\n",
"Name: rmse, dtype: object"
]
},
"metadata": {
"tags": []
},
"execution_count": 67
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "WGZU19iSGHR0"
},
"source": [
" "
]
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"colab_type": "code",
"outputId": "530dd853-d632-4187-b1f8-be58f60b5e28",
"id": "g0OB4lW0GHR1",
"colab": {}
},
"source": [
"print(\"-\"*100)\n",
"print(\"Total time taken to run this entire notebook ( with saved files) is :\",datetime.now()-globalstart)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\n",
"Total time taken to run this entire notebook ( with saved files) is : 0:42:08.302761\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Av5WS6n2xrsZ",
"colab_type": "text"
},
"source": [
"
5. Assignment
"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"id": "JkK0HIzgxrsd",
"colab_type": "text"
},
"source": [
"1.Instead of using 10K users and 1K movies to train the above models, use 25K users and 3K movies (or more) to train all of the above models. Report the RMSE and MAPE on the test data using larger amount of data and provide a comparison between various models as shown above.\n",
"\n",
"NOTE: Please be patient as some of the code snippets make take many hours to compelte execution.\n",
"\n",
"2.Tune hyperparamters of all the Xgboost models above to improve the RMSE."
]
},
{
"cell_type": "code",
"metadata": {
"id": "C-s6Laykxrsd",
"colab_type": "code",
"outputId": "83116885-7860-48e2-befd-8eca1bd66286",
"colab": {}
},
"source": [
"%%javascript\n",
"// Converts integer to roman numeral\n",
"// https://github.com/kmahelona/ipython_notebook_goodies\n",
"// https://kmahelona.github.io/ipython_notebook_goodies/ipython_notebook_toc.js\n",
"function romanize(num) {\n",
" var lookup = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1},\n",
"\troman = '',\n",
"\t i;\n",
"\tfor ( i in lookup ) {\n",
"\t while ( num >= lookup[i] ) {\n",
"\t\troman += i;\n",
"\t\tnum -= lookup[i];\n",
"\t }\n",
"\t}\n",
"\treturn roman;\n",
" }\n",
"\n",
"// Builds a
Table of Contents from all in DOM\n",
"function createTOC(){\n",
" var toc = \"\";\n",
" var level = 0;\n",
" var levels = {}\n",
" $('#toc').html('');\n",
"\n",
" $(\":header\").each(function(i){\n",
"\t if (this.id=='tocheading'){return;}\n",
" \n",
"\t var titleText = this.innerHTML;\n",
"\t var openLevel = this.tagName[1];\n",
"\n",
"\t if (levels[openLevel]){\n",
"\t\tlevels[openLevel] += 1;\n",
"\t } else{\n",
"\t\tlevels[openLevel] = 1;\n",
"\t }\n",
"\n",
"\t if (openLevel > level) {\n",
"\t\ttoc += (new Array(openLevel - level + 1)).join('
');\n",
"\t } else if (openLevel < level) {\n",
"\t\ttoc += (new Array(level - openLevel + 1)).join(\"
"
],
"text/plain": [
" knn_bsl_u knn_bsl_m bsl_algo ... svdpp xgb_final xgb_all_models\n",
"mape 34.9366 34.9281 34.9891 ... 34.9061 34.4833 35.1817\n",
"rmse 1.07241 1.07232 1.07316 ... 1.07311 1.07612 1.07505\n",
"\n",
"[2 rows x 8 columns]"
]
},
"metadata": {
"tags": []
},
"execution_count": 90
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vheIp8ZURGmi",
"colab_type": "text"
},
"source": [
" ## **Note:**\n",
" ### All the results are computed on 25k data points"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "tRMeajT3QkzY",
"colab_type": "text"
},
"source": [
"## Observations\n",
"\n",
"1. We have got slight better scores as compared to scores on 10k data.\n",
"2. Compute time has increased drasticaly as we have increased points.\n",
"3. We have plot feature_importance for each models that helps in understanding features.\n",
"4. **knn_bsl_u and knn_bsl_m** have got slightly better scores as compared to other models.\n",
"5. Surprise library features yeild similar as compared with other Self computed features."
]
}
]
}