Abhijit Rana
Abhijit Rana

Reputation: 23

How to plot a histogram with colored bars in which the coloring should be in accordance to the value in x-axis?

I have written a code which gives me one histogram and one bar plot. My code looks this: `

from math import pi, sin
import numpy as np
import matplotlib.pyplot as plt

plt.style.use('seaborn-whitegrid')

with open('output.txt', 'r') as f:
    lines = f.readlines()
    x = [float(line.split()[12]) for line in lines]

b=[]
a=np.histogram(x,bins=[90,92.5,95,97.5,100,102.5,105,107.5,110,112.5,115,117.5,120,122.5,125,127.5,130,132.5,135,137.5,140,142.5,145,147.5,150,152.5,155,157.5,160,162.5,165,167.5,170,172.5,175,177.5,180])

for i in range(len(a[0])):
    a[0][i]=a[0][i]/sin((91.25 + 2.5*i)*pi/180)
    b.append((91.25 + 2.5*i))

plt.bar(b,a[0],2.5,edgecolor='black')
plt.xlabel('angle($\Theta$)($\circ$)')
plt.ylabel('corrected Frequency')
plt.title('corrected angle distribution')
plt.savefig('Cone_corrected_angle_distribution.jpg', dpi=600)
plt.show()

plt.hist(x,bins=[90,92.5,95,97.5,100,102.5,105,107.5,110,112.5,115,117.5,120,122.5,125,127.5,130,132.5,135,137.5,140,142.5,145,147.5,150,152.5,155,157.5,160,162.5,165,167.5,170,172.5,175,177.5,180],edgecolor='black')
plt.xlabel('angle($\Theta$)($\circ$)')
plt.ylabel('Frequency')
plt.title('Angle distribution')
plt.savefig('angle_distribution.jpg', dpi=600)
plt.show()

` Now, I want to do two modifications for the graph:

  1. I want to color the bars of the histogram such that each bar has a unique color. The coloring of the bars should be according to the values of x-axis. And there should also be a color bar beneath the x-axis for reference.

  2. I want to plot the kernel distribution function along with the histogram.The methods that I have tried, give me a normalised curve. But I want to plot it along the histogram.

It would be a great help!!

Upvotes: 1

Views: 871

Answers (1)

JohanC
JohanC

Reputation: 80339

The bars of a bar plot can be colored via the color= parameter which can either be one specific color or an array of colors. A histogram doesn't allow an array of colors, but the returned rectangles can be easily colored in a loop.

The kde is an estimation of the kernel density and is preferably calculated from the original x-values. It is normalized to have a surface area of 1, so simply multiply it by the area of the histogram (i.e. the sum of all heights times the bar width) to have similar proportions.

The code below first creates some random data. The different arrays in the original code are as much as possible calculated via numpy (which is faster and often more readable and easier to create variations by changing a value at only one spot).

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde

x = np.random.normal(135, 15, 1000)
bin_width = 2.5
bins_bounds = np.arange(90, 180.01, bin_width)
bin_values, _ = np.histogram(x, bins=bins_bounds)
bin_centers = (bins_bounds[:-1] + bins_bounds[1:]) / 2
bin_values = bin_values / np.sin(bin_centers * np.pi / 180)

plt.style.use('seaborn-whitegrid')
fig, ax = plt.subplots(ncols=2, figsize=(10, 4))
cmap = plt.cm.get_cmap('inferno')
norm = plt.Normalize(vmin=90, vmax=180)
colors = cmap(norm(bin_centers))
ax[0].bar(bin_centers, bin_values, bin_width, color=colors, edgecolor='black')
ax[0].set_xlabel('angle($\Theta$)($\circ$)')
ax[0].set_ylabel('corrected Frequency')
ax[0].set_title('corrected angle distribution')

sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
fig.colorbar(sm, ax=ax[0], orientation='horizontal')

_, _, bars = ax[1].hist(x, bins=bins_bounds, edgecolor='black')
for bar, color in zip(bars, colors):
    bar.set_facecolor(color)
xs = np.linspace(90, 180, 200)
kde = gaussian_kde(x)
ax[1]. plot(xs, kde(xs) * len(x) * bin_width, color='dodgerblue', lw=2)
ax[1].set_xlabel('angle($\Theta$)($\circ$)')
ax[1].set_ylabel('Frequency')
ax[1].set_title('Angle distribution')
fig.colorbar(sm, ax=ax[1], orientation='horizontal')
plt.tight_layout()
plt.show()

example plot

Upvotes: 2

Related Questions