Beeswarm Plot

Test Scores by Study Group Size

Student exam performance across different study configurations

Output
Test Scores by Study Group Size
Python
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(304)
BG_COLOR = '#0a0a0f'
TEXT_COLOR = 'white'
COLORS = ['#F54927', '#5314E6', '#27F5B0']

def simple_beeswarm(y, nbins=None, width=1.):
    y = np.asarray(y)
    if nbins is None:
        nbins = np.ceil(len(y) / 6).astype(int)
    nn, ybins = np.histogram(y, bins=nbins)
    nmax = nn.max()
    x = np.zeros(len(y))
    ibs = []
    for ymin, ymax in zip(ybins[:-1], ybins[1:]):
        i = np.nonzero((y > ymin) * (y <= ymax))[0]
        ibs.append(i)
    dx = width / (nmax // 2) if nmax > 1 else width
    for i in ibs:
        yy = y[i]
        if len(i) > 1:
            j = len(i) % 2
            i = i[np.argsort(yy)]
            a = i[j::2]
            b = i[j+1::2]
            x[a] = (0.5 + j / 3 + np.arange(len(b))) * dx
            x[b] = (0.5 + j / 3 + np.arange(len(b))) * -dx
    return x

groups = ['Solo', 'Small (3-5)', 'Large (6+)']
data = {
    'Solo': np.random.normal(72, 12, 55),
    'Small (3-5)': np.random.normal(82, 10, 60),
    'Large (6+)': np.random.normal(75, 14, 48)
}

fig, ax = plt.subplots(figsize=(10, 6), facecolor=BG_COLOR)
ax.set_facecolor(BG_COLOR)

boxplot_data = []
for i, (group, values) in enumerate(data.items()):
    values = np.clip(values, 0, 100)
    x = simple_beeswarm(values, width=0.35)
    ax.scatter(x + i + 1, values, c=COLORS[i], alpha=0.7, s=50, edgecolors='white', linewidth=0.5)
    boxplot_data.append(values)

bp = ax.boxplot(boxplot_data, positions=range(1, len(groups)+1), widths=0.5, patch_artist=True)
for patch in bp['boxes']:
    patch.set_facecolor('none')
    patch.set_edgecolor('#666')
for element in ['whiskers', 'caps', 'medians']:
    for item in bp[element]:
        item.set_color('#666')

ax.axhline(70, color='#22c55e', linestyle='--', alpha=0.5, linewidth=1)
ax.text(3.6, 71, 'Pass', color='#22c55e', fontsize=9)

ax.set_xticks(range(1, len(groups)+1))
ax.set_xticklabels(groups, color=TEXT_COLOR)
ax.set_xlabel('Study Group Size', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_ylabel('Test Score', fontsize=12, color=TEXT_COLOR, fontweight='500')
ax.set_title('Student Performance by Study Group', fontsize=14, color=TEXT_COLOR, fontweight='bold', pad=15)

ax.tick_params(colors='#888', labelsize=10)
for spine in ax.spines.values():
    spine.set_color('#333')

plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Statistical

Did this help you?

Support PyLucid to keep it free & growing

Support