[译]剖析勇士如何成为新赛季夺冠热门:基于Spark GraphFrames的金州勇士传球网络分析...

rank

databricks 最近发布了 GraphFrames,这是一个用 DataFrames 封装图处理过程的Spark插件。

我评估了网络分析并且利用丰富的NBA.com的数据对金州勇士的传球网络进行可视化。

head

金州勇士的传球网络

传接球

联盟 MVP Stephen Curry 接到了大多数的传球,而团队中的 MVP Draymond Green则发动了最多的传球。

我们已经看到大多数的进攻是由 Curry 和 Green 的相互传球开始的。

图片来自 GIPHY

入度 inDegree

idinDegree
CurryStephen3993
GreenDraymond3123
ThompsonKlay2276
LivingstonShaun1925
IguodalaAndre1814
BarnesHarrison1241
BogutAndrew1062
BarbosaLeandro946
SpeightsMarreese826
ClarkIan692
RushBrandon685
EzeliFestus559
McAdooJames Michael182
VarejaoAnderson67
LooneyKevon22

出度 outDegree

idoutDegree
GreenDraymond3841
CurryStephen3300
IguodalaAndre1896
LivingstonShaun1878
BogutAndrew1660
ThompsonKlay1460
BarnesHarrison1300
SpeightsMarreese795
RushBrandon772
EzeliFestus765
BarbosaLeandro758
ClarkIan597
McAdooJames Michael261
VarejaoAnderson94
LooneyKevon36

标签传递算法 (Label Propagation Algorithm)

标签传递是一种在图网络中寻找队伍的算法。
这种算法在没有已有标签的情况下,依然可以很好地将球员分为前锋和后卫。

名字标签
Thompson, Klay3
Barbosa, Leandro3
Curry, Stephen3
Clark, Ian3
Livingston, Shaun3
Rush, Brandon7
Green, Draymond7
Speights, Marreese7
Bogut, Andrew7
McAdoo, James Michael7
Iguodala, Andre7
Varejao, Anderson7
Ezeli, Festus7
Looney, Kevon7
Barnes, Harrison7

网页排名算法 (Pagerank Algorithm)

在一个网络中 PageRank 可以检测节点的重要程度。
毫无疑问,Stephen Curry、 Draymond Green 和 Klay Thompson 是Top3.
这个算法可以发现 Shaun Livingston 和 Andre Iguodala 在金州勇士的传球中扮演着关键角色。

namepagerank
Curry, Stephen2.17
Green, Draymond1.99
Thompson, Klay1.34
Livingston, Shaun1.29
Iguodala, Andre1.21
Barnes, Harrison0.86
Bogut, Andrew0.77
Barbosa, Leandro0.72
Speights, Marreese0.66
Clark, Ian0.59
Rush, Brandon0.57
Ezeli, Festus0.48
McAdoo, James Michael0.27
Varejao, Anderson0.19
Looney, Kevon0.16

示例

library(networkD3)

setwd('/Users/yuki/Documents/code_for_blog/gsw_passing_network')
passes <- read.csv("passes.csv")
groups <- read.csv("groups.csv")
size <- read.csv("size.csv")

passes$source <- as.numeric(as.factor(passes$PLAYER))-1
passes$target <- as.numeric(as.factor(passes$PASS_TO))-1
passes$PASS <- passes$PASS/50

groups$nodeid <- groups$name
groups$name <- as.numeric(as.factor(groups$name))-1
groups$group <- as.numeric(as.factor(groups$label))-1
nodes <- merge(groups,size[-1],by="id")
nodes$pagerank <- nodes$pagerank^2*100


forceNetwork(Links = passes,
             Nodes = nodes,
             Source = "source",
             fontFamily = "Arial",
             colourScale = JS("d3.scale.category10()"),
             Target = "target",
             Value = "PASS",
             NodeID = "nodeid",
             Nodesize = "pagerank",
             linkDistance = 350,
             Group = "group", 
             opacity = 0.8,
             fontSize = 16,
             zoom = TRUE,
             opacityNoHover = TRUE)

example

  • 节点大小: pagerank值

  • 节点颜色: 队伍

  • 连线宽度: 传球次数(接球和发球)

工作流

调用API

我使用 playerdashptpass 的端点并且将同队所有球员数据保存到本地的 JSON 文件中。
数据来自 2015-16赛季的传球记录。

# 金州勇士球员 IDs
playerids = [201575,201578,2738,202691,101106,2760,2571,203949,203546,
203110,201939,203105,2733,1626172,203084]

# 调用 API 并且存储结果为 JSON
for playerid in playerids:
    os.system('curl "http://stats.nba.com/stats/playerdashptpass?'
        'DateFrom=&'
        'DateTo=&'
        'GameSegment=&'
        'LastNGames=0&'
        'LeagueID=00&'
        'Location=&'
        'Month=0&'
        'OpponentTeamID=0&'
        'Outcome=&'
        'PerMode=Totals&'
        'Period=0&'
        'PlayerID={playerid}&'
        'Season=2015-16&'
        'SeasonSegment=&'
        'SeasonType=Regular+Season&'
        'TeamID=0&'
        'VsConference=&'
        'VsDivision=" > {playerid}.json'.format(playerid=playerid))

JSON -> Panda’s DataFrame

接着,我结合每个JSON文件到一个 DataFrame 中。

raw = pd.DataFrame()
for playerid in playerids:
    with open("{playerid}.json".format(playerid=playerid)) as json_file:
        parsed = json.load(json_file)['resultSets'][0]
        raw = raw.append(
            pd.DataFrame(parsed['rowSet'], columns=parsed['headers']))

raw = raw.rename(columns={'PLAYER_NAME_LAST_FIRST': 'PLAYER'})

raw['id'] = raw['PLAYER'].str.replace(', ', '')

准备节点和边

你需要为 Spark 中的 GraphFrames 准备一个像点+边的特殊的数据格式。顶点表示了图中的节点和运动员ID,边表示节点之间的关系。你可以添加一些附加特征比如权重,但是你没法找出在稍后的分析中可以更好表现的特征。一个可行的办法是尝试穷举所有的可能方案。(也欢迎大家留言讨论)

# 生成初始节点
pandas_vertices = raw[['PLAYER', 'id']].drop_duplicates()
pandas_vertices.columns = ['name', 'id']

# 生成初始边
pandas_edges = pd.DataFrame()
for passer in raw['id'].drop_duplicates():
    for receiver in raw[(raw['PASS_TO'].isin(raw['PLAYER'])) &
     (raw['id'] == passer)]['PASS_TO'].drop_duplicates():
        pandas_edges = pandas_edges.append(pd.DataFrame(
            {'passer': passer, 'receiver': receiver
            .replace(  ', ', '')}, 
            index=range(int(raw[(raw['id'] == passer) &
             (raw['PASS_TO'] == receiver)]['PASS'].values))))

pandas_edges.columns = ['src', 'dst']

图分析

vertices = sqlContext.createDataFrame(pandas_vertices)
edges = sqlContext.createDataFrame(pandas_edges)

# Analysis part
g = GraphFrame(vertices, edges)
print("vertices")
g.vertices.show()
print("edges")
g.edges.show()
print("inDegrees")
g.inDegrees.sort('inDegree', ascending=False).show()
print("outDegrees")
g.outDegrees.sort('outDegree', ascending=False).show()
print("degrees")
g.degrees.sort('degree', ascending=False).show()
print("labelPropagation")
g.labelPropagation(maxIter=5).show()
print("pageRank")
g.pageRank(resetProbability=0.15, tol=0.01).vertices.sort(
    'pagerank', ascending=False).show()

网络可视化

当你运行 GitHub 仓库中的代码 gsw_passing_network.py,你需要检查在工作目录下有 passes.csvgroups.csvsize.csv 这三个文件。我用R中的networkD3包来实现酷炫的可交互的 D3 制图。

library(networkD3)

setwd('/Users/yuki/Documents/code_for_blog/gsw_passing_network')
passes <- read.csv("passes.csv")
groups <- read.csv("groups.csv")
size <- read.csv("size.csv")

passes$source <- as.numeric(as.factor(passes$PLAYER))-1
passes$target <- as.numeric(as.factor(passes$PASS_TO))-1
passes$PASS <- passes$PASS/50

groups$nodeid <- groups$name
groups$name <- as.numeric(as.factor(groups$name))-1
groups$group <- as.numeric(as.factor(groups$label))-1
nodes <- merge(groups,size[-1],by="id")
nodes$pagerank <- nodes$pagerank^2*100


forceNetwork(Links = passes,
             Nodes = nodes,
             Source = "source",
             fontFamily = "Arial",
             colourScale = JS("d3.scale.category10()"),
             Target = "target",
             Value = "PASS",
             NodeID = "nodeid",
             Nodesize = "pagerank",
             linkDistance = 350,
             Group = "group", 
             opacity = 0.8,
             fontSize = 16,
             zoom = TRUE,
             opacityNoHover = TRUE)

参考资料

本文已获得原作者:YUKI KATOH 授权HarryZhu翻译
英文原文地址:http://opiateforthemass.es/ar...

作为分享主义者(sharism),本人所有互联网发布的图文均遵从CC版权,转载请保留作者信息并注明作者 Harry Zhu 的 FinanceR专栏:https://segmentfault.com/blog...,如果涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio
商业使用请联系作者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值