top of page
Search

Best teams by offensive statistics

This week we have been working on our main research question which is to determine the best teams in their defence/offence. We decided to use spider plot to answer this question. In this blog post is a prototype of such plot we are currently using.


In the first step we had to transform our data to be used in Vega. Data which we used to create a plot can be viewed here.


To created the plot we used following code in Vega:

{
 "$schema": "https://vega.github.io/schema/vega/v5.0.json",
 "width": 100,
 "height": 300,
 "title" : "Atlantic division offensive stats", 
 "config":{"background":"#ffffff"},
 "padding": 5,
 "signals": [
    {"name": "radius","value": 180},
    {"name": "radiusRange","value": [0,180]}
  ],
 "data": [{
 "name": "nba",
 "url": "https://gist.githubusercontent.com/kangaroo2020/02cc2ce84e2e2d4f40a7710f7894cd60/raw/58b45bd329ff6ae509b5416ab9ec56d94a765e7c/data_off2",
 "format": {
 "type": "csv",
 "parse": "auto"
        },
 "transform": [
      {"type": "filter", "expr": "datum.Division == 'CEN'"}
    ]
  },
    {
 "name": "folded",
 "source": "nba",
 "transform": [
        {
 "type": "fold",
 "fields": [
 "TwoPointShots",
 "ThreePointShotsAttempts",
 "ThreePointShotsAccuracy",
 "FreeThrowsAttempted",
 "FreeThrowsAccuracy",
 "Assists",
 "Opp-Steals",
 "Opp-Blocks",
 "Opp-Turnovers"
          ]
        }
      ]
    },
    {
 "name": "fields",
 "source": "folded",
 "transform": [
        {
 "type": "aggregate",
 "groupby": ["key"],
 "fields": ["value","value"],
 "ops": ["min","max"]
        }
      ]
    }
  ],
 "scales": [
    {
 "name": "angle",
 "type": "point",
 "range": [6.28,0],
 "padding": 0.5,
 "domain": {"data": "fields","field": "key"}
    },
    {
 "name": "color",
 "type": "ordinal",
 "domain": {"data": "nba","field": "Team","sort": true},
 "range": "category"
    },
    {
 "name": "TwoPointShots",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "TwoPointShots"}
    },
    {
 "name": "ThreePointShotsAttempts",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "ThreePointShotsAttempts"}
    },
    {
 "name": "ThreePointShotsAccuracy",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "ThreePointShotsAccuracy"}
    },

    {
 "name": "FreeThrowsAttempted",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "FreeThrowsAttempted"}
    },
    {
 "name": "FreeThrowsAccuracy",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "FreeThrowsAccuracy"}
    },
        {
 "name": "Assists",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "Assists"}
    },
    {
 "name": "Opp-Steals",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "Opp-Steals"}
    },
        {
 "name": "Opp-Blocks",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "Opp-Blocks"}
    },
    {
 "name": "Opp-Turnovers",
 "type": "linear",
 "range": {"signal": "radiusRange"},
 "zero": true,
 "nice": true,
 "domain": {"data": "nba","field": "Opp-Turnovers"}
    }
  ],
 "legends": [
    {
 "fill": "color",
 "orient": "none",
 "title": "Team",
 "encode": {
 "legend": {
 "update": {"x": {"value": 250},"y": {"value": -200}}
        }
      }
    }
  ],
 "marks": [
    {
 "type": "group",
 "from": {
 "facet": {
 "data": "folded",
 "name": "facet",
 "groupby": "Team"
        }
      },
 "marks": [
        {
 "name": "grid",
 "from": {"data": "fields"},
 "type": "rule",
 "encode": {
 "update": {
 "x": {
 "signal": "1 * radius * cos(scale('angle', datum.key))"
              },
 "y": {
 "signal": "1 * radius * sin(scale('angle', datum.key))"
              },
 "x2": {"value": 0},
 "y2": {"value": 0},
 "stroke": {"value": "black"},
 "strokeWidth" : {"value": 1 }
            }
          }
        },
        {
 "name": "label",
 "from": {"data": "fields"},
 "type": "text",
 "encode": {
 "update": {
 "x": {
 "signal": "1.2 * radius * cos(scale('angle', datum.key))"
              },
 "y": {
 "signal": "1.2 * radius * sin(scale('angle', datum.key))"
              },
 "baseline": {"value": "bottom"},
 "text": {"field": "key"},
 "align": {"value": "center"}
            }
          }
        },
        {
 "name": "scale",
 "from": {"data": "fields"},
 "type": "text",
 "encode": {
 "update": {
 "x": {
 "signal": "1.2 * radius * cos(scale('angle', datum.key))"
              },
 "y": {
 "signal": "1.2 * radius * sin(scale('angle', datum.key))"
              },
 "baseline": {"value": "top"},
 "text": {"signal": "'max of ' + format(datum.max_value, '.2f')"},
 "align": {"value": "center"},
 "fill": {"value": "#888"}
            }
          }
        },
        {
 "type": "line",
 "from": {"data": "facet"},
 "encode": {
 "enter": {
 "x": {
 "signal": "scale(datum.key, datum.value) * cos(scale('angle', datum.key))"
              },
 "y": {
 "signal": "scale(datum.key, datum.value) * sin(scale('angle', datum.key))"
              },
 "tooltip": {"signal": "'Team: ' + datum.Team + ', ' + datum.key + ' ' + datum.value"},
 "stroke": {"scale": "color","field": "Team"},
 "strokeWidth": {"value": 3},
 "interpolate": {"value": "linear-closed"},
 "fill": {"scale": "color", "field": "Team"},
 "fillOpacity": {"value": 0.05}
            }
          }
        }
      ]
    }
  ]
}

And the result of this code is as follows:




This plot is for offensive statististics for Atlantic division. NBA has 6 divisions and each of them has 5 teams and we plan to have a visual for each division separately for the start. We think that five teams is a reasonable number to have in one plot. When we determine the best team in each division we will make separate visual for the best 6 teams in order to determine best team based on offence.


To determine offensive power of the team we used 9 statististics which are the most important based on our judgment to evaluate quality of offense of the teams. Currently we continue to develop our visual in Holoviz in Jupyter notebook to add some nice selection boxes to switch among the divisions and between offensive and defensive stats. Unfortunately we still did not succeed in sharing our visual from Jupyter notebook to this blog. So we are only enclosing a screenshot from the web browser. In the picture there are possibilities to chose from the conference, division and defence/offence.





27 views0 comments
Post: Blog2_Post
bottom of page