Sunburst/Fan chart

150 Views Asked by At

I'm interested in developing a custom sunburst plot in for binary search trees (such as those used in genealogy). I'm trying to achieve the following:

enter image description here

As you can see, it is a sunburst chart (such as offered by Plotly) with a removed wedge.

1

There are 1 best solutions below

1
Matt Pitkin On BEST ANSWER

Here's something to at least get you started. You can use matplotlib's pie chart to make nested pie charts. You can also remove specific wedges as shown in https://stackoverflow.com/a/63881380/1862861. Using this information you could do:

from matplotlib import pyplot as plt

ninner = 2  # number of inner wedge
width = 0.15  # width of inner wedges
radius = 0.5  # radius of inner wedges
gap = 100  # size of missing wedge

ngap = 300 - gap  # total size of filled wedges (missing gap will be 1/3 of total)

fig, ax = plt.subplots()

for i in range(8):
    if i > 0:
        # expand pie chart radius and shrink wedge width
        width *= 0.85
        radius += width
    
    # create data
    data = [gap]
    data.extend([ngap / 2**(i+1)] * ninner)
    colors = ["lightgrey"] * len(data)  # for now they are all grey!
    ninner *= 2
    
    # create pie chart
    wedges, _ = ax.pie(
        data,
        radius=radius,
        colors=colors,
        wedgeprops={"width": width, "edgecolor": "w", "linewidth": 0.25},
        startangle=-150,  # shift start angle
    )
    wedges[0].set_visible(False)  # make gap invisible

fig.tight_layout()
fig.show()

which produces:

enter image description here

Obviously this doesn't give the colours, but it's a start.

Update

Here's a version with some added colours:

from matplotlib import pyplot as plt
import numpy as np


def get_colours(N):
    # use the tab20c colour map and get an array of colours

    # Note: the "16" in this is due to the 16 colours in the tab20c colour map
    cmap = plt.colormaps["tab20c"]
    cs = cmap(np.arange(16))
    if N <= 16:
        step = 16 // N
        colours = np.array([cs[i] for i in range(0, 16, step)])
    else:
        s = N // 16
        colours = np.array([cs[int(np.floor(i / s))] for i in range(N)])

    return colours


ninner = 2  # number of inner wedge
width = 0.15  # width of inner wedges
radius = 0.5  # radius of inner wedges
gap = 100  # size of missing wedge

ngap = 300 - gap  # total size of filled wedges (missing gap will be 1/3 of total)

fig, ax = plt.subplots()

for i in range(8):
    if i > 0:
        # expand pie chart radius and shrink wedge width
        width *= 0.85
        radius += width
    
    # create data
    data = [gap]
    data.extend([ngap / 2**(i+1)] * ninner)
    colours = np.array([[0.8, 0.8, 0.8, 1.0] for _ in range(len(data))])  # initialise as all grey

    wcolours = get_colours(ninner)

    # this part will depend on your data!
    # let's colour fill all the inner wedges
    if i < 5:
        colours[1:] = wcolours
    else:
        # choose some values to fill in
        nfill = int(np.sqrt(ninner))
        if nfill > 0:
            wfill = np.zeros(ninner)
            wfill[np.random.choice(np.arange(ninner), nfill, replace=False)] = 1.0
            cv = colours[1:]
            cv[wfill.astype(bool)] = wcolours[wfill.astype(bool)]

    ninner *= 2

    # create pie chart
    wedges, _ = ax.pie(
        data,
        radius=radius,
        colors=colours,
        wedgeprops={"width": width, "edgecolor": "w", "linewidth": 0.25},
        startangle=-150,  # shift start angle
    )
    wedges[0].set_visible(False)  # make gap invisible

fig.tight_layout()
fig.show()

which produces:

enter image description here