60

I'm trying to create a histogram of a data column and plot it logarithmically (y-axis) and I'm not sure why the following code does not work:

import numpy as np
import matplotlib.pyplot as plt
data = np.loadtxt('foo.bar')
fig = plt.figure()
ax = fig.add_subplot(111)
plt.hist(data, bins=(23.0, 23.5,24.0,24.5,25.0,25.5,26.0,26.5,27.0,27.5,28.0))
ax.set_xlim(23.5, 28)
ax.set_ylim(0, 30)
ax.grid(True)
plt.yscale('log')
plt.show()

I've also tried instead of plt.yscale('log') adding Log=true in the plt.hist line and also I tried ax.set_yscale('log'), but nothing seems to work. I either get an empty plot, either the y-axis is indeed logarithmic (with the code as shown above), but there is no data plotted (no bins).

Hadi
  • 5,328
  • 11
  • 46
  • 67
mannaroth
  • 1,473
  • 3
  • 17
  • 38
  • possible duplicate of [Python Pyplot Bar Plot bars disapear when using log scale](http://stackoverflow.com/questions/14047068/python-pyplot-bar-plot-bars-disapear-when-using-log-scale) – tacaswell Aug 02 '13 at 20:53
  • related problem, different solution – tacaswell Aug 02 '13 at 20:54

4 Answers4

110

try

plt.yscale('log', nonposy='clip')

http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.yscale

The issue is with the bottom of bars being at y=0 and the default is to mask out in-valid points (log(0) -> undefined) when doing the log transformation (there was discussion of changing this, but I don't remember which way it went) so when it tries to draw the rectangles for you bar plot, the bottom edge is masked out -> no rectangles.

tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • Thanks for the answer. The solution you proposed solved the disappearing bars, but another "bug" then surfaced: all the labels of the y-logarithmic axis were plotted on top of each other. This last problem was solved by commenting the "ax.set_ylim(0, 30)" line. – mannaroth Jul 31 '13 at 07:49
  • yes, because the 0 in the limit is clipped to some very small number so you have an unreasonable number of decades. use `ax.set_ylim(1, 30)` instead. – tacaswell Jul 31 '13 at 11:39
6

The hist constructor accepts the log parameter.
You can do this:

plt.hist(data, bins=bins, log=True)
Filipe Gomes
  • 189
  • 2
  • 5
3

np.logspace returns bins in [1-10], logarithmically spaced - in my case xx is a npvector >0 so the following code does the trick

logbins=np.max(xx)*(np.logspace(0, 1, num=1000) - 1)/9
hh,ee=np.histogram(xx, density=True, bins=logbins)
Sport
  • 8,570
  • 6
  • 46
  • 65
1

tacaswell's answer was correct, although the nonposy keyword argument has since been deprecated in favor of nonpositive. Furthermore, the link to the documentation is no longer active, please see the current docs for more info.

Updated solution:

plt.yscale('log', nonpositive='clip')
Magnus
  • 41
  • 2