qplotter 1.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qplot/__init__.py +46 -0
- qplot/__main__.py +232 -0
- qplot/_auxiliary_paths.py +67 -0
- qplot/_build_info.json +1 -0
- qplot/_metadata.py +75 -0
- qplot/_repair.py +14 -0
- qplot/_shutdown_supervisor.py +4049 -0
- qplot/_version.py +12 -0
- qplot/_windows_shutdown_job.py +1166 -0
- qplot/configuration/__init__.py +3 -0
- qplot/configuration/config.py +578 -0
- qplot/configuration/config_schema.json +357 -0
- qplot/configuration/fonts.py +3 -0
- qplot/configuration/scripts.py +255 -0
- qplot/configuration/themes/__init__.py +13 -0
- qplot/configuration/themes/_base.py +592 -0
- qplot/configuration/themes/blank.py +17 -0
- qplot/configuration/themes/dark.py +54 -0
- qplot/configuration/themes/light.py +54 -0
- qplot/datahandling/LoadFromDB.py +224 -0
- qplot/datahandling/__init__.py +47 -0
- qplot/datahandling/_trusted_live_helper.py +812 -0
- qplot/datahandling/_trusted_live_protocol.py +1745 -0
- qplot/datahandling/database.py +2340 -0
- qplot/datahandling/dimensions.py +43 -0
- qplot/datahandling/file_identity.py +461 -0
- qplot/datahandling/parameter_data.py +149 -0
- qplot/datahandling/plot_progress.py +17 -0
- qplot/datahandling/qcodes_cache.py +277 -0
- qplot/datahandling/qcodes_compat.py +14 -0
- qplot/datahandling/readSQL.py +2442 -0
- qplot/datahandling/readonly.py +2566 -0
- qplot/datahandling/source_activity.py +21 -0
- qplot/datahandling/trusted_array_preview.py +148 -0
- qplot/datahandling/trusted_derived_cache.py +1605 -0
- qplot/datahandling/trusted_derived_rendering.py +876 -0
- qplot/datahandling/trusted_heatmap.py +246 -0
- qplot/datahandling/trusted_live.py +2808 -0
- qplot/datahandling/trusted_live_queries.py +5907 -0
- qplot/datahandling/trusted_live_service.py +1919 -0
- qplot/datahandling/trusted_live_supervisor.py +2161 -0
- qplot/datahandling/trusted_plot.py +283 -0
- qplot/datahandling/trusted_presentation.py +1065 -0
- qplot/datahandling/trusted_snapshot.py +1132 -0
- qplot/datahandling/trusted_work_coordinator.py +2157 -0
- qplot/datahandling/trusted_work_scheduler.py +1204 -0
- qplot/diagnostics.py +241 -0
- qplot/resources/testdata/qplot_test_db_01_10mb.csv +11 -0
- qplot/resources/testdata/qplot_test_db_02_25mb.csv +14 -0
- qplot/resources/testdata/qplot_test_db_03_50mb.csv +17 -0
- qplot/resources/testdata/qplot_test_db_04_100mb.csv +20 -0
- qplot/resources/testdata/qplot_test_db_05_250mb.csv +23 -0
- qplot/resources/testdata/qplot_test_db_06_500mb.csv +26 -0
- qplot/resources/testdata/qplot_test_db_07_1gb.csv +29 -0
- qplot/resources/testdata/qplot_test_db_08_5gb.csv +32 -0
- qplot/resources/testdata/qplot_test_db_09_10gb.csv +35 -0
- qplot/resources/testdata/qplot_test_db_10_30gb.csv +38 -0
- qplot/testdata.py +1804 -0
- qplot/tools/__init__.py +13 -0
- qplot/tools/finite_means.py +123 -0
- qplot/tools/general.py +139 -0
- qplot/tools/heatmap_geometry.py +391 -0
- qplot/tools/operation_registry.py +179 -0
- qplot/tools/plot_tools.py +650 -0
- qplot/tools/sample_statistics.py +111 -0
- qplot/tools/worker.py +2838 -0
- qplot/windows/__init__.py +9 -0
- qplot/windows/_colorbar.py +422 -0
- qplot/windows/_commands.py +523 -0
- qplot/windows/_config_persistence.py +123 -0
- qplot/windows/_database_actions.py +4933 -0
- qplot/windows/_dataset_handle.py +221 -0
- qplot/windows/_display_precision.py +48 -0
- qplot/windows/_dragdrop.py +70 -0
- qplot/windows/_export_paths.py +1167 -0
- qplot/windows/_help.py +210 -0
- qplot/windows/_mixed_appearance.py +303 -0
- qplot/windows/_mixed_plots.py +461 -0
- qplot/windows/_native_averaging.py +323 -0
- qplot/windows/_native_transforms.py +551 -0
- qplot/windows/_plot1d_snap.py +612 -0
- qplot/windows/_plot1d_traces.py +2236 -0
- qplot/windows/_plot2d_colorbar.py +1127 -0
- qplot/windows/_plot2d_colorbar_dialog.py +786 -0
- qplot/windows/_plot2d_layers.py +2291 -0
- qplot/windows/_plot2d_sweeps.py +712 -0
- qplot/windows/_plotWin.py +2082 -0
- qplot/windows/_plot_actions.py +2703 -0
- qplot/windows/_plot_annotation.py +186 -0
- qplot/windows/_plot_appearance.py +283 -0
- qplot/windows/_plot_axis_scaling.py +1521 -0
- qplot/windows/_plot_data_viewbox.py +94 -0
- qplot/windows/_plot_drop_axes.py +224 -0
- qplot/windows/_plot_export.py +1445 -0
- qplot/windows/_plot_feedback.py +112 -0
- qplot/windows/_plot_marquee.py +659 -0
- qplot/windows/_plot_refresh.py +950 -0
- qplot/windows/_plot_source_promotion.py +178 -0
- qplot/windows/_plot_state.py +237 -0
- qplot/windows/_plot_status.py +115 -0
- qplot/windows/_plot_tiling.py +35 -0
- qplot/windows/_plot_transform_labels.py +60 -0
- qplot/windows/_preferences.py +724 -0
- qplot/windows/_refresh_interval.py +172 -0
- qplot/windows/_run_controls.py +789 -0
- qplot/windows/_shortcuts.py +47 -0
- qplot/windows/_subplots/__init__.py +8 -0
- qplot/windows/_subplots/subplot1d.py +532 -0
- qplot/windows/_subplots/subplot2d.py +919 -0
- qplot/windows/_svg_export.py +209 -0
- qplot/windows/_trusted_derived_qt.py +1759 -0
- qplot/windows/_widgets/__init__.py +23 -0
- qplot/windows/_widgets/_run_formatting.py +387 -0
- qplot/windows/_widgets/details_tables.py +1119 -0
- qplot/windows/_widgets/dropbox.py +290 -0
- qplot/windows/_widgets/operations.py +460 -0
- qplot/windows/_widgets/preview.py +2997 -0
- qplot/windows/_widgets/run_list_items.py +488 -0
- qplot/windows/_widgets/toolbar.py +111 -0
- qplot/windows/_widgets/treeWidgets.py +2572 -0
- qplot/windows/_window_controls.py +414 -0
- qplot/windows/main.py +1632 -0
- qplot/windows/plot1d.py +313 -0
- qplot/windows/plot2d.py +1935 -0
- qplotter-1.6.0.dist-info/METADATA +322 -0
- qplotter-1.6.0.dist-info/RECORD +130 -0
- qplotter-1.6.0.dist-info/WHEEL +5 -0
- qplotter-1.6.0.dist-info/entry_points.txt +4 -0
- qplotter-1.6.0.dist-info/licenses/LICENSE +21 -0
- qplotter-1.6.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""Average displayed source curves without processing generated curves again."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from fractions import Fraction
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pyqtgraph as pg
|
|
9
|
+
from PyQt6 import QtCore
|
|
10
|
+
from pyqtgraph.graphicsItems.PlotDataItem import PlotDataset
|
|
11
|
+
|
|
12
|
+
from qplot.tools.sample_statistics import finite_mean, sample_fraction
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _integer_totals(total, values, weight=1):
|
|
16
|
+
"""Accumulate in int64 when proven safe, otherwise use Python integers."""
|
|
17
|
+
lower = weight * int(np.min(values))
|
|
18
|
+
upper = weight * int(np.max(values))
|
|
19
|
+
if total is not None:
|
|
20
|
+
lower += int(np.min(total))
|
|
21
|
+
upper += int(np.max(total))
|
|
22
|
+
if np.iinfo(np.int64).min <= lower and upper <= np.iinfo(np.int64).max:
|
|
23
|
+
# A large unsigned input may wrap before cancellation restores an
|
|
24
|
+
# int64 total; only choose this path when each operand fits too.
|
|
25
|
+
if (int(np.min(values)) >= np.iinfo(np.int64).min
|
|
26
|
+
and int(np.max(values)) <= np.iinfo(np.int64).max
|
|
27
|
+
and (total is None or total.dtype.kind == "i")):
|
|
28
|
+
weighted = values.astype(np.int64) * weight
|
|
29
|
+
return weighted if total is None else total + weighted
|
|
30
|
+
weighted = values.astype(object) * weight
|
|
31
|
+
return weighted if total is None else total.astype(object) + weighted
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _integer_average(total, count):
|
|
35
|
+
# A representable integer numerator needs only the ordinary vectorized
|
|
36
|
+
# division. Otherwise convert each NumPy integer to Python before dividing
|
|
37
|
+
# so conversion cannot round the total prior to forming the mean.
|
|
38
|
+
exact_float_bound = 2 ** (np.finfo(float).nmant + 1)
|
|
39
|
+
if (count <= exact_float_bound and total.dtype.kind != "O"
|
|
40
|
+
and int(np.min(total)) >= -exact_float_bound
|
|
41
|
+
and int(np.max(total)) <= exact_float_bound):
|
|
42
|
+
return total / count
|
|
43
|
+
return np.fromiter((int(value) / count for value in total), dtype=float, count=len(total))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _binary_integer_samples(values):
|
|
47
|
+
"""Represent finite binary samples as integer numerators and one exponent."""
|
|
48
|
+
if values.dtype.kind in "iu":
|
|
49
|
+
return values, 0
|
|
50
|
+
if values.dtype.kind != "f" or not np.all(np.isfinite(values)):
|
|
51
|
+
return None
|
|
52
|
+
if np.finfo(values.dtype).nmant > np.finfo(float).nmant:
|
|
53
|
+
return None
|
|
54
|
+
if np.all(values == np.trunc(values)):
|
|
55
|
+
shift = 0
|
|
56
|
+
else:
|
|
57
|
+
nonzero = np.abs(values[values != 0])
|
|
58
|
+
shift = max(0, 53 - math.frexp(float(np.min(nonzero)))[1])
|
|
59
|
+
with np.errstate(over="ignore"):
|
|
60
|
+
scaled = np.ldexp(values, shift)
|
|
61
|
+
if not np.all(np.isfinite(scaled)):
|
|
62
|
+
return None
|
|
63
|
+
if np.min(scaled) >= -(2**63) and np.max(scaled) < 2**63:
|
|
64
|
+
return scaled.astype(np.int64), shift
|
|
65
|
+
return np.frompyfunc(int, 1, 1)(scaled), shift
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _add_binary_total(total, shift, values, weight=1, *, finite_cells=None):
|
|
69
|
+
if finite_cells is not None:
|
|
70
|
+
# Nonfinite cells keep the ordinary running result. Exclude only
|
|
71
|
+
# those positions from exact encoding, not the entire source trace.
|
|
72
|
+
values = np.where(finite_cells, values, 0)
|
|
73
|
+
encoded = _binary_integer_samples(values)
|
|
74
|
+
if encoded is None:
|
|
75
|
+
return None, 0
|
|
76
|
+
numerators, value_shift = encoded
|
|
77
|
+
common_shift = max(shift, value_shift)
|
|
78
|
+
if total is not None and shift != common_shift:
|
|
79
|
+
total = total.astype(object) * (1 << (common_shift - shift))
|
|
80
|
+
if value_shift != common_shift:
|
|
81
|
+
numerators = numerators.astype(object) * (1 << (common_shift - value_shift))
|
|
82
|
+
return _integer_totals(total, numerators, weight), common_shift
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class _AveragePlotDataItem(pg.PlotDataItem):
|
|
86
|
+
"""Render samples already mapped, clipped and downsampled by their sources."""
|
|
87
|
+
|
|
88
|
+
def _ignore_processing(self, *_args, **_kwargs):
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
# PlotItem.addItem and its controls configure every PlotDataItem. These
|
|
92
|
+
# generated items contain display coordinates, so all processing is done.
|
|
93
|
+
setFftMode = _ignore_processing
|
|
94
|
+
setDerivativeMode = _ignore_processing
|
|
95
|
+
setPhasemapMode = _ignore_processing
|
|
96
|
+
setSubtractMeanMode = _ignore_processing
|
|
97
|
+
setLogMode = _ignore_processing
|
|
98
|
+
setDownsampling = _ignore_processing
|
|
99
|
+
setClipToView = _ignore_processing
|
|
100
|
+
|
|
101
|
+
def _getDisplayDataset(self) -> PlotDataset | None:
|
|
102
|
+
return self._dataset
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def is_native_source_curve(item: object) -> bool:
|
|
106
|
+
"""Generated averages are render results, never transform/validation inputs."""
|
|
107
|
+
return isinstance(item, pg.PlotDataItem) and not isinstance(item, _AveragePlotDataItem)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class NativePlotItem(pg.PlotItem):
|
|
111
|
+
"""Keep PyQtGraph's native grouping and positional averaging semantics."""
|
|
112
|
+
|
|
113
|
+
_qplot_recomputing_averages = False
|
|
114
|
+
avgCurves: dict[tuple, list[Any]]
|
|
115
|
+
_qplot_average_display_sources: tuple[tuple[Any, Any, Any], ...]
|
|
116
|
+
|
|
117
|
+
def __init__(self, *args, **kwargs):
|
|
118
|
+
super().__init__(*args, **kwargs)
|
|
119
|
+
self._qplot_average_display_sources = ()
|
|
120
|
+
# An owned timer is cancelled with the plot's QObject, including a
|
|
121
|
+
# close before the queued display update reaches the event loop.
|
|
122
|
+
self._qplot_average_refresh_timer = QtCore.QTimer(self)
|
|
123
|
+
self._qplot_average_refresh_timer.setSingleShot(True)
|
|
124
|
+
self._qplot_average_refresh_timer.timeout.connect(self._refresh_view_averages)
|
|
125
|
+
|
|
126
|
+
def addItem(self, item, *args, **kwargs):
|
|
127
|
+
if is_native_source_curve(item) and item not in self.items:
|
|
128
|
+
item._qplot_skip_average = "skipAverage" in kwargs
|
|
129
|
+
super().addItem(item, *args, **kwargs)
|
|
130
|
+
item.sigPlotChanged.connect(self.recomputeAverages)
|
|
131
|
+
display_changed = getattr(item, "sigDisplayChanged", None)
|
|
132
|
+
if display_changed is not None:
|
|
133
|
+
display_changed.connect(self._queue_view_averages)
|
|
134
|
+
else:
|
|
135
|
+
super().addItem(item, *args, **kwargs)
|
|
136
|
+
|
|
137
|
+
def removeItem(self, item):
|
|
138
|
+
source = is_native_source_curve(item) and item in self.items
|
|
139
|
+
if source:
|
|
140
|
+
item.sigPlotChanged.disconnect(self.recomputeAverages)
|
|
141
|
+
display_changed = getattr(item, "sigDisplayChanged", None)
|
|
142
|
+
if display_changed is not None:
|
|
143
|
+
display_changed.disconnect(self._queue_view_averages)
|
|
144
|
+
super().removeItem(item)
|
|
145
|
+
if source:
|
|
146
|
+
self.recomputeAverages()
|
|
147
|
+
|
|
148
|
+
def clear(self):
|
|
149
|
+
# Removing every source is one action. Intermediate averages cannot
|
|
150
|
+
# be displayed and can otherwise stall clearing a large grouped plot.
|
|
151
|
+
recomputing = self._qplot_recomputing_averages
|
|
152
|
+
self._qplot_recomputing_averages = True
|
|
153
|
+
try:
|
|
154
|
+
super().clear()
|
|
155
|
+
finally:
|
|
156
|
+
self._qplot_recomputing_averages = recomputing
|
|
157
|
+
self._qplot_average_display_sources = ()
|
|
158
|
+
timer = getattr(self, "_qplot_average_refresh_timer", None)
|
|
159
|
+
if timer is not None:
|
|
160
|
+
timer.stop()
|
|
161
|
+
|
|
162
|
+
def recomputeAverages(self, *_args):
|
|
163
|
+
if not self.ctrl.averageGroup.isChecked():
|
|
164
|
+
self._qplot_average_display_sources = ()
|
|
165
|
+
return
|
|
166
|
+
if self._qplot_recomputing_averages:
|
|
167
|
+
return
|
|
168
|
+
self._qplot_recomputing_averages = True
|
|
169
|
+
try:
|
|
170
|
+
for _count, average in tuple(self.avgCurves.values()):
|
|
171
|
+
self.removeItem(average)
|
|
172
|
+
self.avgCurves = {}
|
|
173
|
+
# addAvgCurve adds rendered averages to PlotItem.curves. Snapshot
|
|
174
|
+
# only measured inputs so those additions cannot feed back in.
|
|
175
|
+
sources = tuple(curve for curve in self.curves if is_native_source_curve(curve))
|
|
176
|
+
for curve in sources:
|
|
177
|
+
self.addAvgCurve(curve)
|
|
178
|
+
self._qplot_average_display_sources = self._average_display_sources()
|
|
179
|
+
self.replot()
|
|
180
|
+
finally:
|
|
181
|
+
self._qplot_recomputing_averages = False
|
|
182
|
+
|
|
183
|
+
def _average_display_sources(self):
|
|
184
|
+
return tuple((curve, *curve.getData()) for curve in self.curves
|
|
185
|
+
if is_native_source_curve(curve)
|
|
186
|
+
and not getattr(curve, "_qplot_skip_average", False))
|
|
187
|
+
|
|
188
|
+
def _queue_view_averages(self, *_args):
|
|
189
|
+
if (self.ctrl.averageGroup.isChecked()
|
|
190
|
+
and not self._qplot_average_refresh_timer.isActive()):
|
|
191
|
+
self._qplot_average_refresh_timer.start(0)
|
|
192
|
+
|
|
193
|
+
def _refresh_view_averages(self):
|
|
194
|
+
if not self.ctrl.averageGroup.isChecked():
|
|
195
|
+
return
|
|
196
|
+
current = self._average_display_sources()
|
|
197
|
+
previous = self._qplot_average_display_sources
|
|
198
|
+
if (len(current) == len(previous)
|
|
199
|
+
and all(all(now is old for now, old in zip(current_row, previous_row, strict=True))
|
|
200
|
+
for current_row, previous_row in zip(current, previous, strict=True))):
|
|
201
|
+
return
|
|
202
|
+
self.recomputeAverages()
|
|
203
|
+
|
|
204
|
+
def addAvgCurve(self, curve):
|
|
205
|
+
if not is_native_source_curve(curve) or getattr(curve, "_qplot_skip_average", False):
|
|
206
|
+
return
|
|
207
|
+
x, y = curve.getData()
|
|
208
|
+
if x is None or y is None or len(y) == 0:
|
|
209
|
+
return
|
|
210
|
+
# The average is display data. Widen before arithmetic without
|
|
211
|
+
# modifying the measured arrays retained by the source curves.
|
|
212
|
+
if y.dtype.kind in "fc":
|
|
213
|
+
y = y.astype(np.result_type(y.dtype, np.float64), copy=False)
|
|
214
|
+
removed: list[str] = []
|
|
215
|
+
retained: list[str] = []
|
|
216
|
+
for index in range(self.ctrl.avgParamList.count()):
|
|
217
|
+
item = self.ctrl.avgParamList.item(index)
|
|
218
|
+
target = removed if item.checkState() == QtCore.Qt.CheckState.Checked else retained
|
|
219
|
+
target.append(str(item.text()))
|
|
220
|
+
if self.ctrl.avgParamList.count() and not removed:
|
|
221
|
+
return
|
|
222
|
+
params = {
|
|
223
|
+
".".join(key) if isinstance(key, tuple) else key: value
|
|
224
|
+
for key, value in self.itemMeta.get(curve, {}).items()
|
|
225
|
+
}
|
|
226
|
+
for key in removed:
|
|
227
|
+
params.pop(key, None)
|
|
228
|
+
for key in retained:
|
|
229
|
+
params.setdefault(key, None)
|
|
230
|
+
group_key = tuple(params.items())
|
|
231
|
+
if group_key not in self.avgCurves:
|
|
232
|
+
average = _AveragePlotDataItem()
|
|
233
|
+
average.setPen(self.avgPen)
|
|
234
|
+
average.setShadowPen(self.avgShadowPen)
|
|
235
|
+
average.setAlpha(1., False)
|
|
236
|
+
average.setZValue(100)
|
|
237
|
+
self.addItem(average, skipAverage=True)
|
|
238
|
+
self.avgCurves[group_key] = [0, average]
|
|
239
|
+
self.avgCurves[group_key][0] += 1
|
|
240
|
+
count, average = self.avgCurves[group_key]
|
|
241
|
+
# Preserve native compatibility: combine equal-shaped Y by index and
|
|
242
|
+
# keep the first X; a changed Y shape replaces the displayed samples.
|
|
243
|
+
if average.yData is not None and y.shape == average.yData.shape:
|
|
244
|
+
average._qplot_mean_sources.append((1, y))
|
|
245
|
+
average._qplot_minimum = np.minimum(average._qplot_minimum, y)
|
|
246
|
+
average._qplot_maximum = np.maximum(average._qplot_maximum, y)
|
|
247
|
+
# A difference update retains small variations and constant
|
|
248
|
+
# values near float's upper bound. Opposite extremes can overflow
|
|
249
|
+
# that difference; bounded weights handle those cells and preserve
|
|
250
|
+
# the ordinary arithmetic behavior of NaN and infinite samples.
|
|
251
|
+
y = y.astype(np.result_type(y.dtype, np.float64), copy=False)
|
|
252
|
+
previous = average.yData.astype(
|
|
253
|
+
np.result_type(average.yData.dtype, np.float64), copy=False,
|
|
254
|
+
)
|
|
255
|
+
with np.errstate(over="ignore", invalid="ignore"):
|
|
256
|
+
difference = y - previous
|
|
257
|
+
updated = previous + difference / float(count)
|
|
258
|
+
fallback = ~np.isfinite(difference)
|
|
259
|
+
if np.any(fallback):
|
|
260
|
+
updated[fallback] = (
|
|
261
|
+
previous[fallback] * ((count - 1) / float(count))
|
|
262
|
+
+ y[fallback] / float(count)
|
|
263
|
+
)
|
|
264
|
+
y = updated
|
|
265
|
+
x = average.xData
|
|
266
|
+
# A rounded running mean loses small measurements when later
|
|
267
|
+
# traces cancel a large offset. Revisit sensitive cells using
|
|
268
|
+
# references to display samples already owned by sources.
|
|
269
|
+
sources = average._qplot_mean_sources
|
|
270
|
+
exact_cells = any(values.dtype.kind in "iuO" for _weight, values in sources)
|
|
271
|
+
mixed_sign = (average._qplot_minimum < 0) & (average._qplot_maximum > 0)
|
|
272
|
+
# Integer and cancelling binary means can round at intermediate
|
|
273
|
+
# updates. Retain integer totals in common binary units, then
|
|
274
|
+
# round their final ratio once. This also avoids per-cell replay
|
|
275
|
+
# across ordinary mixed-sign float traces on the GUI thread.
|
|
276
|
+
integer_total = average._qplot_integer_total
|
|
277
|
+
shift = average._qplot_total_shift
|
|
278
|
+
finite_cells = (np.isfinite(average._qplot_minimum)
|
|
279
|
+
& np.isfinite(average._qplot_maximum))
|
|
280
|
+
finite_mask = None if np.all(finite_cells) else finite_cells
|
|
281
|
+
if exact_cells or integer_total is not None or np.any(mixed_sign):
|
|
282
|
+
if not np.any(finite_cells):
|
|
283
|
+
integer_total, shift = None, 0
|
|
284
|
+
elif integer_total is None:
|
|
285
|
+
# A later integer or sign change can require exact totals.
|
|
286
|
+
# Encode retained samples once, then update per curve.
|
|
287
|
+
for weight, values in sources:
|
|
288
|
+
integer_total, shift = _add_binary_total(
|
|
289
|
+
integer_total, shift, values, weight, finite_cells=finite_mask,
|
|
290
|
+
)
|
|
291
|
+
if integer_total is None:
|
|
292
|
+
break
|
|
293
|
+
else:
|
|
294
|
+
integer_total, shift = _add_binary_total(
|
|
295
|
+
integer_total, shift, sources[-1][1], finite_cells=finite_mask,
|
|
296
|
+
)
|
|
297
|
+
average._qplot_integer_total = integer_total
|
|
298
|
+
average._qplot_total_shift = shift
|
|
299
|
+
if integer_total is not None:
|
|
300
|
+
exact_average = _integer_average(integer_total, count * (1 << shift))
|
|
301
|
+
y = exact_average if finite_mask is None else np.where(finite_cells, exact_average, y)
|
|
302
|
+
repair = (mixed_sign | exact_cells) & finite_cells
|
|
303
|
+
if integer_total is not None:
|
|
304
|
+
repair[:] = False
|
|
305
|
+
for cell_index in np.flatnonzero(repair):
|
|
306
|
+
cells = np.array([values[cell_index] for _weight, values in sources],
|
|
307
|
+
dtype=object if exact_cells else None)
|
|
308
|
+
if all(weight == 1 for weight, _values in sources):
|
|
309
|
+
y[cell_index] = finite_mean(cells)
|
|
310
|
+
else:
|
|
311
|
+
# Native shape replacement treats the replacement as
|
|
312
|
+
# the preceding count of curves, retaining that weight.
|
|
313
|
+
total = sum((weight * sample_fraction(value)
|
|
314
|
+
for (weight, _values), value in zip(sources, cells, strict=True)),
|
|
315
|
+
Fraction())
|
|
316
|
+
y[cell_index] = float(total / count)
|
|
317
|
+
else:
|
|
318
|
+
average._qplot_mean_sources = [(count, y)]
|
|
319
|
+
average._qplot_minimum = y.copy()
|
|
320
|
+
average._qplot_maximum = y.copy()
|
|
321
|
+
average._qplot_integer_total = _integer_totals(None, y, count) if y.dtype.kind in "iu" else None
|
|
322
|
+
average._qplot_total_shift = 0
|
|
323
|
+
average.setData(x, y, stepMode=curve.opts["stepMode"])
|