11

Similar to a question I asked previously, I have a MWE like this:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

pd.Series(np.random.normal(0, 100, 1000)).plot(kind='hist', bins=50, color='orange')

bar_value_to_colour = 102

I then want to use the bar_value_to_colour variable to automatically change the colour of the bar on the histogram in which the value resides to blue, for example:

enter image description here

How can I achieve this?

Tony Babarino
  • 3,355
  • 4
  • 32
  • 44
BML91
  • 2,952
  • 3
  • 32
  • 54
  • I don't fully understand what are You trying to achieve. Do You want to change the colours of the bars with value 100 to blue? Can You try to explain the last sentence of Your question? – Tony Babarino Mar 09 '16 at 13:38
  • I've edited the question, does that make things clearer? – BML91 Mar 09 '16 at 13:47

2 Answers2

10

It's easy to get the x coordinate of the bar with rectangle.get_x() but the problem is that the bars aren't plotted exactly at the specific values so I've had to choose the closest one. Here is my solution:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

s = pd.Series(np.random.normal(0, 100, 10000))
p = s.plot(kind='hist', bins=50, color='orange')

bar_value_to_label = 100
min_distance = float("inf")  # initialize min_distance with infinity
index_of_bar_to_label = 0
for i, rectangle in enumerate(p.patches):  # iterate over every bar
    tmp = abs(  # tmp = distance from middle of the bar to bar_value_to_label
        (rectangle.get_x() +
            (rectangle.get_width() * (1 / 2))) - bar_value_to_label)
    if tmp < min_distance:  # we are searching for the bar with x cordinate
                            # closest to bar_value_to_label
        min_distance = tmp
        index_of_bar_to_label = i
p.patches[index_of_bar_to_label].set_color('b')

plt.show()

returns:

enter image description here

Tony Babarino
  • 3,355
  • 4
  • 32
  • 44
  • @BML91 I made minor fix in my code, so it calculates the distance from the middle of the bar instead of calculating it from the left coordinate of the bar. `tmp = abs((rectangle.get_x() + (rectangle.get_width() * (1 / 2))) - bar_value_to_label)` instead of `tmp = abs(rectangle.get_x() - bar_value_to_label)`. It can matter if the bars are thicker.. – Tony Babarino Mar 09 '16 at 19:07
3

Here's a simpler version of @Tony Barbarino's solution. It uses numpy.quantize to avoid iterating over the patch edges explicitly.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# Allocate the bin edges ourselves, so we can quantize the bar
# value to label with np.digitize.
bins = np.linspace(-400, 400, 50)

# We want to change the color of the histogram bar that contains
# this value.
bar_value_to_label = 100

# Get the index of the histogram bar that contains that value.
patch_index = np.digitize([bar_value_to_label], bins)[0]

s = pd.Series(np.random.normal(0, 100, 10000))
p = s.plot(kind='hist', bins=bins, color='orange')

# That's it!
p.patches[patch_index].set_color('b')
plt.show()

Color one histogram bar

This generalizes trivially to multiple bars.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# Allocate the bin edges ourselves, so we can quantize the bar
# value to label with np.digitize.
bins = np.linspace(-400, 400, 50)

# We want to change the color of the histogram bar that contains
# these values.
bar_values_to_label = [-54.3, 0, 121]

# Get the indices of the histogram bar that contains those values.
patch_indices = np.digitize([bar_values_to_label], bins)[0]

s = pd.Series(np.random.normal(0, 100, 10000))
p = s.plot(kind='hist', bins=bins, color='orange')

for patch_index in patch_indices:
    # That's it!
    p.patches[patch_index].set_color('b')
plt.show()

Color multiple histogram bars

ndronen
  • 982
  • 8
  • 12