def new_value(value): if self.custom_step_size: step_amount = self.STEP_SIZES[self.step_size] time = fromtimestamp(self.slider.scale(value)) newTime = add_time(time, step_amount, -1 if backward else 1) return self.slider.unscale(timestamp(newTime)) return value + (-delta if backward else delta)
def unscale(self, value): # Unscale e.g. absolute time to slider value dt_val = fromtimestamp(round(value)) delta = self.__time_delta if isinstance(delta, Number): diff = dt_val - self.__scale_minimum return round(diff / datetime.timedelta(milliseconds=1000 * delta)) elif delta: if delta[1] == 'month': return self.minimum() + delta[0] \ * ((dt_val.year - self.__scale_minimum.year) * 12 + max(dt_val.month - self.__scale_minimum.month, 0)) else: # elif delta[1] == 'year': return self.minimum() + delta[0] \ * (dt_val.year - self.__scale_minimum.year) return self.minimum() + ( (dt_val - self.__scale_minimum) * (self.maximum() - self.minimum()) / (self.__scale_maximum - self.__scale_minimum) + self.minimum())
def setSeries(self, timeseries, attr, xdim, ydim, fagg): if timeseries is None or not attr: self.clear() return if isinstance(xdim, str) and xdim.isdigit(): xdim = [str(i) for i in range(1, int(xdim) + 1)] if isinstance(ydim, str) and ydim.isdigit(): ydim = [str(i) for i in range(1, int(ydim) + 1)] if isinstance(xdim, DiscreteVariable): xcol = timeseries.get_column_view(xdim)[0] xvals, xfunc = xdim.values, lambda i, _: xdim.repr_val(xcol[i]) else: xvals, xfunc = xdim.value if isinstance(ydim, DiscreteVariable): ycol = timeseries.get_column_view(ydim)[0] yvals, yfunc = ydim.values, lambda i, _: ydim.repr_val(ycol[i]) else: yvals, yfunc = ydim.value attr = attr[0] values = timeseries.get_column_view(attr)[0] time_values = [ fromtimestamp(i, tz=timeseries.time_variable.timezone) for i in timeseries.time_values ] if not yvals: yvals = sorted( set( yfunc(i, v) for i, v in enumerate(time_values) if v is not None)) if not xvals: xvals = sorted( set( xfunc(i, v) for i, v in enumerate(time_values) if v is not None)) indices = defaultdict(list) for i, tval in enumerate(time_values): if tval is not None: indices[(xfunc(i, tval), yfunc(i, tval))].append(i) if self._owwidget.invert_date_order: yvals = yvals[::-1] series = [] aggvals = [] self.indices = [] xname = self.AxesCategories.name_it(xdim) yname = self.AxesCategories.name_it(ydim) for yval in yvals: data = [] series.append(dict(name=yname(yval), data=data)) self.indices.append([]) for xval in xvals: inds = indices.get((xval, yval), ()) self.indices[-1].append(inds) point = dict(y=1) data.append(point) if inds: try: aggval = np.round(fagg(values[inds]), 4) except ValueError: aggval = np.nan else: aggval = np.nan if isinstance(aggval, Number) and np.isnan(aggval): aggval = 'N/A' point['select'] = '' point['color'] = 'white' else: aggvals.append(aggval) point['n'] = aggval # TODO: allow scaling over just rows or cols instead of all values as currently try: maxval, minval = np.max(aggvals), np.min(aggvals) except ValueError: self.clear() return ptpval = maxval - minval color = GradientPaletteGenerator('#ffcccc', '#cc0000') selected_color = GradientPaletteGenerator('#cdd1ff', '#0715cd') for serie in series: for point in serie['data']: n = point['n'] if isinstance(n, Number): val = (n - minval) / ptpval if attr.is_discrete: point['n'] = attr.repr_val(n) elif isinstance(attr, TimeVariable): point['n'] = attr.repr_val(n) if attr.is_discrete: point['color'] = color = color_to_hex( attr.colors[int(n)]) sel_color = QColor(color).darker(150).name() else: point['color'] = color[val] sel_color = selected_color[val] point['states'] = dict( select=dict(borderColor="black", color=sel_color)) # TODO: make a white hole in the middle. Center w/o data. self.chart(series=series, xAxis_categories=[xname(i) for i in xvals], yAxis_categories=[yname(i) for i in reversed(yvals)], javascript_after=''' // Force zoomType which is by default disabled for polar charts chart.options.chart.zoomType = 'xy'; chart.pointer.init(chart, chart.options); ''')
def time_key(i): return timestamp( aggreagate_time( fromtimestamp(data.time_values[i], tz=data.time_variable.timezone)))
def setSeries(self, timeseries, attr, xdim, ydim, fagg): if timeseries is None or not attr: self.clear() return if isinstance(xdim, str) and xdim.isdigit(): xdim = [str(i) for i in range(1, int(xdim) + 1)] if isinstance(ydim, str) and ydim.isdigit(): ydim = [str(i) for i in range(1, int(ydim) + 1)] if isinstance(xdim, DiscreteVariable): xcol = timeseries.get_column_view(xdim)[0] xvals, xfunc = xdim.values, lambda i, _: xdim.repr_val(xcol[i]) else: xvals, xfunc = xdim.value if isinstance(ydim, DiscreteVariable): ycol = timeseries.get_column_view(ydim)[0] yvals, yfunc = ydim.values, lambda i, _: ydim.repr_val(ycol[i]) else: yvals, yfunc = ydim.value attr = attr[0] values = timeseries.get_column_view(attr)[0] time_values = [fromtimestamp(i) for i in timeseries.time_values] if not yvals: yvals = sorted(set(yfunc(i, v) for i, v in enumerate(time_values) if v is not None)) if not xvals: xvals = sorted(set(xfunc(i, v) for i, v in enumerate(time_values) if v is not None)) indices = defaultdict(list) for i, tval in enumerate(time_values): if tval is not None: indices[(xfunc(i, tval), yfunc(i, tval))].append(i) if self._owwidget.invert_date_order: yvals = yvals[::-1] series = [] aggvals = [] self.indices = [] xname = self.AxesCategories.name_it(xdim) yname = self.AxesCategories.name_it(ydim) for yval in yvals: data = [] series.append(dict(name=yname(yval), data=data)) self.indices.append([]) for xval in xvals: inds = indices.get((xval, yval), ()) self.indices[-1].append(inds) point = dict(y=1) data.append(point) if inds: try: aggval = np.round(fagg(values[inds]), 4) except ValueError: aggval = np.nan else: aggval = np.nan if isinstance(aggval, Number) and np.isnan(aggval): aggval = 'N/A' point['select'] = '' point['color'] = 'white' else: aggvals.append(aggval) point['n'] = aggval # TODO: allow scaling over just rows or cols instead of all values as currently try: maxval, minval = np.max(aggvals), np.min(aggvals) except ValueError: self.clear() return ptpval = maxval - minval color = GradientPaletteGenerator('#ffcccc', '#cc0000') selected_color = GradientPaletteGenerator('#cdd1ff', '#0715cd') for serie in series: for point in serie['data']: n = point['n'] if isinstance(n, Number): val = (n - minval) / ptpval if attr.is_discrete: point['n'] = attr.repr_val(n) elif isinstance(attr, TimeVariable): point['n'] = attr.repr_val(n) if attr.is_discrete: point['color'] = color = color_to_hex(attr.colors[int(n)]) sel_color = QColor(color).darker(150).name() else: point['color'] = color[val] sel_color = selected_color[val] point['states'] = dict(select=dict(borderColor="black", color=sel_color)) # TODO: make a white hole in the middle. Center w/o data. self.chart(series=series, xAxis_categories=[xname(i) for i in xvals], yAxis_categories=[yname(i) for i in reversed(yvals)], javascript_after=''' // Force zoomType which is by default disabled for polar charts chart.options.chart.zoomType = 'xy'; chart.pointer.init(chart, chart.options); ''')
def set_data(self, data): slider = self.slider self.data = data = None if data is None else Timeseries.from_data_table( data) def disabled(): slider.setFormatter(str) slider.setHistogram(None) slider.setScale(0, 0, None) slider.setValues(0, 0) self._set_disabled(True) self.Outputs.subset.send(None) if data is None: disabled() return if not isinstance(data.time_variable, TimeVariable): self.Error.no_time_variable() disabled() return if not data.time_delta.deltas: self.Error.no_time_delta() disabled() return self.Error.clear() var = data.time_variable time_values = data.time_values min_dt = fromtimestamp(round(time_values.min())) max_dt = fromtimestamp(round(time_values.max())) # Depending on time delta: # - set slider maximum (granularity) # - set range for end dt (+ 1 timedelta) # - set date format # - set time overlap options delta = data.time_delta.gcd range = max_dt - min_dt if isinstance(delta, Number): maximum = round(range.total_seconds() / delta) timedelta = datetime.timedelta(milliseconds=delta * 1000) min_dt2 = min_dt + timedelta max_dt2 = max_dt + timedelta if delta >= 86400: # more than a day date_format = ''.join(self.DATE_FORMATS[0:3]) else: date_format = ''.join(self.DATE_FORMATS) for k, n in [(k, n) for k, n in self.STEP_SIZES.items() if isinstance(n, Number)]: if delta <= n: min_overlap = k break else: min_overlap = '1 day' else: # isinstance(delta, tuple) if delta[1] == 'month': months = (max_dt.year - min_dt.year) * 12 + \ (max_dt.month - min_dt.month) maximum = months / delta[0] if min_dt.month < 12 - delta[0]: min_dt2 = min_dt.replace(month=min_dt.month + delta[0]) else: min_dt2 = min_dt.replace(year=min_dt.year + 1, month=12 - min_dt.month + delta[0]) if max_dt.month < 12 - delta[0]: max_dt2 = max_dt.replace(month=max_dt.month + delta[0]) else: max_dt2 = max_dt.replace(year=max_dt.year + 1, month=12 - min_dt.month + delta[0]) date_format = ''.join(self.DATE_FORMATS[0:2]) for k, (i, u) in [(k, v) for k, v in self.STEP_SIZES.items() if isinstance(v, tuple) and v[1] == 'month']: if delta[0] <= i: min_overlap = k break else: min_overlap = '1 year' else: # elif delta[1] == 'year': years = max_dt.year - min_dt.year maximum = years / delta[0] min_dt2 = min_dt.replace(year=min_dt.year + delta[0], ) max_dt2 = max_dt.replace(year=max_dt.year + delta[0], ) date_format = self.DATE_FORMATS[0] for k, (i, u) in [(k, v) for k, v in self.STEP_SIZES.items() if isinstance(v, tuple) and v[1] == 'year']: if delta[0] <= i: min_overlap = k break else: raise Exception('Timedelta larger than 100 years') # find max sensible time overlap upper_overlap_limit = range / 2 for k, overlap in self.STEP_SIZES.items(): if isinstance(overlap, Number): if upper_overlap_limit.total_seconds() <= overlap: max_overlap = k break else: i, u = overlap if u == 'month': month_diff = (max_dt.year - min_dt.year) * 12 \ + max(0, max_dt.month - min_dt.month) if month_diff / 2 <= i: max_overlap = k break else: # if u == 'year': year_diff = max_dt.year - min_dt.year if year_diff / 2 <= i: max_overlap = k break else: # last item in step sizes *_, max_overlap = self.STEP_SIZES.keys() self.stepsize_combobox.clear() dict_iter = iter(self.STEP_SIZES.keys()) next_item = next(dict_iter) while next_item != min_overlap: next_item = next(dict_iter) self.stepsize_combobox.addItem(next_item) self.step_size = next_item while next_item != max_overlap: next_item = next(dict_iter) self.stepsize_combobox.addItem(next_item) slider.setMinimum(0) slider.setMaximum(int(maximum + 1)) self._set_disabled(False) slider.setHistogram(time_values) slider.setFormatter(var.repr_val) slider.setScale(time_values.min(), time_values.max(), data.time_delta.gcd) self.sliderValuesChanged(slider.minimumValue(), slider.maximumValue()) def utc_dt(dt): qdt = QDateTime(dt) qdt.setTimeZone(QTimeZone.utc()) return qdt self.date_from.setDateTimeRange(utc_dt(min_dt), utc_dt(max_dt)) self.date_to.setDateTimeRange(utc_dt(min_dt2), utc_dt(max_dt2)) self.date_from.setDisplayFormat(date_format) self.date_to.setDisplayFormat(date_format) def format_time(i): dt = QDateTime.fromMSecsSinceEpoch(i * 1000).toUTC() return dt.toString(date_format) self.slider.setFormatter(format_time)
def setScale(self, minimum, maximum, time_delta): self.__scale_minimum = fromtimestamp(round(minimum)) self.__scale_maximum = fromtimestamp(round(maximum)) self.__time_delta = time_delta
def time_key(i): return timestamp(aggreagate_time(fromtimestamp(data.time_values[i])))