About
This graph is a lollipop plot, designed to showcase a single value for each category, akin to a barplot but substituting the bar with a line. This specific instance features an appealing background and includes annotations positioned to the right of the chart.
It has been originally designed by Joseph Barbier. Thanks to him for sharing his work!
As a teaser, here is the plot we’re gonna try building:
Libraries
For creating this chart, we will need a whole bunch of libraries!
- matplotlib: to customize the appearance of the chart
- seaborn: to create the chart
- pandas: to handle the data
- highlight_text and
textwrap
: to add text annotations PIL
for the background image
# main libraries
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# image in the background
from PIL import Image
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
# annotations
from highlight_text import fig_text, ax_text
from matplotlib.patches import FancyArrowPatch
import textwrap
Dataset
The data can be accessed using the url below.
path = 'https://raw.githubusercontent.com/holtzy/the-python-graph-gallery/master/static/data/rolling_stone.csv'
df = pd.read_csv(path)
# clean the data
replacements = {
"Blues/Blues ROck": "Blues/Blues Rock",
"Rock n' Roll/Rhythm & Blues": "Blues/Blues Rock"
}
df['genre_clean'] = df['genre'].replace(replacements)
df.drop(['spotify_url', 'sort_name', 'artist_birth_year_sum', 'genre'], axis=1, inplace=True)
df.dropna(subset=['differential'], inplace=True)
df = df[df['release_year']<=2003]
data = df.groupby('genre_clean')['differential'].mean().sort_values()
data = pd.DataFrame({
'genre': data.index,
'diff': data.values
})
data = data[~data['genre'].isin(['Afrobeat'])]
data.sort_values('diff', ascending=False, inplace=True)
# show first rows
data.head()
genre | diff | |
---|---|---|
13 | Hip-Hop/Rap | 156.955556 |
12 | Electronic | 83.222222 |
10 | Indie/Alternative Rock | 59.478261 |
9 | Soul/Gospel/R&B | 13.000000 |
8 | Punk/Post-Punk/New Wave/Power Pop | -13.925926 |
Add background image
- Load Background Image: we first load a background image from a specified path and converts it into a NumPy array for manipulation.
- Initialize Chart with Background: A figure and its axes are then initialized using matplotlib, where the loaded image is set as the background with a reduced opacity.
- Create Secondary Axes for Lollipop Chart: A secondary axes area (sub_ax) is created on top of the main axes. This secondary area is intended for placing a lollipop chart and is sized and positioned to occupy the majority of the figure.
# open image
path_bg = '../../static/graph/background.png'
image_bg = np.array(Image.open(path_bg))
# create chart with background image
fig, ax = plt.subplots(figsize=(12,8), dpi=300)
ax.imshow(image_bg, alpha=0.15)
ax.set_axis_off()
# create lollipop background
sub_ax = inset_axes(
parent_axes=ax,
width="80%",
height="90%",
loc='lower center',
borderpad=3
)
# display plot
plt.show()
Add lollipop plot
Then we add the lollipop in the inside axes using the hlines()
and plot()
functions.
The hlines()
function is used to draw the horizontal lines, while the plot()
function is used to draw the circles at the end of each line.
# open image
path_bg = '../../static/graph/background.png'
image_bg = np.array(Image.open(path_bg))
# create chart with background image
fig, ax = plt.subplots(figsize=(12,8), dpi=300)
ax.imshow(image_bg, alpha=0.15)
ax.set_axis_off()
# create lollipop background
sub_ax = inset_axes(
parent_axes=ax,
width="80%",
height="90%",
loc='lower center',
borderpad=3
)
# add lollipop chart
x = data['diff']
y = data['genre']
sub_ax.hlines(y=y, xmin=0, xmax=x, color='black')
sub_ax.plot(x, y, 'o', color='black', zorder=2)
# display plot
plt.show()
Custom lollipop axes
Now we customize how the inside axes look like. We remove the ticks, the labels, and the left, right and top spines. We also set the background color to be fully transparent.
# open image
path_bg = '../../static/graph/background.png'
image_bg = np.array(Image.open(path_bg))
# create chart with background image
fig, ax = plt.subplots(figsize=(12,8), dpi=300)
ax.imshow(image_bg, alpha=0.15)
ax.set_axis_off()
# create lollipop background
sub_ax = inset_axes(
parent_axes=ax,
width="80%",
height="90%",
loc='lower center',
borderpad=3
)
# custom lollipop axes
sub_ax.set_xlim(-200, 200)
sub_ax.set_xticks([-200, -100, 0, 100, 200])
sub_ax.set_xticklabels(['-200', '-100', '0', '100', '200'])
sub_ax.set_yticklabels([])
sub_ax.set_yticks([])
sub_ax.spines[['top', 'left', 'right']].set_visible(False)
sub_ax.patch.set_alpha(0)
# add lollipop chart
x = data['diff']
y = data['genre']
sub_ax.hlines(y=y, xmin=0, xmax=x, color='black')
sub_ax.plot(x, y, 'o', color='black', zorder=2)
# display plot
plt.show()