Search code examples
pythonmatplotlibpie-chartpygal

chart_pie index to generate


I need help to generate this graph, especially with domains limits enter image description here

and the arrow indicating the domain enter image description here

all I can do is generate the domains name but not the limits and arrow


Solution

  • The following code will produce something like what you require:

    from matplotlib import pyplot as plt
    from matplotlib.patches import Wedge
    import numpy as np
    
    labels = ["Obésité\nmassive", "Obésité", "Surpoids", "Normal", "Maigreur"]
    innerlabels = [">40", "30 à 40", "25 à 30", "18,5 à 25", "< 18,5"]
    colours = ["red", "darkorange", "orange", "green", "blue"]
    
    fig, ax = plt.subplots(figsize=(6, 6), dpi=200)
    
    theta = 0
    dtheta = 180 / len(labels)
    width = 0.35
    
    def pol2cart(rho, phi):
        x = rho * np.cos(phi)
        y = rho * np.sin(phi)
        return(x, y)
    
    patches = []
    for i in range(len(labels)):
        # outer wedge
        wedge = Wedge(0, r=1, width=width, theta1=theta, theta2=(theta + dtheta), fc=colours[i], alpha=0.6, edgecolor="whitesmoke")
        ax.add_patch(wedge)
    
        # inner wedge
        wedge = Wedge(0, r=1 - width, width=width, theta1=theta, theta2=(theta + dtheta), fc=colours[i], edgecolor="whitesmoke")
        ax.add_patch(wedge)
    
        theta += dtheta
    
        # add text label
        tr = 1 - (width / 2)
        ta = theta - dtheta / 2
        x, y = pol2cart(tr, np.deg2rad(ta))
        textangle = -np.fmod(90 - ta, 180)
        ax.text(x, y, labels[i], rotation=textangle, va="center", ha="center", color="white", fontweight="bold")
    
        # inner labels
        tr = (1 - width) - (width / 2)
        x, y = pol2cart(tr, np.deg2rad(ta))
        textangle = -np.fmod(90 - ta, 180)
        ax.text(x, y, innerlabels[i], rotation=textangle, va="center", ha="center", color="white")
    
    
    ax.set_xlim([-1, 1])
    ax.set_ylim([0, 1])
    ax.set_axis_off()
    ax.set_aspect("equal")
    
    
    def bmiposition(bmi):
        """
        Get angular position of BMI arrow.
        """
    
        from scipy.interpolate import interp1d
    
        bmiranges = [(0, 18.5), (18.5, 25), (25, 30), (30, 40), (40, 80)]
        angrange = [(180 - dtheta * i, 180 - dtheta * (i + 1)) for i in range(len(bmiranges))]
    
        interpfuncs = []
        for i in range(len(bmiranges)):
            interpfuncs.append(interp1d(bmiranges[i], angrange[i], kind="linear"))
    
        bmiang = np.piecewise(
            bmi,
            [bmiranges[i][0] < bmi <= bmiranges[i][1] for i in range(len(bmiranges))],
            interpfuncs,
        )
    
        return bmiang
    
    
    bmi = 22.5  # set BMI
    
    # add arrow
    pos = bmiposition(bmi)  # get BMI angle
    x, y = pol2cart(0.25, np.deg2rad(pos))
    ax.arrow(0, 0, x, y, head_length=0.125, width=0.025, fc="k")
    ax.plot(0, 0, 'ko', ms=10)  # circle at origin
    

    giving:

    enter image description here