Так что это не относится к ускорению конкретной функции, о которой идет речь, но показывает несколько способов построения линии с большим количеством точек, достаточно эффективно.Это предполагает, что точки x упорядочены и равномерно (или близки к однородным) выборки.
Настройка
from pylab import *
Вот функция, которая мне нравится, которая уменьшает количество точек путем случайного выбора одной вкаждый интервал.Не гарантируется показ каждого пика в данных, но у него не так много проблем, как непосредственное прореживание данных, и он быстрый.
def calc_rand(y, factor):
split = y[:len(y)//factor*factor].reshape(-1, factor)
idx = randint(0, split.shape[-1], split.shape[0])
return split[arange(split.shape[0]), idx]
А вот мин и макс, чтобы увидетьогибающая сигнала
def calc_env(y, factor):
"""
y : 1D signal
factor : amount to reduce y by (actually returns twice this for min and max)
Calculate envelope (interleaved min and max points) for y
"""
split = y[:len(y)//factor*factor].reshape(-1, factor)
upper = split.max(axis=-1)
lower = split.min(axis=-1)
return c_[upper, lower].flatten()
Следующая функция может использовать любую из этих функций и использовать их для уменьшения отображаемых данных.Количество фактически взятых точек по умолчанию составляет 5000, что должно намного превышать разрешение монитора.Данные кешируются после уменьшения.Возможно, проблема с памятью, особенно с большими объемами данных, но она не должна превышать объем, требуемый для исходного сигнала.
def plot_bigly(x, y, *, ax=None, M=5000, red=calc_env, **kwargs):
"""
x : the x data
y : the y data
ax : axis to plot on
M : The maximum number of line points to display at any given time
kwargs : passed to line
"""
assert x.shape == y.shape, "x and y data must have same shape!"
if ax is None:
ax = gca()
cached = {}
# Setup line to be drawn beforehand, note this doesn't increment line properties so
# style needs to be passed in explicitly
line = plt.Line2D([],[], **kwargs)
def update(xmin, xmax):
"""
Update line data
precomputes and caches entire line at each level, so initial
display may be slow but panning and zooming should speed up after that
"""
# Find nearest power of two as a factor to downsample by
imin = max(np.searchsorted(x, xmin)-1, 0)
imax = min(np.searchsorted(x, xmax) + 1, y.shape[0])
L = imax - imin + 1
factor = max(2**int(round(np.log(L/M) / np.log(2))), 1)
# only calculate reduction if it hasn't been cached, do reduction using nearest cached version if possible
if factor not in cached:
cached[factor] = red(y, factor=factor)
## Make sure lengths match correctly here, by ensuring at least
# "factor" points for each x point, then matching y length
# this assumes x has uniform sample spacing - but could be modified
newx = x[imin:imin + ((imax-imin)//factor)* factor:factor]
start = imin//factor
newy = cached[factor][start:start + newx.shape[-1]]
assert newx.shape == newy.shape, "decimation error {}/{}!".format(newx.shape, newy.shape)
## Update line data
line.set_xdata(newx)
line.set_ydata(newy)
update(x[0], x[-1])
ax.add_line(line)
## Manually update limits of axis, as adding line doesn't do this
# if drawing multiple lines this can quickly slow things down, and some
# sort of check should be included to prevent unnecessary changes in limits
# when a line is first drawn.
ax.set_xlim(min(ax.get_xlim()[0], x[0]), max(ax.get_xlim()[1], x[1]))
ax.set_ylim(min(ax.get_ylim()[0], np.min(y)), max(ax.get_ylim()[1], np.max(y)))
def callback(*ignore):
lims = ax.get_xlim()
update(*lims)
ax.callbacks.connect('xlim_changed', callback)
return [line]
Вот некоторый тестовый код
L=int(100e6)
x=linspace(0,1,L)
y=0.1*randn(L)+sin(2*pi*18*x)
plot_bigly(x,y, red=calc_env)
Вкл.моя машина это отображает очень быстро.Масштабирование немного запаздывает, особенно когда оно большое.Панорамирование не имеет проблем.Использование случайного выбора вместо минимального и максимального значений происходит немного быстрее и имеет проблемы только при очень высоком уровне масштабирования.