Related
I'd like to update a plot by redrawing a new curve (with 100 points) in real-time.
This works:
import time, matplotlib.pyplot as plt, numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
t0 = time.time()
for i in range(10000000):
x = np.random.random(100)
ax.clear()
ax.plot(x, color='b')
fig.show()
plt.pause(0.01)
print(i, i/(time.time()-t0))
but there is only ~10 FPS, which seems slow.
What is the standard way to do this in Matplotlib?
I have already read How to update a plot in matplotlib and How do I plot in real-time in a while loop using matplotlib? but these cases are different because they add a new point to an existing plot. In my use case, I need to redraw everything and keep 100 points.
I do not know any technique to gain an order of magnitude. Nevertheless you can slightly increase the FPS with
update the line data instead of creating a new plot with set_ydata (and/or set_xdata)
use Figure.canvas.draw_idle() instead of Figure.canvas.draw() (cf. this question).
Thus I would recommand you to try the following:
import time
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
t0 = time.time()
x = np.random.random(100)
l, *_ = ax.plot(x, color='b')
fig.show()
fig.canvas.flush_events()
ax.set_autoscale_on(False)
for i in range(10000000):
x = np.random.random(100)
l.set_ydata(x)
fig.canvas.draw_idle()
fig.canvas.flush_events()
print(i, i/(time.time()-t0))
Note that, as mentioned by #Bhargav in the comments, changing matplotlib backend can also help (e.g. matplotlib.use('QtAgg')).
I hope this help.
In my simple example below, how to make x-axis tick values to appear between grids?
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
x = range(10)
y = np.random.random(10)
plt.plot(x,y)
plt.xticks(x)
plt.grid(True)
plt.show()
The following make ticks to be where I want but the grid lines also moves.
np.random.seed(1)
x = range(10)
y = np.random.random(10)
plt.plot(x,y)
plt.xticks(x)
plt.grid(True)
plt.xticks(np.arange(10)+0.5, x)
plt.show()
I would like the result to be:
You can set the minor ticks so that only 1 minor tick appears inbetween your major ticks. This is done using matplotlib.ticker.AutoMinorLocator. Then, set the gridlines to only appear at the minor ticks. You also need to shift your xtick positions by 0.5:
from matplotlib.ticker import AutoMinorLocator
np.random.seed(10)
x = range(10)
y = np.random.random(10)
plt.plot(x,y)
plt.xticks(np.arange(0.5,10.5,1), x)
plt.xlim(0,9.5)
plt.ylim(0,1)
minor_locator = AutoMinorLocator(2)
plt.gca().xaxis.set_minor_locator(minor_locator)
plt.grid(which='minor')
plt.show()
Edit: I'm having trouble getting two AutoMinorLocators to work on the same axis. When trying to add in another one for the y axis, the minor ticks get messed up. A work around I have found is to manually set the locations of the minor ticks using a matplotlib.ticker.FixedLocator and passing in the locations of the minor ticks.
from matplotlib.ticker import AutoMinorLocator
from matplotlib.ticker import FixedLocator
np.random.seed(10)
x = range(10)
y = np.random.random(10)
plt.plot(x,y)
plt.xticks(np.arange(0.5,10.5,1), x)
plt.yticks([0.05,0.15,0.25,0.35,0.45,0.55,0.65,0.75,0.85,0.95,1.05], [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])
plt.xlim(0,9.5)
plt.ylim(0,1.05)
minor_locator1 = AutoMinorLocator(2)
minor_locator2 = FixedLocator([0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])
plt.gca().xaxis.set_minor_locator(minor_locator1)
plt.gca().yaxis.set_minor_locator(minor_locator2)
plt.grid(which='minor')
plt.show()
If you use plt.subplots for figure creation, you get an axes object, too:
f, ax = plt.subplots(1)
This one has a better Interface for adjusting grid/ticks. Then you can give explicitly x-values for your data shifted 0.5 to the left. The same do with the minor ticks and let the grid be shown at the minor ticks:
f, ax = plt.subplots(1)
ax.set_xticks(range(10))
x_values = np.arange(10) - .5
ax.plot(x_values, np.random.random(10))
ax.set_xticks(x_values, minor=True)
ax.grid(which='minor')
I have some code to plot a grid, with the data in each cell being distinct and having a very specific position. The easiest way I found to do this was to create the grid with gridspec and use it to precisely position my subplots, however I'm having a problem where the overall grid is labelled from 0 to 1 along each axis. This happens every time, even when the dimensions of the grid are changed. Obviously these numbers have no relevance to my data, and as what I am aiming to display is qualitative rather than quantitative I would like to remove all labels from this plot entirely.
Here is a link to an image with an example of my problem
And here is the MWE that I used to create that image:
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
# mock-up of data being used
x = 6
y = 7
table = np.zeros((x, y))
# plotting
fig = plt.figure(1)
gs = gridspec.GridSpec(x, y, wspace=0, hspace=0)
plt.title('Example Plot')
for (j, k), img in np.ndenumerate(table):
ax = fig.add_subplot(gs[x - j - 1, k])
ax.set_xticklabels('')
ax.set_yticklabels('')
plt.show()
I have not been able to find note of anything like this problem, so any help would be greatly appreciated.
If you just want to draw a grid over the plot, use this code:
import numpy as np
import matplotlib.pyplot as plt
# mock-up of data being used
x = 6
y = 7
table = np.zeros((x, y))
# plotting
fig = plt.figure(1)
plt.title('Example Plot')
plt.gca().xaxis.grid(True, color='darkgrey', linestyle='-')
plt.gca().yaxis.grid(True, color='darkgrey', linestyle='-')
plt.show()
Another variant is used gridspec:
...
# hide ticks of main axes
ax0 = plt.gca()
ax0.get_xaxis().set_ticks([])
ax0.get_yaxis().set_ticks([])
gs = gridspec.GridSpec(x, y, wspace=0, hspace=0)
plt.title('Example Plot')
for (j, k), img in np.ndenumerate(table):
ax = fig.add_subplot(gs[x - j - 1, k])
# hide ticks of gribspec axes
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
I have a very simple question. I need to have a second x-axis on my plot and I want that this axis has a certain number of tics that correspond to certain position of the first axis.
Let's try with an example. Here I am plotting the dark matter mass as a function of the expansion factor, defined as 1/(1+z), that ranges from 0 to 1.
semilogy(1/(1+z),mass_acc_massive,'-',label='DM')
xlim(0,1)
ylim(1e8,5e12)
I would like to have another x-axis, on the top of my plot, showing the corresponding z for some values of the expansion factor. Is that possible? If yes, how can I have xtics ax
I'm taking a cue from the comments in #Dhara's answer, it sounds like you want to set a list of new_tick_locations by a function from the old x-axis to the new x-axis. The tick_function below takes in a numpy array of points, maps them to a new value and formats them:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
X = np.linspace(0,1,1000)
Y = np.cos(X*20)
ax1.plot(X,Y)
ax1.set_xlabel(r"Original x-axis: $X$")
new_tick_locations = np.array([.2, .5, .9])
def tick_function(X):
V = 1/(1+X)
return ["%.3f" % z for z in V]
ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks(new_tick_locations)
ax2.set_xticklabels(tick_function(new_tick_locations))
ax2.set_xlabel(r"Modified x-axis: $1/(1+X)$")
plt.show()
You can use twiny to create 2 x-axis scales. For Example:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
a = np.cos(2*np.pi*np.linspace(0, 1, 60.))
ax1.plot(range(60), a)
ax2.plot(range(100), np.ones(100)) # Create a dummy plot
ax2.cla()
plt.show()
Ref: http://matplotlib.sourceforge.net/faq/howto_faq.html#multiple-y-axis-scales
Output:
From matplotlib 3.1 onwards you may use ax.secondary_xaxis
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(1,13, num=301)
y = (np.sin(x)+1.01)*3000
# Define function and its inverse
f = lambda x: 1/(1+x)
g = lambda x: 1/x-1
fig, ax = plt.subplots()
ax.semilogy(x, y, label='DM')
ax2 = ax.secondary_xaxis("top", functions=(f,g))
ax2.set_xlabel("1/(x+1)")
ax.set_xlabel("x")
plt.show()
If You want your upper axis to be a function of the lower axis tick-values you can do as below. Please note: sometimes get_xticks() will have a ticks outside of the visible range, which you have to allow for when converting.
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
ax1 = fig.add_subplot(111)
ax1.plot(range(5), range(5))
ax1.grid(True)
ax2 = ax1.twiny()
ax2.set_xticks( ax1.get_xticks() )
ax2.set_xbound(ax1.get_xbound())
ax2.set_xticklabels([x * 2 for x in ax1.get_xticks()])
title = ax1.set_title("Upper x-axis ticks are lower x-axis ticks doubled!")
title.set_y(1.1)
fig.subplots_adjust(top=0.85)
fig.savefig("1.png")
Gives:
Answering your question in Dhara's answer comments: "I would like on the second x-axis these tics: (7,8,99) corresponding to the x-axis position 10, 30, 40. Is that possible in some way?"
Yes, it is.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(111)
a = np.cos(2*np.pi*np.linspace(0, 1, 60.))
ax1.plot(range(60), a)
ax1.set_xlim(0, 60)
ax1.set_xlabel("x")
ax1.set_ylabel("y")
ax2 = ax1.twiny()
ax2.set_xlabel("x-transformed")
ax2.set_xlim(0, 60)
ax2.set_xticks([10, 30, 40])
ax2.set_xticklabels(['7','8','99'])
plt.show()
You'll get:
I'm forced to post this as an answer instead of a comment due to low reputation.
I had a similar problem to Matteo. The difference being that I had no map from my first x-axis to my second x-axis, only the x-values themselves. So I wanted to set the data on my second x-axis directly, not the ticks, however, there is no axes.set_xdata. I was able to use Dhara's answer to do this with a modification:
ax2.lines = []
instead of using:
ax2.cla()
When in use also cleared my plot from ax1.
I use matplotib's Axes API to plot some figures. One of the lines I plot represents the theoretical expected line. It has no meaning outside of the original y and x limits. What I want, is for matlplotlib to ignore it when autoscaling the limits. What I used to do, is to check what are the current limits, then plot, and reset the limits. The problem is that when I plot a third plot, the limits get recalculated together with the theoretical line, and that really expands the graph.
# Boilerplate
from matplotlib.figure import Figure
from matplotlib.backends.backend_pdf import FigureCanvasPdf
from numpy import sin, linspace
fig = Figure()
ax = fig.add_subplot(1,1,1)
x1 = linspace(-1,1,100)
ax.plot(x1, sin(x1))
ax.plot(x1, 3*sin(x1))
# I wish matplotlib would not consider the second plot when rescaling
ax.plot(x1, sin(x1/2.0))
# But would consider the first and last
canvas_pdf = FigureCanvasPdf(fig)
canvas_pdf.print_figure("test.pdf")
The obvious way is to just manually set the limits to what you want. (e.g. ax.axis([xmin, xmax, ymin, ymax]))
If you don't want to bother with finding out the limits manually, you have a couple of options...
As several people (tillsten, Yann, and Vorticity) have mentioned, if you can plot the function you want to ignore last, then you can disable autoscaling before plotting it or pass the scaley=False kwarg to plot
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)
ax.plot(x1, np.sin(x1))
ax.plot(x1, np.sin(x1 / 2.0))
ax.autoscale(False) #You could skip this line and use scalex=False on
ax.plot(x1, 3 * np.sin(x1)) #the "theoretical" plot. It has to be last either way
fig.savefig('test.pdf')
Note that you can adjust the zorder of the last plot so that it's drawn in the "middle", if you want control over that.
If you don't want to depend on the order, and you do want to just specify a list of lines to autoscale based on, then you could do something like this: (Note: This is a simplified version assuming you're dealing with Line2D objects, rather than matplotlib artists in general.)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
def main():
fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)
line1, = ax.plot(x1, np.sin(x1))
line2, = ax.plot(x1, 3 * np.sin(x1))
line3, = ax.plot(x1, np.sin(x1 / 2.0))
autoscale_based_on(ax, [line1, line3])
plt.show()
def autoscale_based_on(ax, lines):
ax.dataLim = mtransforms.Bbox.unit()
for line in lines:
xy = np.vstack(line.get_data()).T
ax.dataLim.update_from_data_xy(xy, ignore=False)
ax.autoscale_view()
if __name__ == '__main__':
main()
Use the scalex/scaley kw arg:
plot(x1, 3*sin(x1), scaley=False)
LineCollection objects can be ignored by using the autolim=False argument:
from matplotlib.collections import LineCollection
fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)
# Will update limits
ax.plot(x1, np.sin(x1))
# Will not update limits
col = LineCollection([np.column_stack((x1, 3 * np.sin(x1)))], colors='g')
ax.add_collection(col, autolim=False)
# Will still update limits
ax.plot(x1, np.sin(x1 / 2.0))
This can be done regardless of plotting order by creating another axes to work on.
In this version, we create a twin axes and disable the autoscaling on that twin axes. In this way, the plot is scaled based on anything plotted in the original axes, but is not scaled by anything put into the twin axes.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)
twin_ax = ax.twinx() # Create a twin axes.
twin_ax.autoscale(False) # Turn off autoscaling on the twin axes.
twin_ax.set_yticks([]) # Remove the extra tick numbers from the twin axis.
ax.plot(x1, np.sin(x1))
twin_ax.plot(x1, 3 * np.sin(x1), c='green') # Plotting the thing we don't want to scale on in the twin axes.
ax.plot(x1, np.sin(x1 / 2.0))
twin_ax.set_ylim(ax.get_ylim()) # Make sure the y limits of the twin matches the autoscaled of the original.
fig.savefig('test.pdf')
Note, the above only prevents the un-twined axis from auto scaling (y in the above case). To get it to work for both x and y, we can do the twinning process for both x and y (or create the new axes from scratch):
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)
x2 = np.linspace(-2,2,100) # Would extend the x limits if auto scaled
twin_ax = ax.twinx().twiny() # Create a twin axes.
twin_ax.autoscale(False) # Turn off autoscaling on the twin axes.
twin_ax.set_yticks([]) # Remove the extra tick numbers from the twin axis.
twin_ax.set_xticks([]) # Remove the extra tick numbers from the twin axis.
ax.plot(x1, np.sin(x1))
twin_ax.plot(x2, 3 * np.sin(x2), c='green') # Plotting the thing we don't want to scale on in the twin axes.
ax.plot(x1, np.sin(x1 / 2.0))
twin_ax.set_ylim(ax.get_ylim()) # Make sure the y limits of the twin matches the autoscaled of the original.
twin_ax.set_xlim(ax.get_xlim()) # Make sure the x limits of the twin matches the autoscaled of the original.
fig.savefig('test.png')
As a generalisation of jam's answer, a collection object can be obtained from any of matplotlib's plotting functions and then re-added with autolim=False. For example,
fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)
# Get hold of collection
collection = ax.plot(x1, np.sin(x1))
# Remove collection from the plot
collection.remove()
# Rescale
ax.relim()
# Add the collection without autoscaling
ax.add_collection(collection, autolim=False)