I have written a Python script (below) to produce a Matplotlib plot to display boxplot data grouped together by subgroups (season), groups (city), and different colors (year).
However, the group (city) labels are quite crude (achieved by including several spaces in the label text), and I am searching for a better way to achieve this. Currently, the city labels are not exactly centered within each group of seasons, and they do not scale if the size of the plot is changed.
Another feature I would like is for the season labels to be at a 45-degree offset while keeping the city text horizontal (as shown in the plot).
How can I achieve this in a better, more Pythonic way?
import matplotlib.pyplot as pltimport numpy as npdata_a = [[1,2,5], [5,7,2,2,5], [7,2,5], [7,2,5]]data_b = [[6,4,2], [1,2,5,3,2], [2,3,5,1], []]data_c = [[1,2,5], [5,7,2,2,5], [7,2,5], [7,2,5]]data_d = [[6,4,2], [1,2,5,3,2], [2,3,5,1], []]data_e = [[1,2,5], [5,7,2,2,5], [7,2,5], [7,2,5]]data_f = [[6,4,2], [1,2,5,3,2], [2,3,5,1], []]data_g = [[1,2,5], [5,7,2,2,5], [7,2,5], [7,2,5]]data_h = [[6,4,2], [1,2,5,3,2], [2,3,5,1], []]def set_box_color(bp, color): plt.setp(bp['boxes'], color=color) plt.setp(bp['whiskers'], color=color) plt.setp(bp['caps'], color=color) plt.setp(bp['medians'], color=color)plt.figure()box_width = 0.6space_year = 0.4space_season = 2.0space_city = 10bpl = plt.boxplot(data_a, positions=np.array(range(len(data_a)))*space_season-space_year, sym='', widths=box_width)bpr = plt.boxplot(data_b, positions=np.array(range(len(data_b)))*space_season+space_year, sym='', widths=box_width)set_box_color(bpl, '#D7191C')set_box_color(bpr, '#2C7BB6')bpl = plt.boxplot(data_c, positions=np.array(range(len(data_a)))*space_season-space_year+space_city, sym='', widths=box_width)bpr = plt.boxplot(data_d, positions=np.array(range(len(data_b)))*space_season+space_year+space_city, sym='', widths=box_width)set_box_color(bpl, '#D7191C')set_box_color(bpr, '#2C7BB6')bpl = plt.boxplot(data_e, positions=np.array(range(len(data_a)))*space_season-space_year+space_city+space_city, sym='', widths=box_width)bpr = plt.boxplot(data_f, positions=np.array(range(len(data_b)))*space_season+space_year+space_city+space_city, sym='', widths=box_width)set_box_color(bpl, '#D7191C')set_box_color(bpr, '#2C7BB6')bpl = plt.boxplot(data_c, positions=np.array(range(len(data_a)))*space_season-space_year+space_city+space_city+space_city, sym='', widths=box_width)bpr = plt.boxplot(data_d, positions=np.array(range(len(data_b)))*space_season+space_year+space_city+space_city+space_city, sym='', widths=box_width)set_box_color(bpl, '#D7191C')set_box_color(bpr, '#2C7BB6')# draw temporary red and blue lines and use them to create a legendplt.plot([], c='#D7191C', label='2016')plt.plot([], c='#2C7BB6', label='2017')plt.legend(loc='upper center')ticks = ['Spring', 'Summer', 'Fall', 'Winter', 'Spring', 'Summer', 'Fall', 'Winter', 'Spring', 'Summer', 'Fall', 'Winter', 'Spring', 'Summer', 'Fall', 'Winter']plt.xticks((0,2,4,6,10,12,14,16,20,22,24,26,30,32,34,36), ticks, rotation=45)plt.plot()plt.xlabel('New York Chicago Houston Los Angeles')plt.tight_layout()plt.show()