def __init__(self, parent=None, field=None, units=None, cmdp=None): pg.GraphicsWidget.__init__(self, parent=parent) self._field = field self._units = units self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self._label = QtWidgets.QGraphicsTextItem(self) self._label.setVisible(True) self._label.document().setUseDesignMetrics(True) self._value_cache = None self._cmdp = cmdp labels = single_stat_to_api(-0.000000001, 0.000001, -0.001, 0.001, self._units) self.data_update(labels) self._resize() self.data_clear() cmdp.subscribe('Widgets/Waveform/Statistics/font-color', self._on_font_color, update_now=True) pg.GraphicsWidget.show(self)
def statistics_at(self, x): """Get the statistics at the provided x value. :param x: The x-axis value in seconds. :return: The dict mapping parameter name to float value. """ if self._most_recent_data is None: return {} z_x, z_mean, z_var, z_min, z_max = self._most_recent_data if not z_x[0] <= x <= z_x[-1]: return {} idx = np.argmax(z_x >= x) y_mean = float(z_mean[idx]) if not np.isfinite(y_mean): return {} if z_min is not None and np.isfinite(z_min[idx]): y_var = float(z_var[idx]) y_min = float(z_min[idx]) y_max = float(z_max[idx]) labels = single_stat_to_api(y_mean, y_var, y_min, y_max, self.units) else: labels = {'µ': {'value': y_mean, 'units': self.units}} return labels
def update(self, x, value): """Update the signal data. :param x: The length N array of x-axis time in seconds. :param value: The y-axis data which can be: * length N array * length Nx4 array of [mean, var, min, max]. Note that var, min, max may be NaN when not available. """ if self.text_item: self.text_item.data_clear() if x is None or value is None or len(x) <= 1: self.data_clear() return # get the mean value regardless of shape z_mean = value['µ']['value'] z_var = value['σ2']['value'] z_min = value['min']['value'] z_max = value['max']['value'] self._most_recent_data = [x, z_mean, z_var, z_min, z_max] # get the valid mean values regardless of shape z_valid = np.isfinite(z_mean) x = x[z_valid] z_mean_valid = z_mean[z_valid] if not len(z_mean_valid): if len(z_mean): pass # self.log.debug('no valid data to display: %d -> %d', len(z_mean), len(z_mean_valid)) if self.text_item: self.text_item.data_clear() return x_range = float(x[-1] - x[0]) z_mean = z_mean_valid z_min = z_min[z_valid] if np.isfinite(z_min[0]): z_var = z_var[z_valid] z_max = z_max[z_valid] # compute statistics over the visible window z = z_mean self.curve_mean.setData(x, self._log_bound(z)) self.curve_mean.show() if not np.isfinite(z_min[0]): self._min_max_disable() v_mean = np.mean(z).item() v_var = np.var(z).item() v_max = np.max(z).item() v_min = np.min(z).item() else: self._min_max_enable() v_mean = np.mean(z_mean).item() v_min = np.min(z_min).item() v_max = np.max(z_max).item() mean_delta = z_mean - v_mean # combine variances across the combined samples v_var = np.sum(np.square(mean_delta, out=mean_delta) + z_var) / len(z_mean) v_var = v_var.item() self.curve_min.setData(x, self._log_bound(z_min)) self.curve_max.setData(x, self._log_bound(z_max)) if self._cmdp['Widgets/Waveform/show_min_max'] == 'off': # use min/max of the mean trace for y-axis autoranging (not actual min/max) v_min = np.min(z_mean).item() v_max = np.max(z_mean).item() if not np.isfinite(v_min) or not np.isfinite( v_max) or np.abs(v_min) > 1000 or np.abs(v_max) > 1000: self.log.warning('signal.update(%r, %r)' % (v_min, v_max)) if self.text_item is not None: labels = single_stat_to_api(v_mean, v_var, v_min, v_max, self.units) integration_units = INTEGRATION_UNITS.get(self.units) if integration_units is not None: labels['∫'] = { 'value': v_mean * x_range, 'units': integration_units } labels['Δt'] = {'value': x_range, 'units': 's'} self.text_item.data_update(labels) self.yaxis_autorange(v_min, v_max)