How do I update the extent of imshow in matplotlib? - python

When updating an "imshow" plot in matplotlib, it's best to use im.set_data, rather than using ax.imshow repeatedly in the loop. But what if the extent of the data is changing? Is it possible to update the extent of the data on each iteration of the loop?
Here is an example:
import numpy as np
import matplotlib.pyplot as plt
import time
ax = plt.subplot(111)
plt.ion()
plt.show()
count = 0
for size in np.linspace(1,3,10):
x = np.linspace(-size,size,100)
y = np.linspace(-size,size,100)
X,Y = np.meshgrid(x,y)
R = (X**2+Y**2)**0.5
Z = np.sin(R)/R
ext =(-size,size,-size,size)
if count == 0:
im = plt.imshow(Z,extent=ext)
else:
im.set_data(Z)
# Update the extent of the data
plt.draw()
plt.pause(0.5)
ax.set_xlim(-size,size)
ax.set_ylim(-size,size)
count += 1
plt.ioff()
plt.show()
The colored region should take up the entire axes if I could update the extent properly.

In your example, im.set_extent(ext).
More generally, though, almost any kwarg you can pass in to a matplotlib artist during initialization will have get_foo and set_foo methods. (That's actually how initialization works and how artist.set(...) and plt.setp works, as well.)
If you're looking for how to change a given property, the first place to look is a set_<name> method.
There are exceptions to this. For example, scatter returns a Collection, so you need to call set_offsets instead of set_xy to change the x, y data. Generally speaking, though, it's consistent.

Related

What is the name of the matplotlib function that gets executed when initially plotting the data which sets all axes correctly?

When I plot some data with matplotlib without setting any parameters, the data gets plotted with both x and y axis limits set correctly, meaning that all data is shown and no space is wasted (case 1):
import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
x = range(10)
plt.plot(x,'-o',markersize='10')
plt.tight_layout()
plt.show()
Result:
If I set some limits for e. g. the x axis, even using autoscale() does not autoscale the y axis anymore (case 2):
import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
x = range(10)
plt.plot(x,'-o',markersize='10')
plt.autoscale(enable=True,axis='y')
plt.xlim(7.5,11)
plt.tight_layout()
plt.show()
Result:
Question: which function is used internally by matplotlib to determine the limits for both axes and update the plot in case 1?
Background: I want to use this function as a base for reimplementing / extending this functionality for case 2.
As #ImportanceOfBeingEarnest pointed out in the answer below, there is no such automatized way at the moment. So, in case you are interested in knowing how to rescale your y-axis, one way to do so is by recomputing the corresponding y-values and then reassigning the y-limits using the method specified in this unaccepted answer. I haven't marked this as a duplicate because there are certain different issues in your example:
First (major one), you have plotted only x-values. So, to apply the method in the other answer, I had to first get the y-values in an array. This is done using get_ydata()
Second, the x-values were changed from range() generator to a NumPy array, as the former does not support indexing.
Third, I had to use a variable for the x-limits to be consistent with the function.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
plt.plot(x,'-o',markersize='10')
x_lims = [7.5, 11]
plt.xlim(x_lims)
ax = plt.gca()
y = ax.lines[0].get_ydata()
def find_nearest(array,value):
idx = (np.abs(array-value)).argmin()
return idx
y_low = y[find_nearest(x, x_lims[0])]
y_high = y[find_nearest(x, x_lims[1])]
ax.set_ylim(y_low, y_high)
plt.tight_layout()
plt.show()

Animating a function where function parameters change with time using FuncAnimation

I am trying to animate a one-dimensional function where the function inputs are same but function parameters are changing with time. The function I am trying to animate is
f(x)=sin(a* pi * x)/(b*x)+ (x-1)^4
Here the data to be plotted is same, but a, b are changing with every update.I am using python and matplotlib library. My initial attempt is as follows:
fig,ax = plt.subplots()
line, = ax.plot([],[])
def animate(i,func_params):
x = np.linspace(-0.5,2.5,num = 200)
a=func_params[i][0]
b=func_params[i][1]
y=np.sin(a*math.pi*x)/b*x + (x-1)**4
line.set_xdata(x)
line.set_ydata(y)
return line,
ani = animation.FuncAnimation(fig,animate,frames=len(visualize_pop),fargs=(visualize_func,),interval = 100,blit=True)
plt.show()
The above code is not plotting anything.
EDIT: Updated code based on comment.
Your problem is that with plot([],[]) you give matplotlib no data and therefore no way do determine the limits of the axes. Therefore it uses some default values which are way out of the range of the data you actually want to plot. Therefore you have two choices:
1) Set the limits to some values that will contain all your plotted data for all cases,
e.g.
ax.set_xlim([-0.5,2.5])
ax.set_ylim([-2,6])
2) Let ax compute the limits automatically each frame and re-scale the plot see here using these two commands within your animate function (note that this option only works correctly if you turn blitting off):
ax.relim()
ax.autoscale_view()
Here still a completely working version of your code (the commands for solution (1) are commented out and I changed some of the notations):
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig,ax = plt.subplots()
x = np.linspace(-0.5,2.5,num = 200)
line, = ax.plot([],[])
#ax.set_xlim([-0.5,2.5])
#ax.set_ylim([-2,6])
##assuming some parameters, because none were given by the OP:
N = 20
func_args = np.array([np.linspace(1,2,N), np.linspace(2,1,N)])
def animate(i,func_params):
a=func_params[0,i]
b=func_params[1,i]
y=np.sin(a*np.pi*x)/b*x + (x-1)**4
line.set_xdata(x)
line.set_ydata(y)
ax.relim()
ax.autoscale_view()
return line, ax
##blit=True will not update the axes labels correctly
ani = FuncAnimation(
fig,animate,frames=N, fargs=(func_args,),interval = 100 #, blit=True
)
plt.show()

How can i remove a plot with Python?

I have some data that I plotted with Python but now I want to erase the plots but not the figure itself.
I have some thing like this :
import numpy as np
import pylab as plt
a = np.array([1,2,3,4,5,6,7,8,9,10])
b = np.array([1,2,3,4,5,6,7,8,9,10])
c = plt.plot(a,b,'r.')
So to clear this I tried this :
a = np.array([])
b = np.array([])
c = plt.plot(a,b,'r.')
but it does not work. What is the best way to accomplish this?
You can use the remove method of the returned plot object. This is true of any plot object that inherits from Artist.
c = plt.plot(a,b,'r.')
for handle in c:
handle.remove()
To have axes with the same values of your a, b arrays, you can do:
import matplotlib.pyplot as plt
plt.clf() # To clear the figure.
plt.axis([1,10,1,10])
From here:
When to use cla(), clf() or close() for clearing a plot in matplotlib?
plt.cla() clears an axis, i.e. the currently active axis in the
current figure. It leaves the other axes untouched.
plt.clf() clears the entire current figure with all its axes, but
leaves the window opened, such that it may be reused for other plots.
plt.close() closes a window, which will be the current window, if not
specified otherwise.
Also if you prefer doing it line by line, you can remove them like this even if you've lost original references:
for l in ax.get_lines():
xval = l.get_xdata()[0]
if (xval == my_criteria):
l.remove()
or for all, simply:
for l in ax.get_lines():
l.remove()
likewise you can do the same indexing by y values.

Scale colormap for contour and contourf

I'm trying to plot the contour map of a given function f(x,y), but since the functions output scales really fast, I'm losing a lot of information for lower values of x and y. I found on the forums to work that out using vmax=vmax, it actually worked, but only when plotted for a specific limit of x and y and levels of the colormap.
Say I have this plot:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
u = np.linspace(-2,2,1000)
x,y = np.meshgrid(u,u)
z = (1-x)**2+100*(y-x**2)**2
cont = plt.contour(x,y,z,500,colors='black',linewidths=.3)
cont = plt.contourf(x,y,z,500,cmap="jet",vmax=100)
plt.colorbar(cont)
plt.show
I want to uncover whats beyond the axis limits keeping the same scale, but if I change de x and y limits to -3 and 3 I get:
See how I lost most of my levels since my max value for the function at these limits are much higher. A work around to this problem is to increase the levels to 1000, but that takes a lot of computational time.
Is there a way to plot only the contour levels that I need? That is, between 0 and 100.
An example of a desired output would be:
With the white space being the continuation of the plot without resizing the levels.
The code I'm using is the one given after the first image.
There are a few possible ideas here. The one I very much prefer is a logarithmic representation of the data. An example would be
from matplotlib import ticker
fig = plt.figure(1)
cont1 = plt.contourf(x,y,z,cmap="jet",locator=ticker.LogLocator(numticks=10))
plt.colorbar(cont1)
plt.show()
fig = plt.figure(2)
cont2 = plt.contourf(x,y,np.log10(z),100,cmap="jet")
plt.colorbar(cont2)
plt.show()
The first example uses matplotlibs LogLocator functions. The second one just directly computes the logarithm of the data and plots that normally.
The third example just caps all data above 100.
fig = plt.figure(3)
zcapped = z.copy()
zcapped[zcapped>100]=100
cont3 = plt.contourf(x,y,zcapped,100,cmap="jet")
cbar = plt.colorbar(cont3)
plt.show()

Automatically Rescale ylim and xlim in Matplotlib

I'm plotting data in Python using matplotlib. I am updating the data of the plot based upon some calculations and want the ylim and xlim to be rescaled automatically. Instead what happens is the scale is set based upon the limits of the initial plot. A MWE is
import random
import matplotlib.pyplot as pyplot
pyplot.ion()
x = range(10)
y = lambda m: [m*random.random() for i in range(10)]
pLine, = pyplot.plot(x, y(1))
for i in range(10):
pLine.set_ydata(y(i+1))
pyplot.draw()
The first plot command generates a plot from [0,1] and I can see everything just fine. At the end, the y-data array goes from [0,10) with most of it greater than 1, but the y-limits of the figure remain [0,1].
I know I can manually change the limits using pyplot.ylim(...), but I don't know what to change them to. In the for loop, can I tell pyplot to scale the limits as if it was the first time being plotted?
You will need to update the axes' dataLim, then subsequently update the axes' viewLim based on the dataLim. The approrpiate methods are axes.relim() and ax.autoscale_view() method.
Your example then looks like:
import random
import matplotlib.pyplot as pyplot
pyplot.ion()
x = range(10)
y = lambda m: [m*random.random() for i in range(10)]
pLine, = pyplot.plot(x, y(1))
for i in range(10):
pLine.set_ydata(y(i+1))
ax = pyplot.gca()
# recompute the ax.dataLim
ax.relim()
# update ax.viewLim using the new dataLim
ax.autoscale_view()
pyplot.draw()

Categories

Resources