ker_laeda86
ker_laeda86

Reputation: 287

How to colour selected range of histogram matplotlib?

I have a data named prices, and I use a prices.tail(1) to build a histogram.

Also I have some variables: left_border = 341.086, right_border = 437.177, line_length = 1099.

And the next code:

plt.figure(figsize=(9,6))
plt.hist(prices.tail(1), bins = 400)
x2 = [left_border,left_border] 
y2 = [0, line_length]
plt.plot(x2, y2, color = 'green')
x3 = [right_border, right_border] 
y3 = [0, line_length]
plt.plot(x3, y3, color = 'green')

plt.show()

Produce an output:

enter image description here

How I can colour part of histogram which is between the green borders differently from part outside the green borders, gradientally? Also to pick bins which are nearly by green borders and turn them into another colour?

Thanks.

Upvotes: 0

Views: 1830

Answers (1)

JohanC
JohanC

Reputation: 80449

The exact meaning of 'gradiently' here is uncertain to me. Here are some ideas that can serve as a base to create the desired solution.

  • hist returns the values of each bin, the limits of the bins and the patches that were drawn; you can color the patches depending on their mean x-position
  • to create a gradient like effect, the simplest is interpolating linearly between two colors; a function such as sqrt can be used to make the effect start quicker
  • axvspan can draw a vertical span between two given x coordinates; set zorder=0 to make sure the span stays behind the histogram bars; or set an alpha=0.3 to draw it as a transparent layer over the bars
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np

prices_np = 14*np.random.normal(5, 1, 10000)**2

left_border = 341.086
right_border = 437.177
# line_length = 1099
divisor_tickness = 10
main_color = mcolors.to_rgb('dodgerblue')
highlight_color = mcolors.to_rgb('limegreen')
divisor_color = mcolors.to_rgb('crimson')

binvals, bins, patches = plt.hist(prices_np, bins = 400, color=main_color)

bin_centers = 0.5 * (bins[:-1] + bins[1:])

for p, x in zip(patches, bin_centers):
    #x, _ = p.get_xy()
    #w = p.get_width()
    if left_border < x  < right_border:
        f = 2*min(x-left_border, right_border-x) / (right_border - left_border)
        f = f ** 0.5
        p.set_facecolor([ (h_rgb*f + m_rgb * (1-f))  for m_rgb, h_rgb in zip(main_color, highlight_color)] )
    elif left_border-divisor_tickness < x <= left_border or right_border <= x < right_border + divisor_tickness:
        p.set_facecolor(divisor_color)

plt.axvspan(left_border, right_border, color='lightgoldenrodyellow', zorder=0)
plt.show()

result

To get a smooth gradient depending on the bar heights, a gaussian kde could be useful:

kde = gaussian_kde(prices_np)
max_kde = max([kde(x)[0] for x in bin_centers])
for x, p in zip(bin_centers, patches):
    p.set_facecolor(plt.cm.viridis((kde(x)[0] / max_kde) ))

using gaussian kde for color

Upvotes: 4

Related Questions