Python Matplotlib Logarithmic Autoscale
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:
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:
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:
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)])
#####################################################
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"