Replies: 7 comments 4 replies
-
Does the xArrayLinSorted really help? ClipToView already assumes that x is sorted and a binary search of 1e7 elements would only take about 23 iterations? The isfinite bypass seems like a straightforward improvement. |
Beta Was this translation helpful? Give feedback.
-
The poor performance seems to be because In [9]: xi32 = np.arange(1e7, dtype=np.int32)
In [10]: xf32 = np.arange(1e7, dtype=np.float32)
In [11]: xf64 = np.arange(1e7, dtype=np.float64)
In [12]: %timeit np.searchsorted(xi32, 100.0)
33.4 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [13]: %timeit np.searchsorted(xf32, 100.0)
32.7 ms ± 1.72 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [14]: %timeit np.searchsorted(xf64, 100.0)
2.57 µs ± 36.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each) I suspect that if you were to replace Or pass in x manually with dtype float64. import pyqtgraph as pg
import numpy as np
pg.mkQApp()
pw = pg.PlotWidget()
pw.setClipToView(True)
pw.enableAutoRange(False)
pw.setDownsampling(ds=None, auto=True, mode="subsample")
data = np.arange(10000000)+np.random.normal(0,10,10000000)
x = np.arange(data.size, dtype=data.dtype)
pw.plot(x, data)
pw.show()
pg.exec() |
Beta Was this translation helpful? Give feedback.
-
We could replace In [3]: xi32 = np.arange(1e7, dtype=np.int32)
In [4]: xf32 = np.arange(1e7, dtype=np.float32)
In [5]: xf64 = np.arange(1e7, dtype=np.float64)
In [7]: %timeit np.searchsorted(xi32, 100.0)
35.7 ms ± 5.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [8]: %timeit np.searchsorted(xf32, 100.0)
36.1 ms ± 5.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [9]: %timeit np.searchsorted(xf64, 100.0)
2.71 µs ± 123 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
In [10]: %timeit bisect.bisect_left(xi32, 100.0)
60 µs ± 4.93 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [11]: %timeit bisect.bisect_left(xf32, 100.0)
53.8 µs ± 1.76 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
In [12]: %timeit bisect.bisect_left(xf64, 100.0)
2.42 µs ± 114 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each) |
Beta Was this translation helpful? Give feedback.
-
I have created #2719 implementing 2 of the ideas in this discussion. Would appreciate it if you could test it out. |
Beta Was this translation helpful? Give feedback.
-
Regarding point 2 of your comment #2719 (comment), would it not be sufficient for you to import pyqtgraph as pg
import numpy as np
pg.mkQApp()
pw = pg.PlotWidget()
pw.show()
pw.setClipToView(True)
pw.enableAutoRange(False)
pw.setDownsampling(ds=None, auto=True, mode="subsample")
data = np.arange(10000000)+np.random.normal(0,10,10000000)
pdi = pw.plot()
pdi.setData(data)
pg.exec() A
where it was previously
|
Beta Was this translation helpful? Give feedback.
-
Hi everyone. Some of this discussion seem to resemble what I was trying in PR #1574. I thought I'd mention it in case you haven't seen it. The approach there seemed to work pretty well, but I never got around to finishing the PR. |
Beta Was this translation helpful? Give feedback.
-
Something like the following would break the oscillation: if not hasattr(self, '_last_ds_value'):
self._last_ds_value = ds
if abs(self._last_ds_value - ds) / ds < 0.05:
ds = self._last_ds_value
self._last_ds_value = ds
print(ds, view_range) The stored state would of course need to be reset at appropriate moments. |
Beta Was this translation helpful? Give feedback.
-
Hey !
I'm using pyqtgraph to display very large datasets and I find the autodownsample and cliptoview very useful. However, for larger datasets (>10⁷ pts) it is very slow and I found simple fixes which increase tremendously the performances, bypassing
np.isfinite
andnp.searchsorted
when they are not necessary. After those little fixes I very smoothly navigate in huge timeseries.This is how I increase a lot the performance in my fork :
About downsampling
About ClipToView
[EDIT : as @pijyoi noted the poor performance of
np.searchsorted
was due to the fact that my xArray was of typeint
. Usingnp.searchsorted
with aint
array while the second argument is afloat
is extremely unefficient, hence my problem. So just make sure your xarray has a typenp.float64
. This should also be corrected in the code of pyqtgraph, replacingnp.arange(len(y))
bynp.arange(len(y),dtype=np.float64)
in the initialization ofPlotDataItem
when no x array is passed].If the xArray is sorted and linear (proportional to indices), I do not use
np.searchsorted
anymore. To do that I added a propertyxArrayLinSorted
to the classPlotDataset
. It can be set toTrue
either by the user by passing the argumentxArrayLinSorted=True
to the initlization of thePlotDataItem
or is automatically be set in the case of the absence of xArray passed by the user (when initialization of a PlotDataItem directly usenp.arange
)I also added the properties
xOrigin
andxIncrement
which contains respectivelyx[0]
andx[1]-x[0]
PlotDataItem update problem bypass
Also there is a problem when you start a plot. The PlotDataItem gets updated before the autodownsample or downsample properties get passed to it by the
PlotItem
. To avoid that I must repeat the argumentsautoDownsample=1,downsample=None,downsampleMethod="subsample"
in theplot
function even if those properties are set in thePlotItem
.Not Fixed : many calls to
_getDisplayDataset
In general when you start a plot,
_getDislayDataset
is called around 5 times because of many functions calling updateItems. I do not think it is necessary and maybe you can find a workaround to increase performance.Further not tested ideas
Downsambling with "subsample" does not take much time but for the others method you might want to avoid to repeat that operation at each update, for example by saving resampled arrays somewhere (in _datasetMapped ?) for later reuse
If the autosampling mode is off and the cliptoview mode is on, only compute the resampled array once and apply cliptoview on the resampled array. Do not resample because of cliptoview.
Note that the condition above which forces the update has a redundancy. It it is also forced by the self._dataDisplay = None in viewRangeChanged
Beta Was this translation helpful? Give feedback.
All reactions