Skip to content

Commit 4b71143

Browse files
Merge pull request #64 from rithwikrajendran/post-usage-viz-batteries
Visualising Usage Rates and more using Batteries
2 parents 7ed6af3 + d572e44 commit 4b71143

File tree

6 files changed

+220
-0
lines changed

6 files changed

+220
-0
lines changed
322 KB
Loading
128 KB
Loading
5.11 KB
Loading
53.4 KB
Loading
20.9 KB
Loading
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
---
2+
title: "Battery Charts - Visualise usage rates & more"
3+
date: 2021-08-19T16:52:58+05:30
4+
draft: false
5+
description: A tutorial on how to show usage rates and more using batteries
6+
categories: ["tutorials"]
7+
displayInList: true
8+
author: Rithwik Rajendran
9+
resources:
10+
- name: featuredImage
11+
src: "Liverpool_Usage_Chart.png"
12+
params:
13+
description: "my image description"
14+
showOnTop: true
15+
16+
---
17+
18+
# Introduction
19+
20+
I have been creating common visualisations like scatter plots, bar charts, beeswarms etc. for a while and thought about doing something different. Since I'm an avid football fan, I thought of ideas to represent players' usage or involvement over a period (a season, a couple of seasons). I have seen some cool visualisations like donuts which depict usage and I wanted to make something different and simple to understand. I thought about representing batteries as a form of player usage and it made a lot of sense.
21+
22+
For players who have been barely used (played fewer minutes) show a ***large amount of battery*** present since they have enough energy left in the tank. And for heavily used players, do the opposite i.e. show ***drained or less amount of battery***
23+
24+
So, what is the purpose of a battery chart? You can use it to show usage, consumption, involvement, fatigue etc. (anything usage related).
25+
26+
The image below is a sample view of how a battery would look in our figure, although a single battery isn't exactly what we are going to recreate in this tutorial.
27+
28+
![A sample visualisation](battery.png)
29+
30+
# Tutorial
31+
32+
Before jumping on to the tutorial, I would like to make it known that the function can be tweaked to fit accordingly depending on the number of subplots or any other size parameter. Coming to the figure we are going to plot, there are a series of steps that is to be considered which we will follow one by one. The following are those steps:-
33+
34+
1. Outlining what we are going to plot
35+
2. Import necessary libraries
36+
3. Write a function to draw the battery
37+
- This is the function that will be called to plot the battery chart
38+
4. Read the data and plot the chart accordingly
39+
- We will demonstrate it with an example
40+
41+
42+
## <span style="text-decoration: underline">Plot Outline</span>
43+
44+
What is our use case?
45+
46+
- We are given a dataset where we have data of Liverpool's players and their minutes played in the last 2 seasons (for whichever club they for played in that time period). We will use this data for our visualisation.
47+
- The final visualisation is the featured image of this blog post. We will navigate step-by-step as to how we'll create the visualisation.
48+
49+
## <span style="text-decoration: underline">Importing Libraries</span>
50+
51+
The first and foremost part is to import the essential libraries so that we can leverage the functions within. In this case, we will import the libraries we need.
52+
53+
```python
54+
import pandas as pd
55+
import matplotlib.pyplot as plt
56+
from matplotlib.path import Path
57+
from matplotlib.patches import FancyBboxPatch, PathPatch, Wedge
58+
```
59+
60+
The functions imported from `matplotlib.path` and `matplotlib.patches` will be used to draw lines, rectangles, boxes and so on to display the battery as it is.
61+
62+
## <span style="text-decoration: underline">Drawing the Battery - A function</span>
63+
64+
The next part is to define a function named `draw_battery()`, which will be used to draw the battery. Later on, we will call this function by specifying certain parameters to build the figure as we require. The following below is the code to build the battery -
65+
66+
```python
67+
def draw_battery(fig, ax, percentage=0, bat_ec="grey",
68+
tip_fc="none", tip_ec="grey",
69+
bol_fc="#fdfdfd", bol_ec="grey", invert_perc=False):
70+
'''
71+
Parameters
72+
----------
73+
fig : figure
74+
The figure object for the plot
75+
ax : axes
76+
The axes/axis variable of the figure.
77+
percentage : int, optional
78+
This is the battery percentage - size of the fill. The default is 0.
79+
bat_ec : str, optional
80+
The edge color of the battery/cell. The default is "grey".
81+
tip_fc : str, optional
82+
The fill/face color of the tip of battery. The default is "none".
83+
tip_ec : str, optional
84+
The edge color of the tip of battery. The default is "grey".
85+
bol_fc : str, optional
86+
The fill/face color of the lighning bolt. The default is "#fdfdfd".
87+
bol_ec : str, optional
88+
The edge color of the lighning bolt. The default is "grey".
89+
invert_perc : bool, optional
90+
A flag to invert the percentage shown inside the battery. The default is False
91+
92+
Returns
93+
-------
94+
None.
95+
96+
'''
97+
try:
98+
fig.set_size_inches((15,15))
99+
ax.set(xlim=(0, 20), ylim=(0, 5))
100+
ax.axis("off")
101+
if invert_perc == True:
102+
percentage = 100 - percentage
103+
# color options - #fc3d2e red & #53d069 green & #f5c54e yellow
104+
bat_fc = "#fc3d2e" if percentage <= 20 else "#53d069" if percentage >= 80 else "#f5c54e"
105+
106+
'''
107+
Static battery and tip of battery
108+
'''
109+
battery = FancyBboxPatch((5, 2.1), 10, 0.8,
110+
"round, pad=0.2, rounding_size=0.5",
111+
fc="none", ec=bat_ec, fill=True,
112+
ls="-", lw=1.5)
113+
tip = Wedge((15.35, 2.5), 0.2, 270, 90, fc="none",
114+
ec=bat_ec, fill=True,
115+
ls="-", lw=3)
116+
ax.add_artist(battery)
117+
ax.add_artist(tip)
118+
119+
'''
120+
Filling the battery cell with the data
121+
'''
122+
filler = FancyBboxPatch((5.1, 2.13), (percentage/10)-0.2, 0.74,
123+
"round, pad=0.2, rounding_size=0.5",
124+
fc=bat_fc, ec=bat_fc, fill=True,
125+
ls="-", lw=0)
126+
ax.add_artist(filler)
127+
128+
'''
129+
Adding a lightning bolt in the centre of the cell
130+
'''
131+
verts = [
132+
(10.5, 3.1), #top
133+
(8.5, 2.4), #left
134+
(9.5, 2.4), #left mid
135+
(9, 1.9), #bottom
136+
(11, 2.6), #right
137+
(10, 2.6), #right mid
138+
(10.5, 3.1), #top
139+
]
140+
141+
codes = [
142+
Path.MOVETO,
143+
Path.LINETO,
144+
Path.LINETO,
145+
Path.LINETO,
146+
Path.LINETO,
147+
Path.LINETO,
148+
Path.CLOSEPOLY,
149+
]
150+
path = Path(verts, codes)
151+
bolt = PathPatch(path, fc=bol_fc,
152+
ec=bol_ec, lw=1.5)
153+
ax.add_artist(bolt)
154+
except Exception as e:
155+
import traceback
156+
print("EXCEPTION FOUND!!! SAFELY EXITING!!! Find the details below:")
157+
traceback.print_exc()
158+
159+
```
160+
161+
## <span style="text-decoration: underline">Reading the Data</span>
162+
163+
Once we have created the API or function, we can now implement the same. And for that, we need to feed in required data. In our example, we have a dataset that has the list of Liverpool players and the minutes they have played in the past two seasons. The data was collected from <a href="www.fbref.com">Football Reference aka FBRef</a>.
164+
165+
We use the read excel function in the pandas library to read our dataset that is stored as an excel file.
166+
167+
```python
168+
data = pd.read_excel("Liverpool Minutes Played.xlsx")
169+
```
170+
171+
Now, let us have a look at how the data looks by listing out the first five rows of our dataset -
172+
173+
```python
174+
data.head()
175+
```
176+
![The first 5 rows of our dataset](head_data.PNG)
177+
178+
## <span style="text-decoration: underline">Plotting our data</span>
179+
180+
Now that everything is ready, we go ahead and plot the data. We have 25 players in our dataset, so a 5 x 5 figure is the one to go for. We'll also add some headers and set the colors accordingly.
181+
182+
```python
183+
fig, ax = plt.subplots(5, 5, figsize=(5, 5))
184+
facecolor = "#00001a"
185+
fig.set_facecolor(facecolor)
186+
fig.text(0.35, 0.95, "Liverpool: Player Usage/Involvement", color="white", size=18, fontname="Libre Baskerville", fontweight="bold")
187+
fig.text(0.25, 0.92, "Data from 19/20 and 20/21 | Battery percentage indicate usage | less battery = played more/ more involved", color="white", size=12, fontname="Libre Baskerville")
188+
```
189+
190+
We have now now filled in appropriate headers, figure size etc. The next step is to plot all the axes i.e. batteries for each and every player. `p` is the variable used to iterate through the dataframe and fetch each players data. The `draw_battery()` function call will obviously plot the battery. We also add the required labels along with that - player name and usage rate/percentage in this case.
191+
192+
```python
193+
p = 0 #The variable that'll iterate through each row of the dataframe (for every player)
194+
for i in range(0, 5):
195+
for j in range(0, 5):
196+
ax[i, j].text(10, 4, str(data.iloc[p, 0]), color="white", size=14, fontname="Lora", va='center', ha='center')
197+
ax[i, j].set_facecolor(facecolor)
198+
draw_battery(fig, ax[i, j], round(data.iloc[p, 8]), invert_perc=True)
199+
'''
200+
Add the battery percentage as text if a label is required
201+
'''
202+
ax[i, j].text(5, 0.9, "Usage - "+ str(int(100 - round(data.iloc[p, 8]))) + "%", fontsize=12, color="white")
203+
p += 1
204+
```
205+
206+
Now that everything is almost done, we do some final touchup and this is a completely optional part anyway. Since the visualisation is focused on Liverpool players, I add Liverpool's logo and also add my watermark. Also, crediting the data source/provider is more of an ethical habit, so we go ahead and do that as well before displaying the plot.
207+
208+
```python
209+
liv = Image.open('Liverpool.png', 'r')
210+
liv = liv.resize((80, 80))
211+
liv = np.array(liv).astype(np.float) / 255
212+
fig.figimage(liv, 30, 890)
213+
fig.text(0.11, 0.08, "viz: Rithwik Rajendran/@rithwikrajendra", color="lightgrey", size=14, fontname="Lora")
214+
fig.text(0.8, 0.08, "data: FBRef/Statsbomb", color="lightgrey", size=14, fontname="Lora")
215+
plt.show()
216+
```
217+
218+
So, we have the plot below. You can customise the design as you want in the `draw_battery()` function - change size, colours, shapes etc
219+
220+
![Usage_Chart_Liverpool](Liverpool_Usage_Chart.png)

0 commit comments

Comments
 (0)