PageRank算法
PageRank算法基于论文影响力因子的思想提出:当一篇论文被引用的次数越多,证明这篇论文的影响力越大
PageRank 的简化模型:
一个网页的影响力 = 所有入链集合的页面的加权影响力之和,公式表示为:
PR(u)=∑v∈BuPR(v)L(v)
P R(u)=\sum_{v \in B_{u}} \frac{P R(v)}{L(v)}
PR(u)=v∈Bu∑L(v)PR(v)
u 为待评估的页面,Bu为页面 u 的入链集合。针对入链集合中的任意页面 v,它能给 u带来的影响力是其自身的影响力 PR(v) 除以 v 页面的出链数量,统计所有能给 u 带来链接的页面 v,得到的总和就是网页 u
的影响力,即为 PR(u)。
对于上图中可以得到转移矩阵M:
M=[01/2101/3001/21/3001/21/31/200]
M=\left[ \begin{array}{cccc}{0} & {1 / 2} & {1} & {0} \\ {1 / 3} & {0} & {0} & {1 / 2} \\ {1 / 3} & {0} & {0} & {1 / 2} \\ {1 / 3} & {1 / 2} & {0} & {0}\end{array}\right]
M=⎣⎢⎢⎡01/31/31/31/2001/2100001/21/20⎦⎥⎥⎤
假设 A、B、C、D 四个页面的初始影响力都是相同的,则当进行第一次转移之后,各页面的影响力变为:
w1=Mw0=[01/2101/3001/21/3001/21/31/200][1/41/41/41/4]=[9/245/245/245/24]
w_{1}=\mathrm{M} w_{0}=\left[ \begin{array}{cccc}{0} & {1 / 2} & {1} & {0} \\ {1 / 3} & {0} & {0} & {1 / 2} \\ {1 / 3} & {0} & {0} & {1 / 2} \\ {1 / 3} & {1 / 2} & {0} & {0}\end{array}\right] \left[ \begin{array}{c}{1 / 4} \\ {1 / 4} \\ {1 / 4} \\ {1 / 4}\end{array}\right]=\left[ \begin{array}{c}{9 / 24} \\ {5 / 24} \\ {5 / 24} \\ {5 / 24}\end{array}\right]
w1=Mw0=⎣⎢⎢⎡01/31/31/31/2001/2100001/21/20⎦⎥⎥⎤⎣⎢⎢⎡1/41/41/41/4⎦⎥⎥⎤=⎣⎢⎢⎡9/245/245/245/24⎦⎥⎥⎤
然后我们再用转移矩阵w1乘以w2得到结果,直到第 n 次迭代后影响力不再发生变化,可以收敛到 (0.3333,0.2222,0.2222,0.2222),也就是对应着 A、B、C、D 四个页面最终平衡状态下的影响力。
实际情况通常较为复杂,可能会面临两个问题:
- 等级泄露:如果一个网页没有出链,最终会导致其他网页的 PR 值为 0。
- 等级沉没::如果一个网页只有出链,没有入链,计算的过程迭代下来,会导致这个网页的 PR 值为 0。
PageRank随机浏览模型:为解决等级泄露和等级沉没的问题而提出。
定义了阻尼因子d,代表用户按照跳转链接上网的概率,通常可以取
一个固定值 0.85,而 1-d=0.15 则代表了用户不是通过跳转链接的方式来访问网页的。
PR(u)=1−dN+d∑v∈BuPR(v)L(v)
P R(u)=\frac{1-d}{N}+d \sum_{v \in B_{u}} \frac{P R(v)}{L(v)}
PR(u)=N1−d+dv∈Bu∑L(v)PR(v)
N 为网页总数,阻尼因子的加入一定程度上解决了等级泄露和等级沉没的问题。
networkx中实现PageRank方法的常用操作:
-
图的创建:
图可以分为无向图和有向图
无向图指的是不用节点之间的边的方向,使用 nx.Graph() 进行创建
有向图指的是节点之间的边是有方向的,使用 nx.DiGraph() 来创建
-
节点增加、删除、查询
G.add_node(‘A’) 添加一个节点
G.add_nodes_from([‘B’,‘C’,‘D’,‘E’]) 添加节点集合
G.remove_node(node) 删除一个指定的节点
G.remove_nodes_from([‘B’,‘C’,‘D’,‘E’]) 删除集合中的节点
G.nodes()得到图中所有节点
G.number_of_nodes() 得到图中节点的个数
-
边的增加、删除、查询
G.add_edge(“A”, “B”) 添加指定的“从 A 到B”的边
add_edges_from 函数从边集合中添加
add_weighted_edges_from 函数从带有权重的边的集合中添加,接收的是 1 个或多个三元组 [u,v,w] 作为参数,u、v、w 分别代表起点、终点和权重
remove_edge 函数和 remove_edges_from 函数删除指定边和从边集合中删除
edges() 函数访问图中所有的边
number_of_edges() 函数得到图中边的个数
例使用PageRank挖掘希拉里邮件中人物关系:
import networkx as nx
import pandas as pd
import numpy as np
from collections import defaultdict
import matplotlib.pyplot as plt
#数据加载
emails = pd.read_csv('./Emails.csv')
#读取别名文件
file = pd.read_csv('./Aliases.csv')
aliases = {}
for index, row in file.iterrows():
aliases[row['Alias']] = row['PersonId']
#读取人名文件
file = pd.read_csv('./Persons.csv')
persons = {}
for index, row in file.iterrows():
persons[row['Id']] = row['Name']
#对别名进行转换
def unify_name(name):
#姓名同一小写
name = str(name).lower()
#去除,和@后内容
name = name.replace(',', '').split('@')[0]
#别名转换
if name in aliases.keys():
return persons[aliases[name]]
return name
#画网络图
def show_graph(graph):
#使用spring layout布局,中心放射状
positions = nx.spring_layout(graph)
#设置网络图中节点大小,大小与PageRank值有关,因为PageRank值很小序所以需要*20000
nodesize = [x['pagerank'] * 20000 for v,x in graph.nodes(data=True)]
#设置网络图中边长度
edgesize = [np.sqrt(e[2]['weight']) for e in graph.edges(data=True)]
#绘制节点
nx.draw_networkx_nodes(graph, positions, node_size=nodesize, alpha=0.4)
#绘制边
nx.draw_networkx_edges(graph, positions, edge_size=edgesize, alpha=0.2)
#绘制节点的label
nx.draw_networkx_labels(graph, positions, font_size=10)
#输出关系图
plt.show()
#将寄件人和收件人名称规范化
emails.MetadataFrom = emails.MetadataFrom.apply(unify_name)
emails.MetadataTo = emails.MetadataTo.apply(unify_name)
#设置权重等于邮件来往次数
edges_weights_temp = defaultdict(list)
for row in zip(emails.MetadataFrom, emails.MetadataTo, 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()
#设置路径及权重
graph.add_weighted_edges_from(edges_weights)
#计算每个节点的PR值,并作为节点的PageRank属性
pagerank = nx.pagerank(graph)
#获取每个节点的PageRank数值
pagerank_list = {node:rank for node, rank in pagerank.items()}
#将pagerank数值作为节点的属性
nx.set_node_attributes(graph, name='pagerank', values=pagerank_list)
#画网络图
show_graph(graph)
#设置PR值的阈值,筛选大于阈值的重要核心节点
pagerank_threshold = 0.005
#复制一份计算好的网络图
small_graph = graph.copy()
#减掉PR值小于阈值的节点
for n, p_rank in graph.nodes(data=True):
if p_rank['pagerank'] < pagerank_threshold:
small_graph.remove_node(n)
#画网络图
show_graph(small_graph)