{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# PageRank " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we discuss the PageRank algorithm, let's take a quick look at its history and what motivated its creation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Early Search Engines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Early approaches to search engines involved crawling web pages and then listing terms that appeared and storing them in an *inverted index*, a data structure that allows locating all places *pointed to* by a term. \n", "\n", "When a *search query* is given:\n", "* terms are extracted from the search query\n", "* pages are pointed to by the terms are retrieved\n", "* pages are ranked according to\n", " * how the terms were used in the page (e.g. terms appearing in headers have more importance)\n", " * how frequent those terms appeared in the page" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Term Spam" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some unethical people saw the opportunity of fooling search engines to direct users to their site (a sort of free advertisement). For example, if they want to direct users searching for 'movie' to their site, they would do the following:\n", "* Add a *lot* of occurances of the term 'movie' to their site (with font color similar to page's background to hide the text)\n", "* Do a search for *movie* and copy contents of the highest-ranked page (again, using the \"invisible text\" approach above).\n", "This technique for fooling a search engine into believing that a page is something that it is not is called *term spam*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introducing Page Rank" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To combat term spam, Google introduced two innovations:\n", "* They introduced PageRank to assess the importance of pages\n", " - A simulation of Web surfers, starting at a random page, randomly following outlinks in each page they visit. Process iterates many times. \n", " - The pages visited more often by the surfers are considered more important\n", "* Pages were judged not only by the terms that appeared in them, but by the *terms used in the link* (the anchor text), and the terms near the links to that page." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Graph Notations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To illustrate how PageRank and other Link Analysis algorithms work, it is useful to think of the Web as a directed graph with the following characteristics:\n", "* **nodes** represent pages\n", "* **arcs** represent links from one page to another " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining and Displaying Graphs with NetworkX" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A useful package for defining and displaying graphs in python is NetworkX. Below is an illustration of how we create a directed graph by defining the graph's nodes and edges. We then show the graph with a call to the `draw` command." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import networkx as nx\n", "import matplotlib.pyplot as plt\n", "\n", "G1 = nx.DiGraph()\n", "G1.add_nodes_from([\"A\",\"B\",\"C\",\"D\",\"E\"])\n", "G1.add_edges_from([\n", " (\"A\",\"B\"), (\"A\",\"C\"), (\"A\",\"D\"), (\"A\",\"E\"), \n", " (\"B\",\"A\"), (\"B\",\"D\"), \n", " (\"C\",\"A\"), \n", " (\"D\",\"B\"), (\"D\",\"C\"),\n", " (\"E\",\"B\"),\n", "])\n", "\n", "plt.figure() \n", "plt.title(\"Graph 1. A Graph as a hypothetical representation of the web\")\n", "nx.draw(G1, node_size=500, node_color='orange', with_labels=True, font_weight='bold', arrowsize=20)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adjacency Matrix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The **Adjacency Matrix** is a representation showing which vertices are adjacent, i.e., connected by edges or links. Formally, if $A$ is the adjacency matrix of a graph $G$, and $a_{ij}$ are the elements of $A$, \n", "$a_{ij}=1$ if there is a link from node $i$ to node $j$, otherwise, $a_{ij}=0$.\n", "\n", "In the discussion later on Hubs and Authorities, this matrix will also be known as the **Link Matrix**. It represents the existence of outgoing links from one page to another.\n", "\n", "In NetworkX, the adjacency matrix can be computed using the `adjacency_matrix` function. This produces a sparse matrix. We can convert this to a dense matrix by calling the `toarray` method." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 1, 1, 1],\n", " [1, 0, 0, 1, 0],\n", " [1, 0, 0, 0, 0],\n", " [0, 1, 1, 0, 0],\n", " [0, 1, 0, 0, 0]])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = nx.adjacency_matrix(G1).toarray()\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Transition Matrix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the computation of PageRank, an important data structure is the **Transition Matrix**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall the idea of a surfer randomly browsing through the Web. Given graph $G1$, our hypothetical representation of the Web in Figure 1, if the surfer is at page A, the following are the probabilities of the next page he will visit:\n", "* page A: 0\n", "* page B: 1/4\n", "* page C: 1/4\n", "* page D: 1/4\n", "* page E: 1/4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do the same for all other nodes and define a *Transition Matrix* to represent the the probabilities of where this surfer would go next, for any starting location in the Web. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To generate the Transition Matrix from a graph, we will need the *transpose* of its Adjacency Matrix as well as a vector of its out degrees (the number of links coming out from a node or page) . We have already computed for $G1$'s adjacency matrix and stored it in $A$; to get the outlinks of the nodes, we can use the graph's `out_degree` method: " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "OutDegreeView({'A': 4, 'B': 2, 'C': 1, 'D': 2, 'E': 1})" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "G1.out_degree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then convert this into an numpy array, getting only the values of the outdegrees:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([4, 2, 1, 2, 1])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "d = np.array([x[1] for x in list(G1.out_degree)])\n", "d" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can compute the Transition matrix:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0. , 0.5 , 1. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0.5 , 1. ],\n", " [0.25, 0. , 0. , 0.5 , 0. ],\n", " [0.25, 0.5 , 0. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0. , 0. ]])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = A.T * (1/d)\n", "M" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def transition_matrix_beta(G):\n", " \"\"\"\n", " Compute the Transition Matrix given a NetworkX graph\n", " This version cannot handle dead-ends, i.e., nodes without outlinks,\n", " hence it's called beta\n", " \n", " Parameters\n", " ----------\n", " G : NetworkX graph\n", " Graph to extract the transition matrix\n", " Returns\n", " -------\n", " M : numpy array\n", " Numpy array of the transition matrix of G\n", " \"\"\" \n", " A = nx.adjacency_matrix(G).toarray()\n", " d = np.array([x[1] for x in list(G.out_degree)])\n", " M = A.T * (1/d)\n", " return M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that version of the function above cannot handle dead-ends (nodes without outlinks, to be discussed in a later section). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The transition matrix could now be computed from a NetworkX graph as follows:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0. , 0.5 , 1. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0.5 , 1. ],\n", " [0.25, 0. , 0. , 0.5 , 0. ],\n", " [0.25, 0.5 , 0. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0. , 0. ]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "transition_matrix_beta(G1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This matrix $M$ represents the transition matrix of $G1$, with the indices for nodes A, B, C, D,E being 0,1,2,3,4 respectively." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PageRank Computation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "PageRank is a function that assigns a real number to a page in the Web, reflecting the page's importance. There are variations in the algorithms that compute the PageRank. We will tackle a few of them in the succeeding sections. \n", "\n", "Formally, if $m_{ij}$ are the elements of $M$, and node $j$ has $d$ outgoing links, we set $m_{ij}$ to $1/d$ if there is a link from page $j$ to page $i$, otherwise, we set it to 0. From this, we get that the $j$th column represents the transition probability distribution for a surfer who is at node $j$. This matrix is the **idealized PageRank**. Note that the sum of each column is exactly 1. Such a matrix where each column sums up to one is said to be *stochastic*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To compute the actual PageRank, we have to iterate the surfing process.\n", "* Surfer starts at any location in the web with equal probability. Let's represent this as $\\textbf{v}^{(0)}$.\n", "* After one step, the distribution of the surfer will be $\\textbf{v}^{(1)}$ = $M \\textbf{v}^{(0)}$\n", "* After two steps, the distribution of the surfer will be $\\textbf{v}^{(2)}$ = $M\\textbf{v}^{(1)}$ = $M(M \\textbf{v}^{(0)})$ = $M^{2} \\textbf{v}^{(0)} $ \n", "\n", "{numref}`pagerank-update` below illustrates how $\\textbf{v}$ is updated for one iteration, showing that the updated value of $\\textbf{v}$ gives the new probability distribution for all the nodes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{figure} ./images/PageRank-Update-Mv.PNG\n", ":name: pagerank-update\n", ":width: 450px\n", "\n", "One iteration/update of PageRank Algorithm.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can thus formulate an update rule $\\textbf{v}^{(i+1)} = M\\textbf{v}^{(i)}$, which is an example of a *Markov process*. It is known that such a process will stabilize at $\\textbf{v} = M\\textbf{v}$ if the following conditions are met:\n", "1. The graph is strongly connected\n", "2. There are no dead ends" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the above, we observe that stability can be reached if $M\\textbf{v}$ no longer updates $\\textbf{v}$. \n", "\n", "This $\\textbf{v}$ is, in fact, the an *eigenvector* of $M$. Recall:\n", "An *eigenvector* of a matrix $M$ is a vector $\\textbf{v}$ that satisfies\n", "$$ \\textbf{v}= \\lambda M \\textbf{v} $$\n", "where $\\lambda$ is some constant known as the *eigenvalue*.\n", "\n", "Since $M$ is stochastic, we can further say that $\\textbf{v}$ is the principal eigenvector of $M$ and $\\lambda =1$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From this, we can say that the principal eigenvector of $M$ gives us the probability distribution of where (at which page) a random surfer would most likely be after a long time. Thus,the principal eigenvector actually gives us the PageRank of each page. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Iterative Method for Computing PageRank" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we summarize the process of computing the PageRank known as the *Power Iteration Method*. Given $n$ pages/nodes:\n", "1. Create the characteristic matrix $M$ (size $nxn$), whose elements $m_{ij}$ are defined as follows:\n", " \n", " $\n", " m_{ij}=\\left\\{\n", " \\begin{array}{@{}ll@{}}\n", " 1/d, & \\text{if there is a link from page } j \\text{ to } i \\text{ and page } j \\text{ has } d \\text{ outlinks}\\\\\n", " 0, & \\text{otherwise}\n", " \\end{array}\\right.\n", " $\n", "\n", "\n", "2. Initialize $\\textbf{v}^{(0)}$ \n", "\n", " $\n", " \\textbf{v}^{(0)}_{i} = 1/n \\text{, for all } i \n", " $\n", "\n", "\n", "3. Iteratively update $\\textbf{v}$ \n", "\n", " $\n", " \\textbf{v}^{(t+1)} = M\\textbf{v}^{(t)}\n", " $\n", "\n", "\n", "4. Stop when $\\lvert \\textbf{v}^{(t+1)}-\\textbf{v}^{(t)} \\rvert < \\epsilon $." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Power Iteration Method for Computing the Idealized PageRank" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get a concrete idea how the algorithm works, below is a python implementation of the Idealized PageRank using the Power Iteration Method." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "scrolled": true }, "outputs": [], "source": [ "def idealized_page_rank(M, tol=10**-6, max_iter=100):\n", " \"\"\"\n", " Compute the Idealized PageRank (without Taxation) of a given Transition Matrix \n", " \n", " Parameters\n", " ----------\n", " M : numpy array\n", " Transition Matrix: Array of shape (n, n), where n is the number of nodes in the network\n", " tol : float\n", " Tolerance: Iteration stops if the distance between previous and updated PageRank vectors \n", " goes below this value\n", " max_iter : integer\n", " Maximum number of iterations\n", " Returns\n", " -------\n", " v : numpy array\n", " Vector of size n containing the ordinary PageRank values \n", " \"\"\" \n", " n = M.shape[0]\n", " v = np.ones(n)/n\n", " delta = 1/tol # initialize vector difference to a large number\n", " i = 0\n", " while delta > tol:\n", " i += 1\n", " prev_v = v\n", " v = M.dot(v)\n", " delta = np.sum(np.abs(v-prev_v))\n", " if i >= max_iter:\n", " break\n", " return v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Applying function to the $G1$ in Figure 1, we get the following results." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "array([0.30000001, 0.25000013, 0.17500001, 0.1999999 , 0.07499995])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = transition_matrix_beta(G1)\n", "idealized_page_rank(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Issues with PageRank" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this subsection, we consider two issues that affect the computation of PageRank." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deadends" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's take a look at *deadends*, pages with no outgoing links. The graph $G2$ below is the same as $G1$ except that the outgoing link from node E was removed." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "G2 = nx.DiGraph()\n", "G2.add_nodes_from([\"A\",\"B\",\"C\",\"D\",\"E\"])\n", "G2.add_edges_from([\n", " (\"A\",\"B\"), (\"A\",\"C\"), (\"A\",\"D\"), (\"A\",\"E\"), \n", " (\"B\",\"A\"), (\"B\",\"D\"), \n", " (\"C\",\"A\"), \n", " (\"D\",\"B\"), (\"D\",\"C\"),\n", " # no outgoing link from node E\n", "])\n", "\n", "plt.figure() \n", "plt.title(\"Graph 2. A network with a deadend (at node E).\")\n", "nx.draw(G2, node_size=500, node_color='orange', with_labels=True, font_weight='bold', arrowsize=20)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Consider the graph bove. Here we see that node E does not have any outgoing links, thus E is a deadend. The problem with deadends is that it is like a sink for infornation because while it 'absorbs' PageRank scores during the iterations: it does not pass them on to other nodes. As a result, some or all the components would eventually go to zero." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To see this effect, let us edit our `transition_matrix` function to handle nodes with zero degrees. The main problem is that a division by zero error could occur when we divide $A.T$ by $d$ if $d$ has zero values. To address this, we just set the zero values of $d$ to 1. This will not affect the resulting matrix since the corresponding column in $A.T$ will all be zeros (recall that there are no outlinks for this node.)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def transition_matrix(G):\n", " \"\"\"\n", " Compute the Transition Matrix given a NetworkX graph\n", " \n", " Parameters\n", " ----------\n", " G : NetworkX graph\n", " Graph to extract the transition matrix\n", " Returns\n", " -------\n", " M : numpy array\n", " Numpy array of the transition matrix of G\n", " \"\"\" \n", " A = nx.adjacency_matrix(G).toarray()\n", " d = np.array([x[1] for x in list(G.out_degree)])\n", " \n", " # get indices with zero and replace them with 1 to avoid division by zero\n", " # this won't affect the result since the corresponding column will have all zeros\n", " d[d==0]=1\n", " \n", " M = A.T * (1/d) \n", " return M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now compute the transition matrix for Graph 2 and see what happens when we apply the Power Iteration Method." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0. , 0.5 , 1. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0.5 , 0. ],\n", " [0.25, 0. , 0. , 0.5 , 0. ],\n", " [0.25, 0.5 , 0. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0. , 0. ]])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2 = transition_matrix(G2)\n", "M2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we observe that the characteristic Matrix is no longer *stochastic* - the sum at column E is 0." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1., 1., 1., 1., 0.])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2.sum(axis=0)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "scrolled": false }, "outputs": [], "source": [ "r = idealized_page_rank(M2)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0., 0., 0., 0., 0.])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.rint(r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We observe that the final values of all elements of $\\textbf{v}$ are 0." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Spider Traps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another issue that PageRank algorithm has to face is the existence of spider traps. A spider trap is a set of nodes with no dead ends but no link out of the set of nodes comprising the spider trap. The effect on the PageRank algorithm is the opposite of deadends. Whereas in deadends information \"leaks out\" from the deadend, in the case of spider traps, they get accumulated.\n", "Graph 3 shown by {numref}`spider-trap` below is an example of a network with a 1-node spider trap." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{figure} ./images/SpiderTrap.PNG\n", ":width: 450px\n", ":name: spider-trap\n", "\n", "(Graph 3): A network with a spider trap (at node E).\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note**: Earlier versions of NetworkX might not show the self-loop in Node E, that's why we provided an image above. However, it can be confirmed in the transition matrix below that the self-loop exists if you look at the noting that the entry for row-column of E is 1 (M3[4,4] below)." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "G3 = nx.DiGraph()\n", "G3.add_nodes_from([\"A\",\"B\",\"C\",\"D\",\"E\"])\n", "G3.add_edges_from([\n", " (\"A\",\"B\"), (\"A\",\"C\"), (\"A\",\"D\"), (\"A\",\"E\"), \n", " (\"B\",\"A\"), (\"B\",\"D\"), \n", " (\"C\",\"A\"), \n", " (\"D\",\"B\"), (\"D\",\"C\"),\n", " (\"E\",\"E\"),\n", "])\n", "\n", "plt.figure() \n", "plt.title(\"Graph 3. A network with a spider trap (at node E).\")\n", "pos = nx.circular_layout(G3)\n", "nx.draw(G3, pos, node_size=500, node_color='orange', with_labels=True, font_weight='bold', arrowsize=20)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0. , 0.5 , 1. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0.5 , 0. ],\n", " [0.25, 0. , 0. , 0.5 , 0. ],\n", " [0.25, 0.5 , 0. , 0. , 0. ],\n", " [0.25, 0. , 0. , 0. , 1. ]])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M3 = transition_matrix(G3)\n", "M3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we compute the PageRank, we'll notice that all the traffic goes to node E." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "array([0., 0., 0., 0., 1.])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = idealized_page_rank(M3)\n", "np.rint(r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PageRank with Taxation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A solution to the two issues we mentioned above (deadends and spider traps) is to allow each random surfer to *teleport* with a small probability $\\beta$ rather than following an out-link in its current page. This method of random teleportation is called *taxation* in the textbook.\n", "\n", "If $\\textbf{e}$ is a vector of 1's of size $n$, The iteration step now becomes:\n", "\n", "$\n", " \\textbf{v}^{(t+1)}= \\beta M\\textbf{v}^{(t)} + (1-\\beta)\\textbf{e}/n\n", "$\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\\beta$ values are usually chosen in the range of 0.8 to 0.9. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We create a taxed version of the PageRank below." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "scrolled": true }, "outputs": [], "source": [ "def taxed_page_rank(M, beta=0.8, tol=10**-6, max_iter=100):\n", " \"\"\"Compute the Taxed PageRank (without Taxation) of a given Transition Matrix \n", " Note that this not make use of `e` -- the vector of ones \n", " since numpy's broadcasting takes care of properly computing a vector-constant addition\n", " Parameters\n", " ----------\n", " M : numpy array\n", " Transition Matrix: Array of shape (n, n), where n is the number of nodes in the network\n", " tol : float\n", " Tolerance: Iteration stops if the distance between previous and updated PageRank vectors \n", " goes below this value\n", " max_iter : integer\n", " Maximum number of iterations\n", " Returns\n", " -------\n", " v : numpy array\n", " Vector of size n containing the ordinary PageRank values \n", " \"\"\" \n", " n = M.shape[0]\n", " v = np.ones(n) \n", " delta = 1/tol # initialize vector difference to a large number\n", " i = 0\n", " while delta > tol:\n", " i += 1\n", " prev_v = v\n", " v = beta*M.dot(v) + ((1-beta)/n) \n", " delta = np.sum(np.abs(v-prev_v)) # compute L1 norm \n", " if i >= max_iter:\n", " break\n", " return v" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.2 , 0.13333334, 0.13333334, 0.13333334, 0.40000391])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M3 = transition_matrix(G3)\n", "taxed_page_rank(M3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using PageRank in Search Engine" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Google is said to use over 250 properties of pages from which the ranking is decided, among those properties, PageRank is among the important ones. The others are:\n", "- presence of search terms\n", "- where the search terms appear in the page (e.g. headers)\n", "- search terms appearing in pages that link to the page in question" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**EXERCISES**\n", "\n", "1. As discussed above, Google introduced the innovation of judging the importance of a page by the terms used in the link, and not just hte links that appeared in the page. How does this defeat Term Spam?\n", "2. Why not just count the number of inlinks to a page instead of doing PageRank?\n", "3. Given Graph 1, remove node E and its edges then compute its PageRank. (This is the example give in Lescovec et al.'s book {cite:ps}`leskovec2020mining`).\n", "4. Given Graph 3, remove node E and its edges then compute its Taxed PageRank. (This is the example give in Lescovec et al.'s book {cite:ps}`leskovec2020mining`)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**REFERENCE** {cite:ps}`leskovec2020mining`" ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:.conda-dla2]", "language": "python", "name": "conda-env-.conda-dla2-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "294.188px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }