Radar charts are a very popular way of visualising multiple features for a single observation(you may plot multiple observations on the same radar but that breaks eveything really quick). Follow along and I'll show you how to create your own in Plotly and some other things you should keep in mind.
The method for creating said radars is pretty intuitive and there's surprisingly less code. Here's a an overview of the steps below:
1. Imports
2. Selecting the columns we need for the radars
3. Choosing the templates for different positions
4. Defining the update_plot
function to allow us to interact with the radar using the widgets.
import plotly as py
import plotly.graph_objs as go
import ipywidgets as widgets
import plotly.express as px
import pandas as pd
py.offline.init_notebook_mode(connected=True)
cols = ['Player', 'Def duels per 90 rank',
'Def duels won rank',
'Interceptions per 90 rank', 'Non-penalty goals per 90 rank',
'Shots per 90 rank',
'Crosses rank', 'Succ Crosses rank', 'Succ Dribb rank', 'Progressive runs per 90 rank',
'Passes acc. %', 'Succ Pass rank',
'xA rank','Key passes per 90 rank',
'Through p90 rank', 'Comp DP rank',
'Comp DC rank'] ##These are the columns I decided I needed. You may choose more or less
df = pd.read_excel(r"C:\\Users\ADMIN\Desktop\Abhishek\James_Wingers.xlsx", usecols = cols)
df.head()
Player | Def duels per 90 rank | Def duels won rank | Interceptions per 90 rank | Non-penalty goals per 90 rank | Shots per 90 rank | Crosses rank | Succ Crosses rank | Succ Dribb rank | Progressive runs per 90 rank | Passes acc. % | Succ Pass rank | xA rank | Key passes per 90 rank | Through p90 rank | Comp DP rank | Comp DC rank | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | S. Ferguson | 24.1 | 20.8 | 45.0 | 10.9 | 2.1 | 40.00 | 94.5 | 16.4 | 8.7 | 71.68 | 29.6 | 95.6 | 79.1 | 28.5 | 5.4 | 94.5 |
1 | S. Downing | 5.4 | 25.2 | 58.2 | 19.7 | 53.8 | 37.50 | 91.2 | 75.8 | 65.9 | 78.95 | 74.7 | 71.4 | 84.6 | 61.5 | 56.0 | 93.4 |
2 | J. Wallace | 60.4 | 82.4 | 26.3 | 35.1 | 52.7 | 33.21 | 97.8 | 73.6 | 82.4 | 66.93 | 61.5 | 50.5 | 59.3 | 68.1 | 71.4 | 97.8 |
3 | A. Armstrong | 6.5 | 24.1 | 9.8 | 53.8 | 79.1 | 26.77 | 63.7 | 71.4 | 81.3 | 71.63 | 26.3 | 63.7 | 54.9 | 42.8 | 73.6 | 63.7 |
4 | B. Celina | 1.0 | 6.5 | 6.5 | 49.4 | 89.0 | 30.77 | 28.5 | 52.7 | 92.3 | 85.21 | 95.6 | 84.6 | 64.8 | 94.5 | 94.5 | 28.5 |
I've included a part of the dataset here (graciously provided to me by James Socik) so that you can follow along. The dataset is in tidy format which essentially means that every observation is a row and every column is a feature. For us, the observations are the players, and the features the stats for the particular player. The plotting will most definitely work for other not-so-clean formats as well - you'll have to make some minor tweaks to the data-shaping bit below.
def update_plot(player, template):
"""
This function updates the plot everytime a widget is changed
"""
x = df.loc[df['Player'] == player]
if template == "Striker":
columns =['Non-penalty goals per 90 rank',
'Shots per 90 rank',
'Key passes per 90 rank',
'Passes acc. %',
'Succ Dribb rank',
'Progressive runs per 90 rank',
'Def duels per 90 rank']
elif template == "Attacking_mid":
columns= ['Non-penalty goals per 90 rank',
'Shots per 90 rank',
'Key passes per 90 rank',
'xA rank',
'Passes acc. %',
'Succ Dribb rank',
'Progressive runs per 90 rank',
'Def duels per 90 rank',
'Through p90 rank',
'Comp DP rank']
elif template == "Central_mid":
columns= ['Shots per 90 rank',
'Key passes per 90 rank',
'xA rank',
'Succ Dribb rank',
'Progressive runs per 90 rank',
'Through p90 rank',
'Passes acc. %',
'Comp DP rank',
'Def duels per 90 rank',
'Interceptions per 90 rank']
elif template == "Full_back":
columns= ['Key passes per 90 rank',
'xA rank',
'Succ Dribb rank',
'Progressive runs per 90 rank',
'Crosses rank',
'Succ Crosses rank',
'Through p90 rank',
'Passes acc. %',
'Def duels per 90 rank',
'Interceptions per 90 rank',
'Comp DC rank']
elif template == "Central_defender":
columns= ['Progressive runs per 90 rank',
'Passes acc. %',
'Through p90 rank',
'Def duels per 90 rank',
'Interceptions per 90 rank']
stats = x[columns] ## Some data-wrangling to get the data in the format we want
stats = stats.T
stats["theta"] = stats.index
stats.reset_index(drop=True, inplace=True)
stats.columns = ["r","theta"]
##Plotting
fig = px.line_polar(stats, r='r', theta='theta', line_close=True)
fig.update_traces(fill='toself', fillcolor = "rgba(29,130,65,0.5)")
fig.update_layout(
title=go.layout.Title(
text="Player Radar - {}".format(player)
),
polar = dict(radialaxis = dict(visible=True, range=[0, 100])), ##ensuring range is always fixed
annotations=[
go.layout.Annotation(
x=0.95,
y=-0.1,
showarrow=False,
text="All ranks in league percentile",
xref="paper",
yref="paper",
font=dict(
family="Courier New, monospace",
size=16,
color="#ffffff"
),
bordercolor="#c7c7c7",
borderwidth=2,
borderpad=4,
bgcolor="rgba(45, 197, 247,0.9)",
opacity=0.8
)]
)
py.offline.iplot(fig)
player = widgets.Dropdown(options=sorted(list(df["Player"])), description='Player List')
template = widgets.Dropdown(options= ["Striker", "Attacking_mid","Central_mid", "Full_back", "Central_defender"], description = "Template")
widgets.interactive(update_plot, player=player, template = template) ##linking the widgets above to the plotting function
interactive(children=(Dropdown(description='Player List', options=('A. Adomah', 'A. Armstrong', 'A. Browne', '…
I used Ashwin Raman's blogpost on bar plots to come up with the different templates. That's because I wasn't sure about it myself. Feel free to change them around. After that, I transposed the dataframe and renamed the columns to ease understanding. There might be a better way - a numpy way, but I'm new to Plotly and wanted to progress according to the manual.
The post would be incomplete without including this tweet from Luke Bonn on why you should not use radar charts. It talks about the importance of the ordering of the variables and that's something you should be very careful with as well. Other than that, radars have also been criticised for the non-linear relation between area of the radar and values along the axes. Check out Cleveland's Hierarchy - a research done which attempted to learn more about the perception of the different graphical representaions amongst the general population.
Nonetheless, their popularity far outweighs their criticism and radar charts might be here to stay for a while.
Any questions or concerns, feel free to text me on Twitter