Search code examples
python-3.xmatplotlibradar-chart

Have Radar Bar Chart Columns Span Whole Width of Bar - Matplotlib


A kind and smart man worked out how to do a beautiful matplotlib radar chart for me. However the issue here is that the bars on the chart do not extend below the given ytick they surpass. Ideally the bar for each value above 1.0 would extend right down to the centre, instead of leaving a gap.

I'm sure there must be a simple fix here but I am very much an amateur in the art of creating radar charts in matplotlib.

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, polar=True)

sample = [
    35.417256011315416,
    0.028288543140028287,
    1.3578500707213579,
    3.3663366336633667,
    0.8203677510608205,
    35.445544554455445,
    3.3946251768033946,
    19.46251768033946,
    0.7072135785007072,
]

N = len(sample)

theta = np.arange(0, 2 * np.pi, 2 * np.pi / N)
bars = ax.bar(theta, np.log10(sample), width=0.4)
ax.set_xticks(theta)
#ax.set_xticklabels(range(1, len(theta) + 1))
ax.set_xticklabels(['Delayed\nExecution', 'File\nOpening', 'Firewall\nModification', 'Permission\nModification', 'Persistence', 'Proxied\nExecution', 'Reconnaissance', 'Registry\nModification', 'Task\nStopping'])
ax.yaxis.grid(True)
precision = 2  # Change to your desired decimal precision

ax.set_yticklabels([str(round(10 ** x, precision)) for x in ax.get_yticks()])
#plt.ioff()
plt.show()

enter image description here


Solution

  • The problem is with you data, np.log10(sample) returns an array of both positive and negative values, therefore, some of the bars are not extended. In order to let all the bars start at the same level, you can firstly scale samples so that the smallest magnitude is non-negative,

    get_mag = lambda x: 10**min(np.floor(np.log10(x)))
    sample = np.array(sample) / get_mag(sample)
    

    with the scaled sample, you can plot your data

    get_mag = lambda x: 10**min(np.floor(np.log10(x)))
    init_mag = get_mag(sample)
    sample = np.array(sample) / get_mag(sample)
    N = len(sample)
    
    theta = np.arange(0, 2 * np.pi, 2 * np.pi / N)
    bars = ax.bar(theta, np.log10(sample), width=0.4)
    ax.set_xticks(theta)
    #ax.set_xticklabels(range(1, len(theta) + 1))
    ax.set_xticklabels(['Delayed\nExecution', 'File\nOpening', 'Firewall\nModification', 'Permission\nModification', 'Persistence', 'Proxied\nExecution', 'Reconnaissance', 'Registry\nModification', 'Task\nStopping'])
    
    dat = np.log10(sample)
    ax.set_ylim(0,max(dat))
    ax.yaxis.grid(True)
    precision = 2  # Change to your desired decimal precision
    
    ax.set_yticklabels([str(round((10 ** x) * init_mag, precision)) for x in ax.get_yticks()])
    #plt.ioff()
    plt.show()
    

    the output is

    output

    Note that I also modify the labels of yticks

    ax.set_yticklabels([str(round((10 ** x) * init_mag, precision)) for x in ax.get_yticks()])