mplsoccer.radar_chart module

A Python module for plotting radar-chart.

Authors: Anmol_Durgapal(@slothfulwave612) and Andrew Rowlinson (@numberstorm)

The radar-chart theme is inspired by StatsBomb/Rami_Moghadam.

class mplsoccer.radar_chart.Radar(params, min_range, max_range, lower_is_better=None, round_int=None, num_rings=4, ring_width=1, center_circle_radius=1)[source]

Bases: object

A class for plotting radar charts in Matplotlib

Parameters:
  • params (sequence of str) – The name of parameters (e.g. ‘Key Passes’)

  • min_range, max_range (sequence of floats) – Minimum and maximum range for each parameter (inner and outer edge of the Radar).

  • lower_is_better (sequence of str, default None) – Any params where it is better to have a lower statistic should be in this list. If any of the strings match the params the statistic will be flipped. In soccer, this is useful for parameters like miss-controls where fewer miss-controls is better than more. The default (None), which does not flip any statistic.

  • round_int (sequence of bool, default None) – Whether to round the respective range values to integers (if True) or floats (if False). The default (None) sets all range values to float.

  • num_rings (int, default 4) – The number of concentric circles. This excludes the center circle so the total number is num_rings + 1.

  • ring_width (float, default 1) – The width of the rings in data units.

  • center_circle_radius (float, default 1) – The radius of the center circle in data units. The defaults mean that the ring_width is the same size as the center circle radius. If the center_circle_radius is increased to more than the ring_width then the center circle radius is wider than the rings.

Methods

draw_circles([ax, inner])

Draw the radar chart's rings (concentric circles).

draw_param_labels([ax, wrap, offset])

Draw the parameter labels (e.g. 'Key Passes') on the edge of the chart.

draw_radar(values[, ax, kwargs_radar, ...])

Draw a single radar (polygon) and some outer rings clipped to the radar's shape.

draw_radar_compare(values, compare_values[, ...])

Draw a radar comparison chart showing two radars.

draw_radar_solid(values[, ax, kwargs])

Draw a single radar (polygon) without cliping to the rings.

draw_range_labels([ax, offset])

Draw the range labels.

setup_axis([facecolor, figsize, ax])

Set up an axis for plotting radar charts. If an ax is specified the settings are applied

spoke([ax])

Draw lines going from the center of the radar to the edge of the radar.

turbine(values, distribution_values[, ax, ...])

Draw the turbine blades (kernel density estimators).

setup_axis(facecolor='#FFFFFF', figsize=(12, 12), ax=None, **kwargs)[source]
Set up an axis for plotting radar charts. If an ax is specified the settings are applied

to an existing axis. This method equalises the aspect ratio, and sets the facecolor and limits.

Parameters:
  • facecolor (a Matplotlib color, default ‘#FFFFFF’) – The background color.

  • figsize (tuple of floats, default (12, 12)) – The figure size in inches (width, height).

  • ax (matplotlib axis, default None) – A matplotlib.axes.Axes to draw the pitch on. If None is specified the pitch is plotted on a new figure.

  • **kwargs (All other keyword arguments are passed on to plt.subplots.)

Returns:

  • If ax=None returns a matplotlib Figure and Axes.

  • Else the settings are applied on an existing axis and returns None.

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> from mplsoccer import Radar
>>> import matplotlib.pyplot as plt
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = plt.subplots(figsize=(12, 12))
>>> radar.setup_axis(ax=ax)
draw_circles(ax=None, inner=True, **kwargs)[source]

Draw the radar chart’s rings (concentric circles).

Parameters:
  • ax (matplotlib axis, default None) – The axis to plot on.

  • inner (bool, default True) – Whether to plot the inner rings (True) or outer rings (False)

  • **kwargs (All other keyword arguments are passed on to PatchCollection.)

Returns:

PatchCollection

Return type:

matplotlib.collections.PatchCollection

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
draw_radar_solid(values, ax=None, kwargs=None)[source]

Draw a single radar (polygon) without cliping to the rings.

Parameters:
  • values (sequence of floats) – A sequence of float values for each parameter.

  • ax (matplotlib axis, default None) – The axis to plot on.

  • **kwargs (All keyword arguments are passed on to Polygon (radar).)

Returns:

  • radar (Polygon : matplotlib.patches.Polygon) – The radar polygon.

  • vertices (a 2d numpy array (number of vertices, 2)) – The vertices of the radar polygon. Where the second dimension is the x, y coordinates.

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
>>> values = [5, 3, 10]
>>> radar_poly, vertices = radar.draw_radar_solid(values, ax=ax,
...                                               kwargs={'facecolor': '#00f2c1',
...                                                       'alpha': 0.6})
draw_radar(values, ax=None, kwargs_radar=None, kwargs_rings=None)[source]

Draw a single radar (polygon) and some outer rings clipped to the radar’s shape.

Parameters:
  • values (sequence of floats) – A sequence of float values for each parameter.

  • ax (matplotlib axis, default None) – The axis to plot on.

  • **kwargs_radar (All keyword arguments are passed on to Polygon (radar).)

  • **kwargs_rings (All keyword arguments are passed on to PatchCollection (rings).)

Returns:

  • radar (Polygon : matplotlib.patches.Polygon) – The radar polygon.

  • rings (PatchCollection : matplotlib.collections.PatchCollection) – The outer rings clipped to the radar polygon.

  • vertices (a 2d numpy array (number of vertices, 2)) – The vertices of the radar polygon. Where the second dimension is the x, y coordinates.

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
>>> values = [5, 3, 10]
>>> radar_poly, rings, vertices = radar.draw_radar(values, ax=ax,
...                                                kwargs_radar={'facecolor': '#00f2c1',
...                                                              'alpha': 0.6},
...                                                kwargs_rings={'facecolor': '#d80499',
...                                                              'alpha': 0.6})
draw_radar_compare(values, compare_values, ax=None, kwargs_radar=None, kwargs_compare=None)[source]

Draw a radar comparison chart showing two radars.

Parameters:
  • values (sequence of floats) – A sequence of float values for each parameter for the first radar.

  • compare_values (sequence of floats) – A sequence of float values for each parameter for the second radar.

  • ax (matplotlib axis, default None) – The axis to plot on.

  • **kwargs_radar (keyword arguments for the first radar are passed on to Polygon.)

  • **kwargs_compare (keyword arguments for the second radar are passed on to Polygon.)

Returns:

  • radar (Polygon : matplotlib.patches.Polygon) – The first radar polygon.

  • radar2 (Polygon : matplotlib.patches.Polygon) – The second radar polygon.

  • vertices (a 2d numpy array (number of vertices, 2)) – The vertices of the first radar. Where the second dimension is the x, y coordinates.

  • vertices2 (a 2d numpy array (number of vertices, 2)) – The vertices of the second radar. Where the second dimension is the x, y coordinates.

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
>>> values = [5, 3, 10]
>>> compare_values = [10, 4, 3]
>>> radar_output = radar.draw_radar_compare(values, compare_values, ax=ax,
...                                         kwargs_radar={'facecolor': '#00f2c1',
...                                                       'alpha': 0.6},
...                                         kwargs_compare={'facecolor': '#d80499',
...                                                         'alpha': 0.6})
draw_range_labels(ax=None, offset=0, **kwargs)[source]

Draw the range labels. These labels are linearly interpolated between min_range and max_range on the ring edges.

The range labels are formatted to 1 or 2, decimal places (dp), depending on whether the maximum of the range is less than or equal to one (1dp) or more than one (2dp). If round_int is True for the parameter, this is overriden so integers are shown instead.

Parameters:
  • ax (matplotlib axis, default None) – The axis to plot on.

  • offset (float, default 0) – Offset the range labels from the center of the rings.

  • **kwargs (All other keyword arguments are passed on to matplotlib.axes.Axes.text.)

Returns:

label_list

Return type:

list of matplotlib.text.Text

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
>>> values = [5, 3, 10]
>>> radar_poly, rings, vertices = radar.draw_radar(values, ax=ax,
...                                                kwargs_radar={'facecolor': '#00f2c1',
...                                                              'alpha': 0.6},
...                                                kwargs_rings={'facecolor': '#d80499',
...                                                              'alpha': 0.6})
>>> range_labels = radar.draw_range_labels(ax=ax)
draw_param_labels(ax=None, wrap=15, offset=1, **kwargs)[source]

Draw the parameter labels (e.g. ‘Key Passes’) on the edge of the chart.

Parameters:
  • ax (matplotlib axis, default None) – The axis to plot on.

  • offset (float, default 1) – Offset the param labels from the last of the rings.

  • wrap (int, default 15) – Wrap the labels so that every line is at most wrap characters long (long words are not broken).

  • **kwargs (All other keyword arguments are passed on to matplotlib.axes.Axes.text.)

Returns:

label_list

Return type:

list of matplotlib.text.Text

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
>>> values = [5, 3, 10]
>>> radar_poly, rings, vertices = radar.draw_radar(values, ax=ax,
...                                                kwargs_radar={'facecolor': '#00f2c1',
...                                                              'alpha': 0.6},
...                                                kwargs_rings={'facecolor': '#d80499',
...                                                              'alpha': 0.6})
>>> range_labels = radar.draw_range_labels(ax=ax)
>>> param_labels = radar.draw_param_labels(ax=ax)
spoke(ax=None, **kwargs)[source]

Draw lines going from the center of the radar to the edge of the radar.

Parameters:
  • ax (matplotlib axis, default None) – The axis to plot on.

  • **kwargs (All other keyword arguments are passed on to matplotlib.axes.Axes.plot.)

Return type:

A list of Line2D objects representing the plotted data.

Examples

>>> from mplsoccer import Radar
>>> radar = Radar(params=['Agility', 'Speed', 'Strength'], min_range=[0, 0, 0],
...               max_range=[10, 10, 10])
>>> fig, ax = radar.setup_axis()
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='#ffb2b2', edgecolor='#fc5f5f')
>>> values = [5, 3, 10]
>>> radar_poly, rings, vertices = radar.draw_radar(values, ax=ax,
...                                                kwargs_radar={'facecolor': '#00f2c1',
...                                                              'alpha': 0.6},
...                                                kwargs_rings={'facecolor': '#d80499',
...                                                              'alpha': 0.6})
>>> range_labels = radar.draw_range_labels(ax=ax)
>>> param_labels = radar.draw_param_labels(ax=ax)
>>> spokes = radar.spoke(ax=ax)
turbine(values, distribution_values, ax=None, scale=0.85, kwargs_inner=None, kwargs_inner_gradient=None, kwargs_outer=None)[source]

Draw the turbine blades (kernel density estimators).

Parameters:
  • values (sequence of floats) – A sequence of float values for each parameter. The turbine blades will be split in half at this value returning an inner and outer part.

  • distribution_values (an array of shape [number of observations, number or params]) – An array holding the values to plot as kernel density estimators (the turbine blades). If there are 500 people and 10 parameters (e.g. skills) the shape should be [500, 10]

  • ax (matplotlib axis, default None) – The axis to plot on.

  • scale (float, default 0.85) – The turbine blade width as a proportion of the ring_width. The default is that the blade widths are 85% of the ring_width of the radar.

  • **kwargs_inner (keyword arguments for the inner turbine blades passed to Polygon.)

  • **kwargs_inner_gradient (keyword arguments for the inner turbine blade (e.g. cmap)) – passed to imshow.

  • **kwargs_outer (keyword arguments for the outer turbine blades passed to PatchCollection.)

Returns:

  • blades_inner (list of matplotlib.patches.Polygon)

  • blades_inner_gradient (list of matplotlib.image.AxesImage)

  • blades_outer (matplotlib.collections.PatchCollection)

Examples

>>> import pandas as pd
>>> from mplsoccer import Radar
>>> import numpy as np
>>> import scipy.stats as stats
>>> np.random.seed(42)
>>> lower, upper, mu, sigma = 0, 1, 0.35, 0.25
>>> X = stats.truncnorm((lower - mu) / sigma, (upper - mu) / sigma, loc=mu, scale=sigma)
>>> values = X.rvs((1000, 11))
>>> params = ['Expected goals', 'Total shots',
...           'Touches in attacking penalty area', 'Pass completion %',
...           'Crosses into the 18-yard box (excluding set pieces)',
...           'Expected goals assisted', 'Fouls drawn', 'Successful dribbles',
...           'Successful pressures', 'Non-penalty expected goals per shot',
...           'Miscontrols/ Dispossessed']
>>> df = pd.DataFrame(values)
>>> df.columns = params
>>> df['player_name'] = np.arange(1000)
>>> low = df[params].quantile(0.05).values
>>> high = df[params].quantile(0.95).values
>>> radar = Radar(params, low, high, num_rings=4)
>>> player_values = df.loc[df.player_name == 23, params].values[0]
>>> fig, ax = radar.setup_axis()
>>> turbine_output = radar.turbine(player_values, df[params].values, ax=ax,
...                                kwargs_inner={'edgecolor': 'black'},
...                                kwargs_inner_gradient={'cmap': 'Blues'},
...                                kwargs_outer={'facecolor': '#b2b2b2',
...                                'edgecolor': 'black'})
>>> rings_inner = radar.draw_circles(ax=ax, facecolor='None',
...                                  edgecolor='black', linestyle='--')
>>> range_labels = radar.draw_range_labels(ax=ax, fontsize=15, zorder=2)
>>> param_labels = radar.draw_param_labels(ax=ax, fontsize=15, zorder=2)