How to create a swarm plot with matplotlib
Asked Answered
E

3

13

I know the question is not very informative.. but as I do not know the name of his type of plot, I can not be more informative..

[EDIT] I changed the title, and now it is more informative...

enter image description here

Elsie answered 22/3, 2016 at 11:40 Comment(3)
have you checked the matplotlib gallery?Immaculate
You might also check out seaborn.swarmplot or seaborn.stripplotOllie
I agree with @tom's last post. This looks like swarm plots with error bars.Dryer
O
19

You can do something similar with seaborn.swarmplot. I also use seaborn.boxplot (with the whiskers and caps turned off) to plot the mean and range:

import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")
tips = sns.load_dataset("tips")
ax = sns.swarmplot(x="day", y="total_bill", data=tips)
ax = sns.boxplot(x="day", y="total_bill", data=tips,
        showcaps=False,boxprops={'facecolor':'None'},
        showfliers=False,whiskerprops={'linewidth':0})

plt.show()

enter image description here

Ollie answered 22/3, 2016 at 12:5 Comment(0)
L
8

If (for whatever reason) you don't want to use seaborn, you can have a go at making them yourself (see e.g. this explanation: https://www.flerlagetwins.com/2020/11/beeswarm.html ).

A simple version is:

#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np


def simple_beeswarm(y, nbins=None):
    """
    Returns x coordinates for the points in ``y``, so that plotting ``x`` and
    ``y`` results in a bee swarm plot.
    """
    y = np.asarray(y)
    if nbins is None:
        nbins = len(y) // 6

    # Get upper bounds of bins
    x = np.zeros(len(y))
    ylo = np.min(y)
    yhi = np.max(y)
    dy = (yhi - ylo) / nbins
    ybins = np.linspace(ylo + dy, yhi - dy, nbins - 1)

    # Divide indices into bins
    i = np.arange(len(y))
    ibs = [0] * nbins
    ybs = [0] * nbins
    nmax = 0
    for j, ybin in enumerate(ybins):
        f = y <= ybin
        ibs[j], ybs[j] = i[f], y[f]
        nmax = max(nmax, len(ibs[j]))
        f = ~f
        i, y = i[f], y[f]
    ibs[-1], ybs[-1] = i, y
    nmax = max(nmax, len(ibs[-1]))

    # Assign x indices
    dx = 1 / (nmax // 2)
    for i, y in zip(ibs, ybs):
        if len(i) > 1:
            j = len(i) % 2
            i = i[np.argsort(y)]
            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


fig = plt.figure(figsize=(2, 4))
fig.subplots_adjust(0.2, 0.1, 0.98, 0.99)
ax = fig.add_subplot(1, 1, 1)
y = np.random.gamma(20, 10, 100)
x = simple_beeswarm(y)
ax.plot(x, y, 'o')
fig.savefig('bee.png')

a beeswarm plot

Lancelle answered 16/3, 2022 at 14:15 Comment(0)
F
1

A variation on the answer by @MichaelClerx, using numpy binning function and adding width parameter, to combine with boxplots:

#!/usr/bin/python3.6

from __future__ import division
import numpy as np
import matplotlib.pyplot as plt

def simple_beeswarm2(y, nbins=None, width=1.):
    """
    Returns x coordinates for the points in ``y``, so that plotting ``x`` and
    ``y`` results in a bee swarm plot.
    """
    y = np.asarray(y)
    if nbins is None:
        # nbins = len(y) // 6
        nbins = np.ceil(len(y) / 6).astype(int)

    # Get upper bounds of bins
    x = np.zeros(len(y))

    nn, ybins = np.histogram(y, bins=nbins)
    nmax = nn.max()

    #Divide indices into bins
    ibs = []#np.nonzero((y>=ybins[0])*(y<=ybins[1]))[0]]
    for ymin, ymax in zip(ybins[:-1], ybins[1:]):
        i = np.nonzero((y>ymin)*(y<=ymax))[0]
        ibs.append(i)

    # Assign x indices
    dx = width / (nmax // 2)
    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


y1 = np.random.gamma(20, 10, 100)
y2 = np.random.gamma(20, 10, 100)

fig, ax = plt.subplots(1, 1, figsize=(4, 8))
ax.boxplot([y1, y2], widths=0.5, showfliers=False, showcaps=False)
x1 = simple_beeswarm2(y1, width=0.25)
ax.plot(x1+1., y1, 'o')
x2 = simple_beeswarm2(y2, width=0.25)
ax.plot(x2+2., y2, 'o')
# ax.plot(x2, y, 'o')
plt.savefig('./swarm_final.png')
plt.close(fig)

enter image description here

Figurate answered 5/6, 2023 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.