How to draw a box plot in Matplotlib without a dataset?

Viewed 618

I am trying to draw box plot from a previous data. I do not have the dataset but I have the median, average, min val, max val, 10th and 90th percentile. I have seen similar questions on here but they all have answers only for 25th and 75th percentile (i.e, 1st and 3rd quartile). Thank you in advance!

1 Answers

Matplotlib's bxp function could be used. To show the information that is important in your case, instead of drawing the box between the 25th and the 75th percentile, it could be drawn between the 10th and 90th. Optionally labels, a legend or an accompanying text could explain the meaning of the elements of the boxplot.

import matplotlib.pyplot as plt

median = 5
average = 6
min_val = 1
max_val = 9
p10 = 3
p90 = 8
stats = [{'mean': average, 'med': median, 'q1': p10, 'q3': p90, 'whislo': min_val, 'whishi': max_val}]

fig, ax = plt.subplots()
ax.bxp(stats, showfliers=False, showmeans=True)

names = {'mean': 'average', 'med': 'median', 'q1': 'perc 10', 'q3': 'perc 90', 'whislo': 'min val', 'whishi': 'max val'}
for s in stats[0]:
    ax.text(1.1, stats[0][s], names[s], va='center', ha='left')
plt.xticks([1], ['boxplot 1'])
plt.show()

example boxplot

The following code adds two extra entries to stats dictionary for the average plus and minus the standard deviation. These extra entries are ignored by ax.bxp and can be plotted in a separate step. As an example, they are plotted with a circular marker.

Note that the plot gets quite full. Also, it might be confusing to add mean and sdev while the five other elements are percentile-based. It can be especially confusing when the sdev would fall outside the rectangular box.

The example shows how 3 different boxplots could look like:

import matplotlib.pyplot as plt
import numpy as np

def get_stats(x):
    return {'mean': x.mean(), 'med': np.median(x), 'q1': np.percentile(x, 10), 'q3': np.percentile(x, 90),
            'whislo': x.min(), 'whishi': min(10, x.max()),
            'minus sdev': x.mean() - x2.std(), 'plus sdev': x.mean() + x.std()}

median = 5
average = 6
min_val = 1
max_val = 9
p10 = 3
p90 = 8
sdev = 1.5
stats1 = {'mean': average, 'med': median, 'q1': p10, 'q3': p90, 'whislo': min_val, 'whishi': max_val,
          'minus sdev': average - sdev, 'plus sdev': average + sdev}

x2 = np.random.zipf(2.6, 100)
stats2 = get_stats(x2)

x3 = np.random.normal(np.tile(np.random.uniform(2, 8, 5), 100), 1)
stats3 = get_stats(x3)

stats = [stats1, stats2, stats3]
names = {'mean': 'average', 'med': 'median', 'q1': 'perc 10', 'q3': 'perc 90', 'whislo': 'min val', 'whishi': 'max val',
         'minus sdev': 'minus sdev', 'plus sdev': 'plus sdev'}

fig, ax = plt.subplots()
ax.bxp(stats, showfliers=False, showmeans=True)
for i in range(1, len(stats) + 1):
    ax.scatter([i, i], [stats[i - 1]['minus sdev'], stats[i - 1]['plus sdev']], marker='o', color='g')
    if i == 1:  # add explanory texts
        for s in stats[i - 1]:
            ax.text(i + .2, stats[i - 1][s], names[s], va='center', ha='left')

ax.set_xticks(range(1, len(stats) + 1))
ax.set_xticklabels(['imaginary example', 'zipf(2.6)', 'combined gaussians'])
plt.show()

adding sdev

Related