Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

performance - How to make Matplotlib widget faster?

I am using Matplotlib widget slider on a figure having many plots. When I move the slider it takes about 5 s to update the figure and that is my problem.

After troubleshooting the problem, it seems that the culprit is the widget itself that calls fig.canvas.draw(). When not doing any action (not modifying any of the subplots) on moving the slider it is still taking 5s to update the slider because fig.canvas.draw() makes it replot all the figure with all the subplots each time I touch the slider.

In all threads complaining about Matplotlib plotting speed, like here: why is plotting with Matplotlib so slow?, it is advised to not use fig.canvas.draw() because it redraws the whole figure but use ax.draw_artist() instead to only replot the data of the subplot that has been modified. So Matplotlib widget is ruining all my efforts to make this plot interactive (5s lag is not really interactive). I would have known before I would probably have gone with a different library, but it is too late now.

This problem was actually brought up in 2013 (http://matplotlib.1069221.n5.nabble.com/Making-an-interactive-plot-faster-td41312.html) and it was suggested to have the option to use draw_artist instead. The possibility to set drawon to False doesn't work for me because, as explained in this post, the widget calls fig.canvas.draw() at least once anyways. Since this problem was first brought up, almost 8 years ago, has there been any improvement (that I haven't found) to make Matplotlib widget only update the widget and let the user decide what element on the figure should be updated as well?

I think that it is a major limitation of Matplotlib widgets and I really hope there is a way to correct this behaviour. In my case I could probably speed up the plot by plotting the biggest plot (which is static) as an image or reduce the number of points, but it will still be less efficient than just having widgets not redraw it everytime.

--------------------UPDATE----------------------

After creating the figure and only adding the widgets (5 sliders, 4 buttons, 1 radio button) and 3 empty subplots, the sliders already lag! At this point there is no data, only 10 widgets and 3 empty subplots on a figure, and there is already about 0.5s - 1s lag (by eye) everytime I want to move a slider. If I move a slider continuously up and down my CPU jumps from 15% to 50%! I have a Dell XPS13 with i7 7660U (2 cores 4 threads with 4GHz turbo). I don't understand how moving 1 slider can be so CPU intensive. What is so heavy for matplotlib to calculate? As a result, I don't have any workaround anymore. My idea of replacing the biggest plot by an image is not very useful since even without any data or image the slider is already too slow!

Does anyone understand how moving 1 slider can be so crazily cpu intensive, and most importantly, how to avoid this lag each time I touch a slider?

Thanks

Here is an example of the code I use to generate the figure with 10 widgets and 3 empty subplots.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons

fig = plt.figure(figsize=(16, 10))
ax1 = plt.subplot2grid((6,10),(0,0), rowspan = 2, colspan = 8)
ax2 = plt.subplot2grid((6,10),(3,0), rowspan = 2, colspan = 7)
ax3 = plt.subplot2grid((6,10),(3,8), rowspan = 2, colspan = 2)

ax_color = 'lightgoldenrodyellow' # Color of the sliders
ax_win = plt.axes([0.23, 0.53, 0.65, 0.023], facecolor=ax_color)
ax_thres = plt.axes([0.23, 0.5, 0.65, 0.023], facecolor=ax_color)
ax_min_dist = plt.axes([0.23, 0.56, 0.65, 0.023], facecolor=ax_color)
ax_move_win = plt.axes([0.55, 0.05, 0.35, 0.023], facecolor=ax_color)
ax_resize_win = plt.axes([0.55, 0.08, 0.35, 0.023], facecolor=ax_color)
ax_save_res = plt.axes([0.20, 0.05, 0.075, 0.04])
ax_save_all = plt.axes([0.30, 0.05, 0.075, 0.04])
ax_previous = plt.axes([0.05, 0.05, 0.05, 0.04])
ax_next = plt.axes([0.1, 0.05, 0.05, 0.04])
ax_cb = plt.axes([0.85, 0.6, 0.12, 0.15])
ax_cb.set_frame_on(False)
ax_cb.text(0.5, 1, 'Plot', horizontalalignment='center', verticalalignment='top', transform=ax_cb.transAxes, fontsize=16)

s_win = Slider(ax_win, 'Res window width [kHz]', 10, 1000, valinit = 2e5/1e3)
s_thresdB = Slider(ax_thres, 'Min res depth threshold [dB]', -10, 0.0, valinit = -0.5)
s_min_dist = Slider(ax_min_dist, 'Min inter-res distance [kHz]', 0, 3, valinit = np.log10(6))
s_move_win = Slider(ax_move_win, 'Zoom position', 0, 1, valinit = 0)
s_resize_win = Slider(ax_resize_win, 'Zoom width', 0, 0.25, valinit = 0.05)
b_save_res = Button(ax_save_res, 'Save res', color='lightblue', hovercolor='gold')
b_save_all = Button(ax_save_all, 'Save all res', color='lightblue', hovercolor='gold')
b_previous = Button(ax_previous, 'Previous', color='greenyellow', hovercolor='gold')
b_next = Button(ax_next, 'Next', color='greenyellow', hovercolor='gold')
cb_plot2 = RadioButtons(ax_cb, ('FPtemp vs fres', 'BBtemp vs fres', 'Close'), (False, False, True))
question from:https://stackoverflow.com/questions/65876820/how-to-make-matplotlib-widget-faster

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Ok, after some investigation it looks like Matplotlib 3.3.1 had a problem (as described here: https://github.com/matplotlib/matplotlib/pull/18304). Updating to version 3.3.2 helped a lot. There is still a small lag so it is not perfect but it is a lot more usable.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...