Control the facecolor of histograms

Question:

In the following histogram,

import numpy as np
import matplotlib.pyplot as plt
tinter = [0.8253909999999998, 0.8804020000000001, 0.14202099999999973, 0.7727520000000005, 1.1040220000000005, 0.10714699999999944, 0.5750040000000016, 0.7876929999999973, 0.8980040000000002, 1.1478490000000008, 0.44635099999999994, 1.052067000000001, 0.4327469999999991, 0.3272960000000005, 0.26918099999999967, 0.3854459999999982, 0.1827140000000007, 0.8867760000000011, 0.7774879999999982, 0.21082900000000038, 0.6758939999999996, 0.4335760000000022, 0.6791699999999992, 0.7758439999999993, 0.15755200000000258, 0.1414289999999987, 0.36975599999999886, 0.8993549999999999, 0.6577640000000002, 1.043728999999999, 0.19952800000000082, 0.12645200000000045, 0.3454990000000002, 0.9054520000000004, 0.7165229999999987, 0.9425269999999983, 0.7159280000000052, 0.7413279999999958, 0.12669900000000212, 0.2822880000000012, 0.3690029999999993, 0.7246340000000018, 0.4718329999999966, 0.7580859999999987, 0.744059, 0.19344999999999857, 0.12031900000000206, 0.47543600000000197, 0.437542999999998, 0.44232000000000227, 0.5250109999999992, 0.17673200000000122, 0.2440649999999991, 0.31524799999999686, 0.7674680000000009, 0.7837700000000041, 1.1910290000000003, 0.14404899999999543, 0.21560399999999902, 0.19931500000000568, 0.27113699999999596, 0.728234999999998, 0.5061920000000057, 0.6459329999999994, 0.7817450000000008, 0.8265129999999985, 0.17931199999999592, 0.30208600000000274, 0.32583699999999993, 0.41771599999999864]
n, bins, patches = plt.hist(x=tinter, bins='auto', color='#0504aa',
                            alpha=0.7, rwidth=0.9,)
plt.grid(axis='y', alpha=0.35)
plt.xlabel('time [s]')
plt.ylabel('Frequency')
plt.title('Hit interval')

enter image description here

I would like to set two different colors for the bars of the histogram.
There is a precedent question (this link), but the solution sets the bars’ colors in a range, e.g., bars 0 to 3 of one color.

This is a different problem since I would like to differentiate two zones in the histogram in general. I do not exactly how the particular histogram is and how many bars are.
In the example above, the separation of colors should be at 0.6: left of time=0.6s of one color and right of time=0.6 of another.
If it is not possible to make a bar of two colors (in the example, a range of time values is covered in the bar including 0.6), the separation of colors should go from the lowest bar in the example (bar of time=0.2) to the bar of time=0.6 included. And, in the example, the last four bars (above time=0.6s) of another color.

For example, in this example the values of n and bins that gives ‘plt.hist()` are:

n = array([16., 11., 10.,  3., 16.,  8.,  3.,  3.])
bins=array([0.107147  , 0.24263225, 0.3781175 , 0.51360275, 0.649088  , 0.78457325, 0.9200585 , 1.05554375, 1.191029  ])

I would like bars with n 16, 11, 10 and 3 of one color different of the other group of bars with n 16, 8, 3,3, and make it for a general histogram plot.

Asked By: user1993416

||

Answers:

You could loop through the bars and test whether it is completely to the right of the separation, completely to the left or crosses it. You change the bar’s color correspondingly.

When a bar crosses the separator, the bar gets the color for the left area and its size is narrowed to touch the separator. A copy of the bar can be added, with the right-area color and its x-position moved.

import matplotlib.pyplot as plt
import numpy as np
from copy import copy

tinter = [0.82539, 0.8804, 0.14202, 0.77275, 1.10402, 0.10715, 0.575, 0.78769, 0.898, 1.14785, 0.44635, 1.05207, 0.43275, 0.3273, 0.26918, 0.38545, 0.18271, 0.88678, 0.77749, 0.21083, 0.67589, 0.43358, 0.67917, 0.77584, 0.15755, 0.14143, 0.36976, 0.89935, 0.65776, 1.04373, 0.19953, 0.12645, 0.3455, 0.90545, 0.71652, 0.94253, 0.71593, 0.74133, 0.1267, 0.28229, 0.369, 0.72463, 0.47183, 0.75809, 0.74406, 0.19345, 0.12032, 0.47544, 0.43754, 0.44232, 0.52501, 0.17673, 0.24406, 0.31525, 0.76747, 0.78377, 1.19103, 0.14405, 0.2156, 0.19932, 0.27114, 0.72823, 0.50619, 0.64593, 0.78175, 0.82651, 0.17931, 0.30209, 0.32584, 0.41772]
n, bins, patches = plt.hist(x=tinter, bins='auto', color='#0504aa', alpha=0.7, rwidth=0.9)
plt.grid(axis='y', alpha=0.35)
plt.xlabel('time [s]')
plt.ylabel('Frequency')
plt.title('Hit interval')
separator = 0.6
left_color = 'turquoise'
right_color = 'tomato'
for bar in patches:
    x, y = bar.get_xy()
    w = bar.get_width()
    if x > separator:
        bar.set_color(right_color)
    else:
        bar.set_color(left_color)
        if x + w > separator:
            bar.set_width(separator-x)
            new_bar = copy(bar)
            new_bar.set_x(separator)
            new_bar.set_width(x+w-separator)
            new_bar.set_color(right_color)
            plt.gca().add_patch(new_bar)
plt.show()

histogram with two colors

If you want the complete bar that contains the separator value to be one color, you can leave out the block below if x + w > separator:.

n, bins, patches = plt.hist(x=tinter, bins='auto', color='#0504aa', alpha=0.7, rwidth=0.9)
plt.grid(axis='y', alpha=0.35)
plt.xlabel('time [s]')
plt.ylabel('Frequency')
plt.title('Hit interval')
separator = 0.6
left_color = 'turquoise'
right_color = 'tomato'
for bar in patches:
    x, y = bar.get_xy()
    w = bar.get_width()
    if x > separator:
        bar.set_color(right_color)
    else:
        bar.set_color(left_color)
plt.show()

histogram with two colors without splitting

Answered By: JohanC

Alternatively, without using transparent colors (alpha), one can do the following:

left_color = 'turquoise'
right_color = 'tomato'
separator = 0.6

tinter = [0.82539, 0.8804, 0.14202, 0.77275, 1.10402, 0.10715, 0.575, 0.78769, 0.898, 1.14785, 0.44635, 1.05207, 0.43275, 0.3273, 0.26918, 0.38545, 0.18271, 0.88678, 0.77749, 0.21083, 0.67589, 0.43358, 0.67917, 0.77584, 0.15755, 0.14143, 0.36976, 0.89935, 0.65776, 1.04373, 0.19953, 0.12645, 0.3455, 0.90545, 0.71652, 0.94253, 0.71593, 0.74133, 0.1267, 0.28229, 0.369, 0.72463, 0.47183, 0.75809, 0.74406, 0.19345, 0.12032, 0.47544, 0.43754, 0.44232, 0.52501, 0.17673, 0.24406, 0.31525, 0.76747, 0.78377, 1.19103, 0.14405, 0.2156, 0.19932, 0.27114, 0.72823, 0.50619, 0.64593, 0.78175, 0.82651, 0.17931, 0.30209, 0.32584, 0.41772]

# Set the color of histogram bars with the left_color from the beginning
n, bins, patches = plt.hist(x=tinter, bins='auto', color=left_color, rwidth=0.9)
plt.grid(axis='y', alpha=0.35)
plt.xlabel('time [s]')
plt.ylabel('Frequency')
plt.title('Hit interval')

for i, bar in enumerate(patches):
    x, y = bar.get_xy()
    w = bar.get_width()
    
    if x > separator:
        bar.set_color(right_color)

    # Get the dimension for the partial bar 
    else:
        width = x + w - separator
        height = n[i]

# Create the partial bar        
plt.bar(x=separator, height=height, width=width, color=right_color, align='edge')

plt.show()

enter image description here