如果我们自己做搜索,可以想到文章和搜索词的相关性,从而判断这篇文章是不是我们想要的。最初的搜索有的是这样做的,有的是根据网站类型做一个很大的索引表,但能索引的关键词有限。
互联网上的网页数量估计有几百亿(猜测),所以显然不是所有包含搜索关键词的网页都同等重要。有的在标题中包含关键词,有的在文档中包含关键词;有的是权威机构的网站,有的是个人博客。显然,在将网页返回给用户时,更重要的应该放在前面,不重要的应该放在后面。这里还有一个问题,如何确定一个网页的重要性。
网页是由链接组织起来的,所以我们可以把整个互联网看成一个大图,每个节点就是一个网页,网页之间的链接就是边。一个网页是否重要,取决于是否有多个网页链接。链接的网页越多,就越重要。当然,链接这个网页的多个链接的重要性是不同的。
假设我们通过搜索得到很多网页,一个网页Y的排名应该来自所有指向这个网页X1、X2和X3的权重之和:
网页的权重Y = X1+X2+X3...+Xn,那么X1的权重是多少,X2,...分别Xn?如何衡量它们?这个需要通过链接到它的网页权重来计算,这样就无解了。据说谷歌的布林破解了这个怪圈,就是一开始就给每个网页设置相同的初始值。然后经过几轮计算,这个算法可以保证网页在多次排名后收敛到排名的真实值。
让我理解一下,大概是这样的:
第一轮,我们假设所有网页的权重都是1,那么一个网页的权重就是1+1+1到3。第二轮计算,一个网页的权重变成2,最后一个网页的权重变成2+2+2=6。经过多次计算,权重高的网页链接多的网页排名靠前,其他的排名靠后。
整个过程有点类似民主选举,每个人的选票权重不一样,和现实差不多。那么PageRank算法除了计算网页排名还有什么用呢?实际数据战斗45中有个有趣的例子。它可以计算出希拉里邮件列表中被泄露人物的影响力。PageRank的值可以通过python的networkx库很容易地计算出来。
如下网络图:
计算PageRank的简单代码:
导入networkx为nx#创建一个有向图G = nx。有向图()# Edge =[( # 34;B1 # 34, #34;B第34名;), (#34;B2 # 34;, #34;B第34名;), (#34;C1 # 34;, #34;C # 34), (#34;C2 # 34;, #34;C # 34), (#34;D1 # 34;, #34;D # 34), (#34;D2 # 34;, #34;D # 34), (#34;D # 34, #34;一 # 34;), (#34;C # 34, #34;一 # 34;), (#34;B第34名;, #34;一 # 34;)]对于edges中的edge:G . add _ edge(edge[0],edge[1])page rank _ list = NX . page rank(G,alpha = 1)print( # 34;Pagerank值为: # 34;,pagerank_list)结果:
整个数据采集分为三个文件:Aliases.csv、Emails.csv和people.csv,其中Emails文件是邮件内容,包括重要的发件人和收件人信息。Persons文件统计邮件中所有人的姓名和相应的id。下面是实际数据战斗的代码。其实流程比较简单,但是这个思路比较重要。
# -*-编码:utf-8 -*-#用PageRank挖掘希拉里邮件中的重要任务关系导入Pandas作为PD导入Network X作为NX导入Numpy作为NP从Collections导入Default dict Import matchplotlib . py Plot作为PLT # Data Load emails = PD . read _ CSV( # 34;。/input/emails . CSV # 34;)#读取别名文件file = PD . Read _ CSV( # 34;。/input/aliases . CSV # 34;)aliases = {}for index,row in file . ITER rows():aliases[row[ # 39;别名 # 39;]] =行[ # 39;PersonId # 39] #读取名称文件file = PD . Read _ CSV( # 34;。/input/persons . CSV # 34;)persons = {}for index,row in file . ITER rows():persons[row[ # 39;Id # 39]] =行[ # 39;姓名 # 39;] #转换别名def unify_name(name): #名称用小写name = str(name)统一。lower() # Remove,@ name = name . replace( # 34;,#34;,#34;#34;).拆分( # 34;@#34;)[0] #别名转换if name in aliases . keys():return people[aliases[name]]return name #绘制网络图def show _ graph (graph,layout = # 39spring _ layout # 39):#使用弹簧布局布局,类似于中心径向if布局= = # 39;circular _ layout # 39:positions = NX . circular _ layout(graph)Else:positions = NX . spring _ layout(graph)#设置网络图中的节点大小,与pagerank值有关。因为pagerank值小,所以需要* 20000 nodesize =[x[ # 39;pagerank # 39] * 20000 for v,x in graph.nodes (data = true)] #设置边长edgesize =[NP . sqrt(e[2][ # 39;重量 # 39;])for e in graph . edges(data = true)]# Draw node NX . Draw _ networkx _ nodes(graph,positions,node _ size = nodesize,alpha = 0.4)# Draw edge NX . Draw _ networkx _ edges(graph,positions,edge _ size = edgesize,alpha = 0.2)# Draw the label NX . Draw _ networkx _ labels(graph,Positions,Font_size=10) #输出希拉里邮件plt.show()中的所有关系图#规范发件人和收件人邮件的名称,元数据来自=电子邮件。元数据来自。apply(unify _ name)emails . metadata to = emails . metadata to . apply(unify _ name)#将每次传递的权重设置为发送的电子邮件数。edges _ weights _ temp = zip(emails . metadata from,emails)中的行的默认字典(列表)。元数据,电子邮件。RawText): temp = (row[0],Row[1])if temp not in edges _ weights _ temp:edges _ weights _ temp[temp]= 1 else:edges _ weights _ temp[temp]= edges _ weights _ temp[temp]+1 #转换格式(from,to),weight = > >;From,to,weight edges _ weights = [(key [0],key [1],val) for key,val in edges _ weights _ temp . items()]#创建一个有向图graph = nx。DiGraph()#设置有向图(from,To,weight)中的路径和权重graph . add _ weighted _ edges _ from(edges _ weights)#计算每个节点(person)的PR值并将其作为节点pagerank = nx.pagerank(graph)#使用pagerank值作为节点nx.set _ node _ attributes (graph,name = pagerank # 39,values=pagerank)#绘制网络图show_graph(graph)#精简完整图#设置PR值的阈值,Pagerank_threshold = 0.005#对于大于筛选阈值的重要核心节点,复制一个计算出来的网络图small_graph = graph.copy()#截断PR值小于Pagerank_threshold的节点为n,p _ rank in graph.nodes (data = true):如果p _ rank[ # 39;pagerank # 39] lt;PageRank _ Threshold: small _ graph。Remove _ node (n) #画一个网络图,用circular_layout布局使选中的点形成一个圆show_graph(small_graph, # 39;circular _ layout # 39)