龙空技术网

使用加权PageRank发现最好的网球运动员

今天无bug 287

前言:

当前大家对“pagerank可视化”都比较关注,朋友们都需要分析一些“pagerank可视化”的相关知识。那么小编也在网摘上搜集了一些关于“pagerank可视化””的相关内容,希望看官们能喜欢,朋友们快快来了解一下吧!

原文链接:

最新版本的Neo4j图形算法库对PageRank算法增加了权重变量的支持。

我的同事Ryan()最近发表的一篇论文《谁是有史以来最好的网球运动员?基于职业网球历史的复杂网络分析》(),在这篇论文中,他使用了一种PageRank的变种算法,于是我就在想,我也是一名网球爱好者,我是不是也可以基本这种算法去做点什么呢?

我原计划要做一些数据的抓取,但是Kevin Lin已经做了一些比较困难的工作,他将到2017年底的所有比赛结果都以csv文件的形式放到了Github《atp-world-tour-tennis-data》()上.

感谢Kevin的付出。

数据导入

在导入数据之前,我们先创建一些约束,以避免导入一些重复的数据:

CREATE constraint on (p:Player)ASSERT p.id is unique;CREATE constraint on (m:Match)ASSERT m.id is unique;

接下来我们将把数据导入到Neo4j中。先把Kevin创建的CSV文件拷贝到Neo4j的import目录下。

完成之后我们就可以使用Cypher的LOAD CSV命令将数据导入到Neo4j里了。

LOAD CSV FROM "" AS rowMERGE (winner:Player {id: row[8]}) ON CREATE SET winner.name = row[7]MERGE (loser:Player {id: row[11]}) ON CREATE SET loser.name = row[10]MERGE (m:Match {id: row[22]})SET m.score = row[15], m.year = toInteger(split(row[0], "-")[0])MERGE (m)-[w:WINNER]->(winner) SET w.seed = toInteger(row[13])MERGE (m)-[l:LOSER]->(loser) SET l.seed = toInteger(row[14]);LOAD CSV FROM "" AS rowMERGE (winner:Player {id: row[8]})ON CREATE SET winner.name = row[7]MERGE (loser:Player {id: row[11]})ON CREATE SET loser.name = row[10]MERGE (m:Match {id: row[22]})SET m.score = row[15], m.year = toInteger(split(row[0], "-")[0])MERGE (m)-[w:WINNER]->(winner) SET w.seed = toInteger(row[13])MERGE (m)-[l:LOSER]->(loser) SET l.seed = toInteger(row[14]);LOAD CSV FROM "" AS rowMERGE (winner:Player {id: row[8]}) ON CREATE SET winner.name = row[7]MERGE (loser:Player {id: row[11]}) ON CREATE SET loser.name = row[10]MERGE (m:Match {id: row[22]})SET m.score = row[15], m.year = toInteger(split(row[0], "-")[0])MERGE (m)-[w:WINNER]->(winner) SET w.seed = toInteger(row[13])MERGE (m)-[l:LOSER]->(loser) SET l.seed = toInteger(row[14]);

这个模型非常简单,可以运行下面的请求看到可视化的描述:

CALL db.schema()

可以看到:

看起来不错。在继续写之前,我们先写个简单查询来看一下数据情况:

MATCH p=()<-[:LOSER]-()-[r:WINNER]->() RETURN p LIMIT 25

获胜最多的选手

现在,我们想看一下获胜最多选手,这个语句要怎么写呢?

MATCH (p:Player)WITH p,  size((p)<-[:WINNER]-()) AS wins,  size((p)<-[:LOSER]-()) as defeatsRETURN p.name, wins, defeats,  CASE WHEN wins+defeats = 0 THEN 0  ELSE (wins * 100.0) / (wins + defeats) END AS percentageWinsORDER BY wins DESCLIMIT 10

运行上面的语句,将看到下面的输出:

如果你也是一名网球迷,你可能认识这份名单中的大部分名字。他们中的大部分都被认为是有史以来最优秀的球员,但是仅仅计算赢球的场数好像并不太严谨。

此时,似乎我们可以试试更高级的方法---PageRank算法.....

建立一个可信的投影图

通过一个结点的入口关系来决定这个结点的可信度,这就是PageRank算法的工作原理。例如,在网络的世界里,一个网页通过链接到另一个网页,而给他带来可信度。这个可信度可以通过这个关系的权重属性来决定。

在我们的网球世界里,运动员的可信度则由他们彼此之间比较的胜负数来决定。例如,下面的查询显示了费德勒和纳达尔相互赢了多少次。

MATCH (p1:Player {name: "Roger Federer"}), (p2:Player {name: "Rafael Nadal"})RETURN p1.name, p2.name,  size((p1)<-[:WINNER]-()-[:LOSER]->(p2)) AS p1Wins,  size((p1)<-[:LOSER]-()-[:WINNER]->(p2)) AS p2Wins

运行输出结果如下:

我们的投影图应该在费德勒和纳达尔之间建立直接关系,用权重表示他们互相之间比赛赢的次数。从费德勒到纳达尔的关系上权重是23,表示费德勒赢了纳达尔23次。而纳达尔到费德勒的有关系上权重就是15.

我们写下面的查询语句将投影出这个图:

MATCH (p1)<-[:WINNER]-(match)-[:LOSER]->(p2) WHERE p1.name IN ["Roger Federer", "Rafael Nadal"]AND p2.name IN ["Roger Federer", "Rafael Nadal"]RETURN p2.name AS source, p1.name AS target, count(*) as weightLIMIT 10

这个查询的输出结果如下所示:

接下来我们要做的是删除WHERE条件,使这个查询可以在全图中进行。

使用加权PageRank来发现最好的网球运动员

现在我们通过PageRank算法的weightProperty参数来调用加权PageRank算法。默认情况下,PageRank算法是非加权模式。

下面的语句即是在全图上运行加权PageRank算法:

CALL algo.pageRank.stream( "MATCH (p:Player) RETURN id(p) AS id", "MATCH (p1)<-[:WINNER]-(match)-[:LOSER]->(p2)  RETURN id(p2) AS source, id(p1) AS target, count(*) as weight ", {graph:"cypher", weightProperty: "weight"})YIELD nodeId, scoreRETURN algo.getNodeById(nodeId).name AS player, scoreORDER BY score DESCLIMIT 10

其运行结果如下:

我们看到,我们排名的头部与Filippo Radicchi论文的排名是不一样的,主要区别是费德勒,纳达尔和德约科维奇进入了前五。这是因为Radicchi的分析仅到2010年,而这三名球员在此后的8年时间里一直非常优秀,所以,这也就是我们排名不太一样的原因。

我们可以模板仅包括2010年之前的比赛,则如下查询语句:

CALL algo.pageRank.stream( "MATCH (p:Player) RETURN id(p) AS id", "MATCH (p1)<-[:WINNER]-(match)-[:LOSER]->(p2)  WHERE match.year <= $year RETURN id(p2) AS source, id(p1) AS target, count(*) as weight ", {graph:"cypher", weightProperty: "weight", params: {year: 2010}})YIELD nodeId, scoreRETURN algo.getNodeById(nodeId).name AS player, scoreORDER BY score DESCLIMIT 10

运行效果如下:

注意,在这个查询中,我们将年份值通过params键作为参数传递到Cypher投影查询中。

我们排行榜的前两名现在与Radicche的一样了,但是费德勒目前在第三位并不是Radicche的榜单中是第七位,同时纳达尔和德约科维奇在我们榜单中已经排到前十之外了。

我们还可能查询某一个比赛的PageRank排名,下面的查询是2017年的PageRank排名

CALL algo.pageRank.stream( "MATCH (p:Player) RETURN id(p) AS id", "MATCH (p1)<-[:WINNER]-(match)-[:LOSER]->(p2) WHERE match.year = $year  RETURN id(p2) AS source, id(p1) AS target, count(*) as weight ", {graph:"cypher", weightProperty: "weight", params: {year: 2017}})YIELD nodeId, scoreRETURN algo.getNodeById(nodeId).name AS player, scoreORDER BY score DESCLIMIT 10

运行效果如下:

下图是2017年ATP世界巡回赛的年终排名

这个排名与我们的排名完全不同!这是什么原因呢?这是因为官方排名为每场比赛都给予了不同的权重,而我们的PageRank排名在每场比赛上给予的是相同权重。

好,关于使用加权PageRank找史上最优化网球选手的问题就介绍到这,我期待着看到更多人使用加权PageRank去解决其他问题。如是你也用了,请告诉我devrel@neo4j.com

Enjoy!

译者言:作者仅从应用角度介绍了如果实现这个事,完成没有介绍algo.pageRank.stream这个方法各个参数的功能,以后有空找到相关文章再给大家介绍。

标签: #pagerank可视化