Skip to content Skip to sidebar Skip to footer

Python Matplotlib Logarithmic Autoscale

I need to get a x,y plot with a logarithmic scale on the x-axes. When I zoom in the x-axes should autoscale as the (not logarithmic) y-axes. plt.xscale('log') plt.grid(True) plt.au

Solution 1:

The solution by @hashcode55 does not work as it is what I was attempting before I found this thread.

It seems to me that there is simply a "bug" in that:

plt.yscale('log')
plt.autoscale(enable=True, axis='y')

are not compatible.

Here is my sample code:

import matplotlib.pyplot as plt
import matplotlib
import random
import numpy as np

# generate some random data and add it to the plot
x = np.array(range(1,100))
y = np.maximum(np.ones(99), np.random.randn(99))
plt.plot(x, y, markersize=4, marker='.', color='red')

# format
ax = plt.gca()
plt.ylabel('LOGARITHMIC SCALE')
plt.yscale('log')
plt.minorticks_on
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
plt.autoscale(enable=True, axis='y')

#ax.set_ylim([np.min(y), np.max(y)])

#plot
plt.show()

which produces:

enter image description here

log scale, but clearly not autoscale

if I remove the comments from this line:

ax.set_ylim([np.min(y), np.max(y)])

Then it actually plots as would be expected with autoscale:

enter image description here

Nice, but what if I've lost reference to my y values on the plot?

while this solution/answer is a good "hack" to this sample problem, it its not a solid solution for my situation as my chart is a) live; continually updating every minute b) contains MANY plots c) is dropping off data older than past 24 hours; so such a solution would get really hacky if implemented every time something was added or removed from the plot in live session.

I would be interested in a true built-in "autoscale" solution, if such exists, that works with log y scale and I can auto update using plt.ion()

until then, what about this:

h/t @David Z How to extract data from matplotlib plot

#if you do need to get data out of a plot, I think this should do it

gca().get_lines()[n].get_xydata()

#Alternatively you can get the x and y data sets separately:

line = gca().get_lines()[n]
xd = line.get_xdata()
yd = line.get_ydata()

implemented in our situation at hand (with an extra blue line to test multiple lines) as:

import matplotlib.pyplot as plt
import matplotlib
import random
import numpy as np

# generate some random data and add it to the plot
x = np.array(range(1,100))
y = np.maximum(np.ones(99), np.random.randn(99))
plt.plot(x, y, markersize=4, marker='.', color='red')

# test for compatibility with multilpes lines
x = np.array(range(1,100))
y = np.maximum(np.ones(99), 1.5*np.random.randn(99))
plt.plot(x, y, markersize=4, marker='.', color='blue')

# format
ax = plt.gca()
plt.ylabel('LOGARITHMIC SCALE')
plt.yscale('log')
plt.minorticks_on
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
#plt.autoscale(enable=True, axis='y')

#####################################################
#force 'autoscale'
#####################################################
yd = [] #matrix of y values from all lines on plot
for n in range(len(plt.gca().get_lines())):
        line = plt.gca().get_lines()[n]
        yd.append(line.get_ydata())
ax.set_ylim([0.9*np.min(yd), 1.1*np.max(yd)])
#####################################################  

#plot
plt.show()

which, in essence, is pulling all y data from all lines on the plot, finding the max and min; then implementing them via set_ylim; "forcing" autoscale

yields:

enter image description here

voila!

for my situation I had somewhat more complicated plots in the format:

plt.plot((x1,x2), (y1,y2))

creating a matrix in matrix situation producing a 'Value Error'

for that I had to flatten using:

yd = [item for sublist in yd for item in sublist]

h/t @Alex Martelli Making a flat list out of list of lists in Python

and this was the final product:

#####################################################
#force 'autoscale'
#####################################################
yd = [] #matrix of y values from all lines on plot
for n in range(len(plt.gca().get_lines())):
        line = plt.gca().get_lines()[n]
        yd.append((line.get_ydata()).tolist())

yd = [item for sublist in yd for item in sublist]
ax.set_ylim([0.9*np.min(yd), 1.1*np.max(yd)])
#####################################################

enter image description here


Solution 2:

If you look at the documentation the function is like-

matplotlib.pyplot.autoscale(enable=True, axis='both', tight=None)

What you are sending is an invalid argument...

just make it

plt.autoscale(True, axis = 'both')

And about tight -

If True, set view limits to data limits; if False, let the locator and margins expand the view limits; if None, use tight scaling if the only artist is an image, otherwise treat tight as False. The tight setting is retained for future autoscaling until it is explicitly changed.


Solution 3:

I had a similar problem and I was able to solve it by setting the 'log' scale before plotting. In this case, the autoscaling is working as expected.


Post a Comment for "Python Matplotlib Logarithmic Autoscale"