Exemplo n.º 1
0
    def commit(self):
        data = self.data
        if not data or not len(self.selected):
            self.Outputs.time_series.send(None)
            return

        X = []
        attrs = []
        invert = self.invert_direction
        shift = self.shift_period
        order = self.diff_order
        op = self.chosen_operation

        for var in self.selected:
            col = np.ravel(data[:, var])

            if invert:
                col = col[::-1]

            out = np.empty(len(col))
            if op == self.Operation.DIFF and shift == 1:
                out[order:] = np.diff(col, order)
                out[:order] = np.nan
            else:
                if op == self.Operation.DIFF:
                    out[shift:] = col[shift:] - col[:-shift]
                else:
                    out[shift:] = np.divide(col[shift:], col[:-shift])
                    if op == self.Operation.PERC:
                        out = (out - 1) * 100
                out[:shift] = np.nan

            if invert:
                out = out[::-1]

            X.append(out)

            if op == self.Operation.DIFF and shift == 1:
                details = f'order={order}'
            else:
                details = f'shift={shift}'

            template = f'{var} ({op[:4].lower()}; {details})'
            name = available_name(data.domain, template)
            attrs.append(ContinuousVariable(name))

        ts = Timeseries.from_numpy(Domain(data.domain.attributes + tuple(attrs),
                                          data.domain.class_vars,
                                          data.domain.metas),
                                   np.column_stack((data.X, np.column_stack(X))),
                                   data.Y, data.metas)
        ts.time_variable = data.time_variable
        self.Outputs.time_series.send(ts)
    def commit(self):
        data = self.data
        if not data or not len(self.selected):
            self.send(Output.TIMESERIES, None)
            return

        X = []
        attrs = []
        invert = self.invert_direction
        shift = self.shift_period
        order = self.diff_order
        for var in self.selected:
            col = np.ravel(data[:, var])

            if invert:
                col = col[::-1]

            out = np.empty(len(col))
            if shift == 1:
                out[:-order] = np.diff(col, order)
                out[-order:] = np.nan
            else:
                out[:-shift] = col[shift:] - col[:-shift]
                out[-shift:] = np.nan

            if invert:
                out = out[::-1]

            X.append(out)

            template = '{} (diff; {})'.format(var,
                                              'order={}'.format(order) if shift == 1 else
                                              'shift={}'.format(shift))
            name = available_name(data.domain, template)
            attrs.append(ContinuousVariable(name))

        ts = Timeseries(Domain(data.domain.attributes + tuple(attrs),
                               data.domain.class_vars,
                               data.domain.metas),
                        np.column_stack((data.X, np.column_stack(X))),
                        data.Y, data.metas)
        ts.time_variable = data.time_variable
        self.send(Output.TIMESERIES, ts)
Exemplo n.º 3
0
    def commit(self):
        data = self.data
        if not data or not len(self.selected):
            self.send(Output.TIMESERIES, None)
            return

        X = []
        attrs = []
        invert = self.invert_direction
        shift = self.shift_period
        order = self.diff_order
        for var in self.selected:
            col = np.ravel(data[:, var])

            if invert:
                col = col[::-1]

            out = np.empty(len(col))
            if shift == 1:
                out[:-order] = np.diff(col, order)
                out[-order:] = np.nan
            else:
                out[:-shift] = col[shift:] - col[:-shift]
                out[-shift:] = np.nan

            if invert:
                out = out[::-1]

            X.append(out)

            template = '{} (diff; {})'.format(
                var, 'order={}'.format(order)
                if shift == 1 else 'shift={}'.format(shift))
            name = available_name(data.domain, template)
            attrs.append(ContinuousVariable(name))

        ts = Timeseries(
            Domain(data.domain.attributes + tuple(attrs),
                   data.domain.class_vars, data.domain.metas),
            np.column_stack((data.X, np.column_stack(X))), data.Y, data.metas)
        ts.time_variable = data.time_variable
        self.send(Output.TIMESERIES, ts)
Exemplo n.º 4
0
def moving_transform(data, spec, fixed_wlen=0):
    """
    Return data transformed according to spec.

    Parameters
    ----------
    data : Timeseries
        A table with features to transform.
    spec : list of lists
        A list of lists [feature:Variable, window_length:int, function:callable].
    fixed_wlen : int
        If not 0, then window_length in spec is disregarded and this length
        is used. Also the windows don't shift by one but instead align
        themselves side by side.

    Returns
    -------
    transformed : Timeseries
        A table of original data its transformations.
    """
    from itertools import chain
    from Orange.data import ContinuousVariable, Domain
    from orangecontrib.timeseries import Timeseries
    from orangecontrib.timeseries.widgets.utils import available_name
    from orangecontrib.timeseries.agg_funcs import Cumulative_sum, Cumulative_product

    X = []
    attrs = []

    for var, wlen, func in spec:
        col = np.ravel(data[:, var])

        if fixed_wlen:
            wlen = fixed_wlen

        if func in (Cumulative_sum, Cumulative_product):
            out = list(chain.from_iterable(func(col[i:i + wlen])
                                           for i in range(0, len(col), wlen)))
        else:
            # In reverse cause lazy brain. Also prefer informative ends, not beginnings as much
            col = col[::-1]
            out = [func(col[i:i + wlen])
                   for i in range(0, len(col), wlen if bool(fixed_wlen) else 1)]
            out = out[::-1]

        X.append(out)

        template = '{} ({}; {})'.format(var.name, wlen, func.__name__.lower().replace('_', ' '))
        name = available_name(data.domain, template)
        attrs.append(ContinuousVariable(name))

    dataX, dataY, dataM = data.X, data.Y, data.metas
    if fixed_wlen:
        n = len(X[0])
        dataX = dataX[::-1][::fixed_wlen][:n][::-1]
        dataY = dataY[::-1][::fixed_wlen][:n][::-1]
        dataM = dataM[::-1][::fixed_wlen][:n][::-1]

    ts = Timeseries(Domain(data.domain.attributes + tuple(attrs),
                           data.domain.class_vars,
                           data.domain.metas),
                    np.column_stack(
                        (dataX, np.column_stack(X))) if X else dataX,
                    dataY, dataM)
    ts.time_variable = data.time_variable
    return ts
Exemplo n.º 5
0
def seasonal_decompose(data, model='multiplicative', period=12, *, callback=None):
    """
    Return table of decomposition components of original features and
    original features seasonally adjusted.

    Parameters
    ----------
    data : Timeseries
        A table of featres to decompose/adjust.
    model : str {'additive', 'multiplicative'}
        A decompostition model. See:
        https://en.wikipedia.org/wiki/Decomposition_of_time_series
    period : int
        The period length of season.
    callback : callable
        Optional callback to call (with no parameters) after each iteration.

    Returns
    -------
    table : Timeseries
        Table with columns: original series seasonally adjusted, original
        series' seasonal components, trend components, and residual components.
    """
    from operator import sub, truediv
    from Orange.data import Domain, ContinuousVariable
    from orangecontrib.timeseries import Timeseries
    from orangecontrib.timeseries.widgets.utils import available_name
    import statsmodels.api as sm

    def _interp_trend(trend):
        first = next(i for i, val in enumerate(trend) if val == val)
        last = trend.size - 1 - next(
            i for i, val in enumerate(trend[::-1]) if val == val)
        d = 3
        first_last = min(first + d, last)
        last_first = max(first, last - d)

        k, n = np.linalg.lstsq(
            np.column_stack((np.arange(first, first_last), np.ones(first_last - first))),
            trend[first:first_last])[0]
        trend[:first] = np.arange(0, first) * k + n

        k, n = np.linalg.lstsq(
            np.column_stack((np.arange(last_first, last), np.ones(last - last_first))),
            trend[last_first:last])[0]
        trend[last + 1:] = np.arange(last + 1, trend.size) * k + n
        return trend

    attrs = []
    X = []
    recomposition = sub if model == 'additive' else truediv
    interp_data = data.interp()
    for var in data.domain.variables:
        decomposed = sm.tsa.seasonal_decompose(np.ravel(interp_data[:, var]),
                                               model=model,
                                               freq=period)
        adjusted = recomposition(decomposed.observed,
                                 decomposed.seasonal)

        season = decomposed.seasonal
        trend = _interp_trend(decomposed.trend)
        resid = recomposition(adjusted, trend)

        # Re-apply nans
        isnan = np.isnan(data[:, var]).ravel()
        adjusted[isnan] = np.nan
        trend[isnan] = np.nan
        resid[isnan] = np.nan

        attrs.extend(
            ContinuousVariable(
                available_name(data.domain,
                               var.name + ' ({})'.format(transform)))
            for transform in
            ('season. adj.', 'seasonal', 'trend', 'residual')
        )
        X.extend((adjusted, season, trend, resid))

        if callback:
            callback()

    ts = Timeseries(Domain(attrs), np.column_stack(X))
    return ts
Exemplo n.º 6
0
def moving_transform(data, spec, fixed_wlen=0):
    """
    Return data transformed according to spec.

    Parameters
    ----------
    data : Timeseries
        A table with features to transform.
    spec : list of lists
        A list of lists [feature:Variable, window_length:int, function:callable].
    fixed_wlen : int
        If not 0, then window_length in spec is disregarded and this length
        is used. Also the windows don't shift by one but instead align
        themselves side by side.

    Returns
    -------
    transformed : Timeseries
        A table of original data its transformations.
    """
    from itertools import chain
    from Orange.data import ContinuousVariable, Domain
    from orangecontrib.timeseries import Timeseries
    from orangecontrib.timeseries.widgets.utils import available_name
    from orangecontrib.timeseries.agg_funcs import Cumulative_sum, Cumulative_product

    X = []
    attrs = []

    for var, wlen, func in spec:
        col = np.ravel(data[:, var])

        if fixed_wlen:
            wlen = fixed_wlen

        if func in (Cumulative_sum, Cumulative_product):
            out = list(
                chain.from_iterable(
                    func(col[i:i + wlen]) for i in range(0, len(col), wlen)))
        else:
            # In reverse cause lazy brain. Also prefer informative ends, not beginnings as much
            col = col[::-1]
            out = [
                func(col[i:i + wlen])
                for i in range(0, len(col), wlen if bool(fixed_wlen) else 1)
            ]
            out = out[::-1]

        X.append(out)

        template = '{} ({}; {})'.format(
            var.name, wlen,
            func.__name__.lower().replace('_', ' '))
        name = available_name(data.domain, template)
        attrs.append(ContinuousVariable(name))

    dataX, dataY, dataM = data.X, data.Y, data.metas
    if fixed_wlen:
        n = len(X[0])
        dataX = dataX[::-1][::fixed_wlen][:n][::-1]
        dataY = dataY[::-1][::fixed_wlen][:n][::-1]
        dataM = dataM[::-1][::fixed_wlen][:n][::-1]

    ts = Timeseries.from_numpy(
        Domain(data.domain.attributes + tuple(attrs), data.domain.class_vars,
               data.domain.metas),
        np.column_stack((dataX, np.column_stack(X))) if X else dataX, dataY,
        dataM)
    ts.time_variable = data.time_variable
    return ts
Exemplo n.º 7
0
def seasonal_decompose(data,
                       model='multiplicative',
                       period=12,
                       *,
                       callback=None):
    """
    Return table of decomposition components of original features and
    original features seasonally adjusted.

    Parameters
    ----------
    data : Timeseries
        A table of featres to decompose/adjust.
    model : str {'additive', 'multiplicative'}
        A decompostition model. See:
        https://en.wikipedia.org/wiki/Decomposition_of_time_series
    period : int
        The period length of season.
    callback : callable
        Optional callback to call (with no parameters) after each iteration.

    Returns
    -------
    table : Timeseries
        Table with columns: original series seasonally adjusted, original
        series' seasonal components, trend components, and residual components.
    """
    from operator import sub, truediv
    from Orange.data import Domain, ContinuousVariable
    from orangecontrib.timeseries import Timeseries
    from orangecontrib.timeseries.widgets.utils import available_name
    import statsmodels.api as sm

    def _interp_trend(trend):
        first = next(i for i, val in enumerate(trend) if val == val)
        last = trend.size - 1 - next(
            i for i, val in enumerate(trend[::-1]) if val == val)
        d = 3
        first_last = min(first + d, last)
        last_first = max(first, last - d)

        k, n = np.linalg.lstsq(
            np.column_stack(
                (np.arange(first, first_last), np.ones(first_last - first))),
            trend[first:first_last])[0]
        trend[:first] = np.arange(0, first) * k + n

        k, n = np.linalg.lstsq(
            np.column_stack((np.arange(last_first,
                                       last), np.ones(last - last_first))),
            trend[last_first:last])[0]
        trend[last + 1:] = np.arange(last + 1, trend.size) * k + n
        return trend

    attrs = []
    X = []
    recomposition = sub if model == 'additive' else truediv
    interp_data = data.interp()
    for var in data.domain.variables:
        decomposed = sm.tsa.seasonal_decompose(np.ravel(interp_data[:, var]),
                                               model=model,
                                               freq=period)
        adjusted = recomposition(decomposed.observed, decomposed.seasonal)

        season = decomposed.seasonal
        trend = _interp_trend(decomposed.trend)
        resid = recomposition(adjusted, trend)

        # Re-apply nans
        isnan = np.isnan(data[:, var]).ravel()
        adjusted[isnan] = np.nan
        trend[isnan] = np.nan
        resid[isnan] = np.nan

        attrs.extend(
            ContinuousVariable(
                available_name(data.domain, var.name +
                               ' ({})'.format(transform)))
            for transform in ('season. adj.', 'seasonal', 'trend', 'residual'))
        X.extend((adjusted, season, trend, resid))

        if callback:
            callback()

    ts = Timeseries.from_numpy(Domain(attrs), np.column_stack(X))
    return ts