sam
sam

Reputation: 25

Altering display pattern in bar plot -Pandas

This is a follow-up to How to combine groups of columns as a bar plot in pandas

Is it possible to alter the way bar plots are displayed. To display a player, a bar with solid color fill and the next bar with just a colored border. This is a sample plot.

enter image description here

Upvotes: 0

Views: 74

Answers (1)

Henry Ecker
Henry Ecker

Reputation: 35686

This is a slight change to the styling of the columns in the previous question:

fig, ax = plt.subplots()
plot_df.plot(kind='bar', rot=0, ax=ax, legend=False, xlabel='', ylabel='Score')

players = plot_df.columns.get_level_values(0).unique()
score_types = plot_df.columns.get_level_values(1).unique()

hatches = ['/', None]
colours = ['red', 'green', 'blue']
iter_hatches = np.tile(np.repeat(hatches, plot_df.shape[0]),
                       len(players))
iter_colours = np.tile(np.repeat([v for c in colours
                                  for v in [None, c]], plot_df.shape[0]),
                       len(players))

iter_edges = np.roll(iter_colours, -plot_df.shape[0])

for patch, colour, hatch, edge in zip(ax.patches, iter_colours,
                                      iter_hatches, iter_edges):
    if colour is None:
        patch.set_fill(False)
        patch.set_edgecolor(edge)
    else:
        patch.set_facecolor(colour)
    patch.set_hatch(hatch)

ax.legend(loc=1)
plt.tight_layout()
plt.show()

plot 1

Logic is to intersperse None with the colours:

[v for c in colours for v in [None, c]]

[None, 'red', None, 'green', None, 'blue']

Create Edges based on the color column using np.roll this will shift the colours back so that there are Nones are inverse:

iter_colours

array([None, None, None, 'red', 'red', 'red', None, None, None, 'green',
       'green', 'green', None, None, None, 'blue', 'blue', 'blue', None,
       None, None, 'red', 'red', 'red', None, None, None, 'green',
       'green', 'green', None, None, None, 'blue', 'blue', 'blue', None,
       None, None, 'red', 'red', 'red', None, None, None, 'green',
       'green', 'green', None, None, None, 'blue', 'blue', 'blue'],
      dtype=object)

iter_edges = np.roll(iter_colours, -plot_df.shape[0])

array(['red', 'red', 'red', None, None, None, 'green', 'green', 'green',
       None, None, None, 'blue', 'blue', 'blue', None, None, None, 'red',
       'red', 'red', None, None, None, 'green', 'green', 'green', None,
       None, None, 'blue', 'blue', 'blue', None, None, None, 'red', 'red',
       'red', None, None, None, 'green', 'green', 'green', None, None,
       None, 'blue', 'blue', 'blue', None, None, None], dtype=object)

Then perform separate logic based on None colours and not None colours:

for patch, colour, hatch, edge in zip(ax.patches, iter_colours,
                                      iter_hatches, iter_edges):
    if colour is None:  # If colour is None
        patch.set_fill(False)  # Set the fill to False
        patch.set_edgecolor(edge)  # Add an Edge Colour
    else:
        patch.set_facecolor(colour)  # Otherwise use face colour
    patch.set_hatch(hatch)  # Set Hatch

Upvotes: 1

Related Questions