机器学习竞赛分享:通用的团队竞技类的数据分析挖掘方法

前言

  1. 该篇分享来源于NFL竞赛官方的R语言版本,我做的主要是翻译为Python版本;
  2. 分享中用到的技巧、构建的特征、展示数据的方式都可以应用到其他领域,比如篮球、足球、LOL、双人羽毛球等等,只要是团队竞技,都可以从中获益;
  3. 分享基于kaggle上的NFL大数据碗,也就是基于橄榄球;
  4. 泰森多边形的概念最好可以去了解一下,可以不用纠结于公式,看看它对一些实际问题的抽象建模表示即可;

分享目的

言简意赅的分享下在团队竞技类问题中一些有用的数据可视化、分析方法,不同的领域下对数据的处理确实千差万别,每次遇到都深感自己的不足,幸好有各位大佬们的分享,跪谢;

分享目录

  1. 使用matplotlib对比赛实况进行绘制,直观理解某一时刻下的球场状态;
  2. 使用泰森多边形可视化各个球员的控制区域,借以理解、量化当前的形势;
  3. 结合球员们的当前位置、速度、加速度、方向等信息绘制行进路线图,可视化在N秒后的状态;
  4. 分享一篇去年关于足球球员控制区域热图相关的论文中的信息,这部分没有在项目里,大家感兴趣可以看看这里

竞赛链接

https://www.kaggle.com/c/nfl-big-data-bowl-2020

项目链接,该项目代码已经public,大家可以copy下来直接运行

https://www.kaggle.com/holoong9291/nfl-tracking-wrangling-voronoi-and-sonars-python

github仓库链接,更多做的过程中的一些思考、问题等可以在我的github中看到

https://github.com/NemoHoHaloAi/Competition/tree/master/kaggle/Top61%25-0.01404-zzz-NFL-Big-Data-Bowl

一些橄榄球相关的基本概念

  • 美式足球:进攻方目的是通过跑动、传球等尽快抵达对方半场,也就是达阵,而防守方的目的则是相反,尽全力去阻止对方的前进以及尽可能断球;
  • 球场长120码(109.728米),宽53码(48.768米),周长是361.992米;
  • 球员:双方场上共22人,进攻方11人,防守方11人,进攻方持球;
  • 进攻机会:进攻方共有四次机会,需要推进至少十码;
  • 进攻方:进攻方的职责是通过四次机会,尽可能的向前推进10码或者达阵,以获得下一个四次机会,否则就需要交出球权;
  • 防守方:防守方则是相反,尽可能的阻止对方前进,如果能够断球那更好,直接球权交换;
  • handoff:传球;
  • snap:发球;
  • 橄榄球基本知识点我了解
  • QB:四分卫,通常是发球后接球的那个人,一般口袋阵的中心,但是也不乏有像拉马尔-杰克逊这样的跑传结合的QB,目前古典QB代表是新英格兰爱国者NE汤姆-布雷迪
  • RB:跑卫,通常发球后进行冲刺、摆脱等,试图接住本方QB的传球后尽可能远的冲刺;

分享正式开始

绘制比赛实况

绘制的必要性:想象这样一种情况,我们拿到的都是比赛方的表格数据,不仅枯燥,而且不够直观,即便我们足够了解橄榄球,依然无法通过数据感受到场上紧张的氛围,进攻方的战术安排,防守方的防守计划等等,而这些实际上都是隐藏在数据中的,这就好像是玩LOL或者Dota(我个人两个都玩过,目前主要玩Dota),我给你十个英雄的坐标、移动速度、朝向、装备,你很难理解当前的情况,但是如果看看游戏中的小地图(假设小地图能看到全部10个英雄),我相信大部分玩家都能看出当前是在争夺肉山(抢大龙)、上高地、团战、局部团战等,因此绘制一个类似游戏中的小地图是非常有用的,会帮助我们更深刻的了解比赛;

绘制代码思路

  1. 区分进攻方和防守方,进攻方为红色,防守方为绿色(因为进攻方和防守方会交替,所以进攻方可能是球队A可能是球队B);
  2. 将持球人用黑色特别标示出来;
  3. 将橄榄球场特有的码线绘制出来,这一特点在篮球和足球中是没有的,不过球队半场的概念是通用的;
  4. 将得分线加粗绘制出来,得分线就是橄榄球中的TouchDown的区域,进攻方持球过了这条线得6分;

下面是相关代码:

plt.figure(figsize=(30, 15))
plt.suptitle("Sample plays, standardized, Offense moving left to right")
plt.xlabel("Distance from offensive team's own end zone")
plt.ylabel("Y coordinate")

i=1
for gp,chance in sample_chart_v2.groupby('PlayId'):
    play_id = gp
    rusher = chance[chance.NflId==chance.NflIdRusher].iloc[0]
    offense = chance[chance.IsOnOffense]
    defense = chance[~chance.IsOnOffense]

    plt.subplot(3,2,i)
    i+=1
    plt.xlim(0,120)
    plt.ylim(-10,63)

    plt.scatter(offense.X_std,offense.Y_std,marker='o',c='red',s=55,alpha=0.5,label='OFFENSE')
    plt.scatter(defense.X_std,defense.Y_std,marker='o',c='green',s=55,alpha=0.5,label='DEFENSE')
    plt.scatter([rusher.X_std],[rusher.Y_std],marker='o',c='black',s=30,label='RUSHER')

    for line in range(10,130,10):
        plt.plot([line,line],[-100,100],c='silver',linewidth=0.8,linestyle='-')

    plt.plot([rusher.YardsFromOwnGoal,rusher.YardsFromOwnGoal],[-100,100],c='black',linewidth=1.5,linestyle=':')
    plt.plot([10,10],[-100,100],c='black',linewidth=2)
    plt.plot([110,110],[-100,100],c='black',linewidth=2)

    plt.title(play_id)
    plt.legend()

plt.show()

下面是效果图:

可以看到,通常对比赛实况的可视化,可以清晰的看到当前处于哪个半场,距离达阵还有多远,进攻方、防守方的站位分别是怎样,持球人周围的队友、对手数量、距离等,这非常有利于后续的分析挖掘;

绘制动态比赛实况

绘制的目的:上面的绘制能看出是静态的,而且并没有用上球员的速度、加速度、面向、移动方向等数据,而我们知道球员总是处于不断运动当中的,他们的当前状态很重要,但是1s后,2s后可能更重要,这就是这一部分绘制的目的,强调每个球员在一段时间后的状态,当然,这部分绘制有一个前提假设,那就是球员当前的速度、加速度、面向、移动方向等信息在短时间内是不变的,这一点也符合实际情况(),当然绘制与现实会有一些出入,但是这些差异不影响我们分析比赛;

绘制的代码

plt.figure(figsize=(12, 8))
plt.suptitle("Playid:20170910001102")
plt.xlabel("Distance from offensive team's own end zone")
plt.ylabel("Y coordinate")

for gp,chance in sample_20170910001102.groupby('PlayId'):
    play_id = gp
    rusher = chance[chance.NflId==chance.NflIdRusher].iloc[0]
    offense = chance[chance.IsOnOffense]
    defense = chance[~chance.IsOnOffense]

    plt.subplot(1,1,1)
    i+=1

    x_min, x_max = chance.X_std.min()-5, chance.X_std.max()+5
    y_min, y_max = chance.Y_std.min()-5, chance.Y_std.max()+5
    plt.xlim(x_min,x_max)
    plt.ylim(y_min,y_max)

    plt.scatter(offense.X_std,offense.Y_std,marker='o',c='green',s=55,alpha=0.5,label='OFFENSE')
    plt.scatter(defense.X_std,defense.Y_std,marker='o',c='red',s=55,alpha=0.5,label='DEFENSE')
    plt.scatter([rusher.X_std],[rusher.Y_std],marker='o',c='black',s=30,label='RUSHER')

    for idx, row in chance.iterrows():
        _color='black' if row.IsBallCarrier else('green' if row.IsOnOffense else 'red')
        plt.arrow(row.X_std,row.Y_std,row.X_std_end-row.X_std,row.Y_std_end-row.Y_std,width=0.05,head_width=0.3,ec=_color,fc=_color)

    for line in range(10,130,10):
        plt.plot([line,line],[-100,100],c='silver',linewidth=0.8,linestyle='-')

    plt.plot([rusher.YardsFromOwnGoal,rusher.YardsFromOwnGoal],[-100,100],c='black',linewidth=1.5,linestyle=':')
    plt.plot([10,10],[-100,100],c='black',linewidth=2)
    plt.plot([110,110],[-100,100],c='black',linewidth=2)

    plt.title(play_id)
    plt.legend()

plt.show()

下面是效果图:

绘制球员的泰森多边形

绘制的必要性:百度百科定义点泰森多边形-冯洛诺伊图,简单理解就是在一个球场中,每个球员都是一个个不重合的点,那么将整个球场划分到这些点上,那么可以认为每个点都有自己的一片控制区域,这也经常用于狮群领土划分、机场划分等问题,抽象出来都是同一个问题;

泰森多边形的局限

  1. 没有考虑球员与球员的差异;
  2. 没有考虑球员的移动方向速度;
  3. 没有考虑球的位置和影响;

相对来说,泰森多边形是对这一类问题的简单抽象,没有考虑一些复杂因素,但是也揭示了很多信息;

绘制代码如下

from scipy.spatial import Voronoi

plt.figure(figsize=(12, 8))
plt.suptitle("Sample plays, standardized, Offense moving left to right")
plt.xlabel("Distance from offensive team's own end zone")
plt.ylabel("Y coordinate")

sample_20171120000963 = train_1[train_1.PlayId==20171120000963].copy()
for gp,chance in sample_20171120000963.groupby('PlayId'):
    play_id = gp
    rusher = chance[chance.NflId==chance.NflIdRusher].iloc[0]
    offense = chance[chance.IsOnOffense]
    defense = chance[~chance.IsOnOffense]

    plt.subplot(1,1,1)
    i+=1

    x_min, x_max = chance.X_std.min()-2, chance.X_std.max()+2
    y_min, y_max = chance.Y_std.min()-2, chance.Y_std.max()+2
    #plt.xlim(8,50) # 特定
    plt.xlim(x_min,x_max)
    #plt.ylim(5,40) # 特定
    plt.ylim(y_min,y_max)
    #plt.plot([x_min,x_min,x_max,x_max,x_min],[y_min,y_max,y_max,y_min,y_min],c='black',linewidth=1.5)

    vor = Voronoi(np.array([[row.X_std,row.Y_std] for index, row in chance.iterrows()]))
    regions, vertices = voronoi_finite_polygons_2d(vor)
    for region in regions:
        polygon = vertices[region]
        plt.plot(*zip(*polygon),c='black',alpha=0.8)

    plt.scatter(offense.X_std,offense.Y_std,marker='o',c='green',s=55,alpha=0.5,label='OFFENSE')
    plt.scatter(defense.X_std,defense.Y_std,marker='o',c='red',s=55,alpha=0.5,label='DEFENSE')
    plt.scatter([rusher.X_std],[rusher.Y_std],marker='o',c='black',s=30,label='RUSHER')

    for line in range(10,130,10):
        plt.plot([line,line],[-100,100],c='silver',linewidth=0.8,linestyle='-')

    plt.plot([rusher.YardsFromOwnGoal,rusher.YardsFromOwnGoal],[-100,100],c='black',linewidth=1.5,linestyle=':')
    plt.plot([10,10],[-100,100],c='black',linewidth=2)
    plt.plot([110,110],[-100,100],c='black',linewidth=2)

    plt.title(play_id)
    plt.legend()

plt.show()

运行效果图:

从该图中,能清晰的看到各个球员的控制区域,有一个量化因子是将这部分区域相加,量化每个球队的控制区域大小以及分布;

球员控制区域热图

这部分的分享目的:这部分分享来自这篇论文,我也还没看完,所以分享内容会比较少,简单概述一下。首先大家应该能看到泰森多边形的不足,首先它没有考虑速度等动态因素,其次它是针对每个球员而不是球队的,但是我们知道球队的信息更重要,因为这是团队竞技,因此缺乏对球员进行叠加的过程,而这些都是这篇论文重点探讨的地方;

  1. 论文以足球数据为基础,量化了某个时刻的球场控制热图,且考虑了球在其中的影响,注意此时还是假设每个球员的影响在球场中都是一个圆形区域:
  2. 但是理想状态每个球员的影响可能是圆可能是椭圆,这里我想象一个球员是一颗石子,如果垂直丢入水中(球员静置不动时),那么波纹就是一个圆形,如果是斜着抛入水中,那么波纹应该是一个与石子方向上的椭圆:
  3. 那么引入速度、方向后的球场控制热图,就应该是下面这样:

实际上这篇论文还有很多内容,且主要内容是关于如何量化球员影响区域的,也就是如何抽象为一些数学公式上,当然这部分我目前也算不上理解,所以处于外行看热闹的阶段,不过大家应该可以从中感受到数学建模的威力,以及这些东西的广泛应用,希望这篇分享能够帮到大家一点点;

最后

大家可以到我的Github上看看有没有其他需要的东西,目前主要是自己做的机器学习项目、Python各种脚本工具、数据分析挖掘项目以及Follow的大佬、Fork的项目等:
https://github.com/NemoHoHaloAi

原文地址:https://www.cnblogs.com/helongBlog/p/12218108.html

时间: 2024-08-02 13:02:52

机器学习竞赛分享:通用的团队竞技类的数据分析挖掘方法的相关文章

教你如何在机器学习竞赛中更胜一筹

更多技术干活请关注:阿里云云栖社区 - 汇聚阿里技术精粹 作者:Team Machine Learning,这是一个机器学习爱好者团队,他们热衷于建立一个有希望在数据科学/机器学习方面建立事业的有抱负的年轻毕业生和专业人士的环境. 介绍 机器学习很复杂.你可能会遇到一个令你无从下手的数据集,特别是当你处于机器学习的初期. 在这个博客中,你将学到一些基本的关于建立机器学习模型的技巧,大多数人都从中获得经验.这些技巧由Marios Michailidis(a.k.a Kazanova),Kaggle

JDBC:编写通用的 JDBCUtils工具类

基本上已经可以应付常用方法 1.为JDBCUtils 添加事务处理方法 2.处理多线程并发访问问题 package cn.cil.Utls; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 编写通用的 JDB

帧同步在竞技类网络游戏中的应用

帧同步在竞技类网络游戏中的应用 帧同步在网上可以搜的资料比较少,关于游戏的更是没有,不过,实现的原理也比较简单,最近几天就写了份关于帧同步的文档,当作给同事扫扫盲,顺便也在这里发发,可以给其他人参考参考     --竞技类网络游戏设计方案   一.        前言 帧同步,根据wiki百科的定义是,一种对同步源进行像素级同步显示的处理技术,对于网络上的多个接入者,一个信号将会通过主机同步发送给其他人,并同步显示在各个终端上.同步信号可以是每帧的像素数据,也可以是影响数据变化的关键事件信息.

通用的树型类,可以生成任何树型结构

<?php namespace Vendor\Tree; /** * 通用的树型类,可以生成任何树型结构 */ class Tree { /** * 生成树型结构所需要的2维数组 * @var array */ public $arr = array(); /** * 生成树型结构所需修饰符号,可以换成图片 * @var array */ public $icon = array('│', '├', '└'); public $nbsp = " "; private $str =

分享一个自定义的console类 让你不再纠结JS中的调试代码的兼容

分享一个自定义的console类 让你不再纠结JS中的调试代码的兼容 在写JS的过程中,为了调试我们常常会 写很多 console.log.console.info.console.group.console.warn.console.error代码来查看JS 的运行情况,但发布时又因为IE不支持console,又要去掉这些代码,一不小心就会出错 问题的产生 在写JS的过程中,为了调试我们常常会写很多 console.log.console.info.console.group.console.

分享一个PHP数据库分页类

本帖最后由 luenmicro 于 2014-11-12 23:19 编辑 分享一个PHP数据库分页类. [code]<?php    class page    {        private $pagesize;        private $lastpage;        private $totalpages;        private $nums;        private $numPage=1; function __construct($page_size,$tota

机器学习-KMeans聚类 K值以及初始类簇中心点的选取

[转]http://www.cnblogs.com/kemaswill/archive/2013/01/26/2877434.html 本文主要基于Anand Rajaraman和Jeffrey David Ullman合著,王斌翻译的<大数据-互联网大规模数据挖掘与分布式处理>一书. KMeans算法是最常用的聚类算法,主要思想是:在给定K值和K个初始类簇中心点的情况下,把每个点(亦即数据记录)分到离其最近的类簇中心点所代表的类簇中,所有点分配完毕之后,根据一个类簇内的所有点重新计算该类簇的

Effective Java 第三版——16.在公共类中使用访问方法而不是公共属性

Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化. 在这里第一时间翻译成中文版.供大家学习分享之用. 16. 在公共类中使用访问方法而不是公共属性 有时候,你可能会试图写一些退化的类(degenerate classes),除了集中实例属性之外别无用处: // Degene

Java面试08|Java重要的类和相关的方法

1.深入理解Class类及其中的方法 获取Class类的方法: 1.调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法.2.使用Class类的中静态forName()方法获得与字符串对应的Class对象.例如: Class c2=Class.forName("MyObject") // MyObject必须是接口或者类的名字 3.获取Class类型对象的第三个方法非常简单.如果T是一个Java类型,那么T.class就代表了匹配的类