Basic Circle Packing Chart

This post explains how to build a simple circle packing chart with Python. It uses the `circlify` library to compute the circle positions, and `matplotlib` for the rendering. It considers a dataset with only 1 level of hierarchy. See here if you have several levels.

Libraries

First, we need to load a few libraries:

• pandas: to handle the data
• matplotlib: to draw the chart
• `circlify`: for computing the size and position of the circles
``````import pandas as pd
import matplotlib.pyplot as plt
import circlify``````

📍 Data

A basic circle packing chart based on 1 level of hierarchy only requires a 2 columns data frame. The first column provides the name of each item (used for labeling). The second column provides a numeric value for the item. It controls the bubble size.

``````df = pd.DataFrame({
'Name': ['A', 'B', 'C', 'D', 'E', 'F'],
'Value': [10, 2, 23, 87, 12, 65]
})``````

You can type `df.head(3)` to observe the 3 first rows of this dataset in case you're not sure how it looks like 🧐 .

🖥️ Circle Packing algorithm

In a basic circle packing chart with one level of hierarchy, each entity of the dataset is represented by a circle. The circle size is proportional to the item value it represents.

The hardest part of the job is to compute the position and size of each circle. Fortunately, the `circlify` library offers a `circlify()` function that does the computation. It requires as input:

• `data` : (necessary) A list of positive values sorted from largest to smallest
• `target_enclosure` : (optional) A target enclosure where the packed circles should fit (default to the unit circle (0, 0, 1))
• `show_enclosure` : (optional) A boolean indicating if the target enclosure should be appended to the output (default to False)

Let's compute circle positions for the dataset created above.

``````# compute circle positions
circles = circlify.circlify(
df['Value'].tolist(),
show_enclosure=False,
target_enclosure=circlify.Circle(x=0, y=0, r=1)
)

# reverse the order of the circles to match the order of data
circles = circles[::-1]``````

I strongly advise to use `print(circles)` to have a look to this newly created object. You will see that it provides `x`, `y` and `r` for each item of our dataset 🎉 .

📊 Basic chart

``````# Create just a figure and only one subplot
fig, ax = plt.subplots(figsize=(10, 10))

# Remove axes
ax.axis('off')

# Find axis boundaries
lim = max(
max(
abs(circle.x) + circle.r,
abs(circle.y) + circle.r,
)
for circle in circles
)
plt.xlim(-lim, lim)
plt.ylim(-lim, lim)

# print circles
for circle in circles:
x, y, r = circle
ax.add_patch(plt.Circle((x, y), r, alpha=0.2, linewidth=2, fill=False))``````

🎨 Visual tweaks

Let's make something pretty and more insightful from this. Let's add a title, color the bubbles and add labels:

``````# Create just a figure and only one subplot
fig, ax = plt.subplots(figsize=(10, 10))

# Title
ax.set_title('Basic circular packing')

# Remove axes
ax.axis('off')

# Find axis boundaries
lim = max(
max(
abs(circle.x) + circle.r,
abs(circle.y) + circle.r,
)
for circle in circles
)
plt.xlim(-lim, lim)
plt.ylim(-lim, lim)

# list of labels
labels = df['Name']

# print circles
for circle, label in zip(circles, labels):
x, y, r = circle
plt.annotate(
label,
(x, y),
va='center',
ha='center'
)``````

Note: the `circlize` library offers a `bubbles()` function that does all the drawing for you. But it does not provide a lot of customization, so I feel like `matplotlib` is a better option here.

Space between bubbles

You can easily add spacing between bubbles. You just have to provide a percentage of the radius parameter of `add_patch()` (70% here).

``````# Create just a figure and only one subplot
fig, ax = plt.subplots(figsize=(10, 10))

# Title
ax.set_title('Basic circular packing')

# Remove axes
ax.axis('off')

# Find axis boundaries
lim = max(
max(
abs(circle.x) + circle.r,
abs(circle.y) + circle.r,
)
for circle in circles
)
plt.xlim(-lim, lim)
plt.ylim(-lim, lim)

# list of labels
labels = df['Name']

# print circles
for circle, label in zip(circles, labels):
x, y, r = circle
facecolor="#69b2a3", edgecolor="black"))
plt.annotate(label, (x, y), va='center', ha='center', bbox=dict(
This post explains how to create a circular packing chart using matplotlib and the `circlify` package.