Ajihood
Ajihood

Reputation: 133

Dividing matplotlib histogram by maximum bin value

I want to plot multiple histograms on the same plot and I need to compare the spread of the data. I want to do this by dividing each histogram by its maximum value so all the distributions have the same scale. However, the way matplotlib's histogram function works, I have not found an easy way to do this.

This is because n in

n, bins, patches = ax1.hist(y, bins = 20, histtype = 'step', color = 'k')

Is the number of counts in each bin but I can not repass this to hist since it will recalculate.

I have attempted the norm and density functions but these normalise the area of the distributions, rather than the height of the distribution. I could duplicate n and then repeat the bin edges using the bins output but this is tedious. Surely the hist function must allow for the bins values to be divided by a constant?

Example code is below, demonstrating the problem.

y1 = np.random.randn(100)
y2 = 2*np.random.randn(50)
x1 = np.linspace(1,101,100)
x2 = np.linspace(1,51,50)
gs = plt.GridSpec(1,2, wspace = 0, width_ratios = [3,1])
ax = plt.subplot(gs[0])
ax1 = plt.subplot(gs[1])
ax1.yaxis.set_ticklabels([])   # remove the major ticks

ax.scatter(x1, y1, marker='+',color = 'k')#, c=SNR, cmap=plt.cm.Greys)
ax.scatter(x2, y2, marker='o',color = 'k')#, c=SNR, cmap=plt.cm.Greys)
n1, bins1, patches1 = ax1.hist(y1, bins = 20, histtype = 'step', color = 'k',linewidth = 2, orientation = 'horizontal')
n2, bins2, patched2 = ax1.hist(y2, bins = 20, histtype = 'step', linestyle = 'dashed', color = 'k', orientation = 'horizontal')

Example output. I want the max bins of the dashed and dotted lines to be 1.

Upvotes: 7

Views: 9726

Answers (4)

Gabriel
Gabriel

Reputation: 42439

This can be accomplished using numpy to obtain a priori histogram values, and then plotting them with a bar plot.

import numpy as np
import matplotlib.pyplot as plt

# Define random data and number of bins to use
x = np.random.randn(1000)
bins = 10

plt.figure()
# Obtain the bin values and edges using numpy
hist, bin_edges = np.histogram(x, bins=bins, density=True)
# Plot bars with the proper positioning, height, and width.
plt.bar(
    (bin_edges[1:] + bin_edges[:-1]) * .5, hist / hist.max(),
    width=(bin_edges[1] - bin_edges[0]), color="blue")

plt.show()

enter image description here

Upvotes: 0

cphlewis
cphlewis

Reputation: 16269

Slightly different approach set up for comparisons. Could be adapted to the step style:

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np

y = []
y.append(np.random.normal(2, 2, size=40))
y.append(np.random.normal(3, 1.5, size=40))
y.append(np.random.normal(4,4,size=40))
ls = ['dashed','dotted','solid']

fig, (ax1, ax2, ax3) = plt.subplots(ncols=3)
for l, data in zip(ls, y):
    n, b, p = ax1.hist(data, normed=False,
                       #histtype='step', #step's too much of a pain to get the bins
                       #color='k', linestyle=l,
                       alpha=0.2
                       )
    ax2.hist(data, normed=True,
             #histtype = 'step', color='k', linestyle=l,
             alpha=0.2
             )

    n, b, p = ax3.hist(data, normed=False,
                       #histtype='step', #step's too much of a pain to get the bins
                       #color='k', linestyle=l,
                       alpha=0.2
                       )
    high = float(max([r.get_height() for r in p]))
    for r in p:
        r.set_height(r.get_height()/high)
        ax3.add_patch(r)
    ax3.set_ylim(0,1)

ax1.set_title('hist')
ax2.set_title('area==1')
ax3.set_title('fix height')
plt.show()

a couple outputs:

enter image description here

enter image description here

enter image description here

Upvotes: 0

Ajihood
Ajihood

Reputation: 133

I do not know whether matplotlib allows this normalisation by default but I wrote a function to do it myself.

It takes the output of n and bins from plt.hist (as above) and then passes this through the function below.

def hist_norm_height(n,bins,const):
    ''' Function to normalise bin height by a constant. 
        Needs n and bins from np.histogram or ax.hist.'''

    n = np.repeat(n,2)
    n = float32(n) / const
    new_bins = [bins[0]]
    new_bins.extend(np.repeat(bins[1:],2))
    return n,new_bins[:-1]

To plot now (I like step histograms), you pass it to plt.step.

Such as plt.step(new_bins,n). This will give you a histogram with height normalised by a constant.

Upvotes: 2

Bennett Brown
Bennett Brown

Reputation: 5383

You can assign the argument bins equal to a list of values. Use np.arange() or np.linspace() to generate the values. http://matplotlib.org/api/axes_api.html?highlight=hist#matplotlib.axes.Axes.hist

Upvotes: 0

Related Questions