From 8086ab31ce131a1c8fb0d80d45a39e8a6b7db4c2 Mon Sep 17 00:00:00 2001 From: Jan Gasthaus Date: Thu, 5 Sep 2019 20:25:39 +0200 Subject: [PATCH 1/5] More operators for synthetic data generation --- src/gluonts/dataset/artificial/recipe.py | 186 +++++++++++++++++++++-- 1 file changed, 176 insertions(+), 10 deletions(-) diff --git a/src/gluonts/dataset/artificial/recipe.py b/src/gluonts/dataset/artificial/recipe.py index 528ce5a28e..5e3d7b7a84 100644 --- a/src/gluonts/dataset/artificial/recipe.py +++ b/src/gluonts/dataset/artificial/recipe.py @@ -32,6 +32,7 @@ def generate( start: pd.Timestamp, global_state: Optional[dict] = None, seed: int = 0, + item_id_prefix: str = "", ) -> Iterator[DataEntry]: np.random.seed(seed) @@ -48,24 +49,28 @@ def generate( field_name=k, global_state=global_state, ) - yield dict(**data, item=x, start=start) + yield dict(**data, item_id=item_id_prefix + str(x), start=start) else: assert callable(recipe) for x in itertools.count(): data = recipe(length=length, global_state=global_state) - yield dict(**data, item=x, start=start) + yield dict(**data, item_id=item_id_prefix + str(x), start=start) -def evaluate_recipe( - funcs: List[Tuple[str, Callable]], length: int, global_state: dict = None +def evaluate( + funcs: List[Tuple[str, Callable]], length: int, *args, global_state: dict = None, **kwargs ) -> dict: if global_state is None: global_state = {} data: DataEntry = {} for k, f in funcs: - data[k] = f( - data, length=length, field_name=k, global_state=global_state - ) + try: + data[k] = f( + data, length=length, field_name=k, global_state=global_state + ) + except ValueError as e: + raise ValueError("Error while evaluating key \"{}\"".format(k), e) + return data @@ -90,6 +95,13 @@ def take_as_list(iterator, num): return list(itertools.islice(iterator, num)) +def resolve(val_or_callable, context, *args, **kwargs): + if callable(val_or_callable): + return val_or_callable(context, *args, **kwargs) + else: + return val_or_callable + + class Debug: @validated() def __init__(self, print_global=False): @@ -109,6 +121,12 @@ def __add__(self, other): def __radd__(self, other): return LiftedAdd(other, self) + def __sub__(self, other): + return LiftedSub(self, other) + + def __rsub__(self, other): + return LiftedSub(other, self) + def __mul__(self, other): return LiftedMul(self, other, operator.mul) @@ -146,6 +164,12 @@ def __init__(self, left, right) -> None: super().__init__(left, right, operator.add) +class LiftedSub(LiftedBinaryOp): + @validated() + def __init__(self, left, right) -> None: + super().__init__(left, right, operator.sub) + + class LiftedMul(LiftedBinaryOp): @validated() def __init__(self, left, right) -> None: @@ -252,6 +276,26 @@ def __call__(self, x, length, *args, **kwargs): return self.constant * np.ones(length) +class NormalizeMax(Lifted): + @validated() + def __init__(self, input) -> None: + self.input = input + + def __call__(self, x, *args, **kwargs): + inp = resolve(self.input, x, *args, kwargs) + return inp / np.max(inp) + + +class OnesLike(Lifted): + @validated() + def __init__(self, other) -> None: + self.other = other + + def __call__(self, x, length, *args, **kwargs): + other = resolve(self.other, x, length, **kwargs) + return np.ones_like(other) + + class LinearTrend(Lifted): @validated() def __init__(self, slope_fun: Callable = Constant(1.0)) -> None: @@ -390,8 +434,130 @@ def __call__(self, x, **kwargs): class Stack(Lifted): @validated() - def __init__(self, inputs: List[str]) -> None: + def __init__(self, inputs: List[Callable]) -> None: self.inputs = inputs - def __call__(self, x, **kwargs): - return np.stack([x[k] for k in self.inputs], axis=0) + def __call__(self, x, length, **kwargs): + inputs = [resolve(z, x, length, **kwargs) for z in self.inputs] + return np.stack(inputs, axis=0) + + +class StackPrefix(Lifted): + @validated() + def __init__(self, prefix: str) -> None: + self.prefix = prefix + + def __call__(self, x, length, **kwargs): + inputs = [v for k, v in x.items() if k.startswith(self.prefix)] + return np.stack(inputs, axis=0) + + +class Ref(Lifted): + @validated() + def __init__(self, field_name: str) -> None: + self.field_name = field_name + + def __call__(self, x, *args, **kwargs): + return x[self.field_name] + + +class RandomUniform(Lifted): + @validated() + def __init__(self, low: float = 0, high: float = 1, shape=(0, )) -> None: + self.low = low + self.high = high + self.shape = shape + + def __call__(self, x, length, **kwargs): + s = np.array(self.shape) + s[s == 0] = length + return np.random.uniform(self.low, self.high, s) + + +class RandomInteger(Lifted): + @validated() + def __init__(self, low: int, high: int, length=None) -> None: + self.low = low + self.high = high + self.length = length + + def __call__(self, x, length, **kwargs): + length = self.length if self.length is not None else length + return np.random.randint(self.low, self.high, length) + + +class RandomChangepoints(Lifted): + @validated() + def __init__(self, max_num_changepoints: int) -> None: + self.max_num_changepoints = max_num_changepoints + + def __call__(self, x, length, **kwargs): + num_changepoints = np.random.randint(0, self.max_num_changepoints + 1) + change_idx = np.sort(np.random.randint(low=1, high=length - 1, size=(num_changepoints, ))) + change_ranges = np.concatenate([change_idx, [length]]) + out = np.zeros(length, dtype=np.int) + for i in range(0, num_changepoints): + out[change_ranges[i]:change_ranges[i+1]] = i + 1 + return out + + +class Repeated(Lifted): + @validated() + def __init__(self, pattern) -> None: + self.pattern = pattern + + def __call__(self, x, length, **kwargs): + pattern = resolve(self.pattern, x, length, **kwargs) + repeats = length // len(pattern) + 1 + out = np.tile(pattern, (repeats, )) + return out[:length] + + +class Convolve(Lifted): + @validated() + def __init__(self, input: Lifted, filter) -> None: + self.filter = filter + self.input = input + + def __call__(self, x, length, **kwargs): + fil = resolve(self.filter, x, length, **kwargs) + inp = resolve(self.input, x, length, **kwargs) + out = np.convolve(inp, fil, mode="same") + return out + + +class Dilated(Lifted): + @validated() + def __init__(self, source: Lifted, dilation: int) -> None: + self.source = source + self.dilation = dilation + + def __call__(self, x, length, **kwargs): + inner = self.source(x, length // self.dilation + 1, **kwargs) + out = np.repeat(inner, self.dilation) + return out[:length] + + +class Choose(Lifted): + @validated() + def __init__(self, options: Lifted, selector: Lifted): + self.options = options + self.selector = selector + + def __call__(self, x, length, **kwargs): + options = resolve(self.options, x, length, **kwargs) + selector = resolve(self.selector, x, length, **kwargs) + e = np.eye(options.shape[0]) + out = np.sum(e[selector] * options.T, axis=1) + return out + + +class Eval(Lifted): + @validated() + def __init__(self, env, op: Lifted): + self.env = env + self.op = op + + def __call__(self, x, *args, **kwargs): + xx = evaluate(self.env, *args, **kwargs) + return self.op(xx, *args, **kwargs) From a724ceff5360e6a0e59f65d6683687a6b6439c47 Mon Sep 17 00:00:00 2001 From: Jan Gasthaus Date: Fri, 13 Sep 2019 16:49:09 +0200 Subject: [PATCH 2/5] Fix typing, black, tests, and item_id issues --- src/gluonts/dataset/artificial/_base.py | 16 ++-- src/gluonts/dataset/artificial/recipe.py | 109 ++++++++++++++--------- test/dataset/artificial/test_recipe.py | 10 ++- 3 files changed, 82 insertions(+), 53 deletions(-) diff --git a/src/gluonts/dataset/artificial/_base.py b/src/gluonts/dataset/artificial/_base.py index 6e480ba216..7545fb57cb 100644 --- a/src/gluonts/dataset/artificial/_base.py +++ b/src/gluonts/dataset/artificial/_base.py @@ -330,7 +330,7 @@ def generate_ts( ts_data = dict( start=self.start, target=target, - item=str(i), + item_id=str(i), feat_static_cat=[i], feat_static_real=[i], ) @@ -508,7 +508,7 @@ def train(self) -> List[DataEntry]: dict( start=ts["start"], target=ts["target"][: -self.prediction_length], - item=ts["item"], + item_id=ts["item_id"], ) for ts in self.make_timeseries() ] @@ -609,7 +609,7 @@ def sigmoid(x: np.ndarray) -> np.ndarray: dict( start=pd.Timestamp(start, freq=self.freq_str), target=np.array(v), - item=i, + item_id=i, ) ) return res @@ -681,7 +681,9 @@ def trim_ts_item_end(x: DataEntry, length: int) -> DataEntry: the last prediction_length time points from the target and dynamic features.""" y = dict( - item=x["item"], start=x["start"], target=x["target"][:-length] + item_id=x["item_id"], + start=x["start"], + target=x["target"][:-length], ) if "feat_dynamic_cat" in x: @@ -703,7 +705,7 @@ def trim_ts_item_front(x: DataEntry, length: int) -> DataEntry: assert length <= len(x["target"]) y = dict( - item=x["item"], + item_id=x["item_id"], start=x["start"] + length * x["start"].freq, target=x["target"][length:], ) @@ -799,7 +801,7 @@ def constant_dataset() -> Tuple[DatasetInfo, Dataset, Dataset]: train_ds = ListDataset( data_iter=[ { - "item": str(i), + "item_id": str(i), "start": start_date, "target": [float(i)] * 24, "feat_static_cat": [i], @@ -813,7 +815,7 @@ def constant_dataset() -> Tuple[DatasetInfo, Dataset, Dataset]: test_ds = ListDataset( data_iter=[ { - "item": str(i), + "item_id": str(i), "start": start_date, "target": [float(i)] * 30, "feat_static_cat": [i], diff --git a/src/gluonts/dataset/artificial/recipe.py b/src/gluonts/dataset/artificial/recipe.py index 5e3d7b7a84..fd6795f4bb 100644 --- a/src/gluonts/dataset/artificial/recipe.py +++ b/src/gluonts/dataset/artificial/recipe.py @@ -15,7 +15,7 @@ import functools import itertools import operator -from typing import Any, Callable, Iterator, List, Optional, Tuple, Union +from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union # Third-party imports import numpy as np @@ -25,10 +25,14 @@ from gluonts.core.component import validated from gluonts.dataset.common import DataEntry +ValueOrCallable = Union[Any, Callable] +Recipe = List[Tuple[str, Callable]] +Env = Dict[str, Any] + def generate( length: int, - recipe: Union[Callable, List[Tuple[str, Callable]]], + recipe: Union[Callable, Recipe], start: pd.Timestamp, global_state: Optional[dict] = None, seed: int = 0, @@ -58,8 +62,8 @@ def generate( def evaluate( - funcs: List[Tuple[str, Callable]], length: int, *args, global_state: dict = None, **kwargs -) -> dict: + funcs: Recipe, length: int, *args, global_state: dict = None, **kwargs +) -> Env: if global_state is None: global_state = {} data: DataEntry = {} @@ -69,14 +73,14 @@ def evaluate( data, length=length, field_name=k, global_state=global_state ) except ValueError as e: - raise ValueError("Error while evaluating key \"{}\"".format(k), e) + raise ValueError('Error while evaluating key "{}"'.format(k), e) return data def make_func( - length: int, funcs: List[Tuple[str, Callable]], global_state=None -) -> Callable[[int, dict], DataEntry]: + length: int, funcs: Recipe, global_state=None +) -> Callable[[int, Env], DataEntry]: if global_state is None: global_state = {} @@ -95,7 +99,7 @@ def take_as_list(iterator, num): return list(itertools.islice(iterator, num)) -def resolve(val_or_callable, context, *args, **kwargs): +def resolve(val_or_callable: ValueOrCallable, context: Env, *args, **kwargs): if callable(val_or_callable): return val_or_callable(context, *args, **kwargs) else: @@ -104,10 +108,10 @@ def resolve(val_or_callable, context, *args, **kwargs): class Debug: @validated() - def __init__(self, print_global=False): + def __init__(self, print_global=False) -> None: self.print_global = print_global - def __call__(self, x, global_state, **kwargs): + def __call__(self, x: Env, global_state, **kwargs): print(x) if self.print_global: print(global_state) @@ -139,6 +143,17 @@ def __truediv__(self, other): def __rtruediv__(self, other): return LiftedTruediv(other, self, operator.truediv) + def __call__( + self, + x: Env, + length: int, + field_name: str, + global_state: Dict, + *args, + **kwargs + ): + pass + class LiftedBinaryOp(Lifted): def __init__(self, left, right, op) -> None: @@ -190,7 +205,7 @@ def __init__( self.stddev = stddev self.length = length - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): if self.length is not None: length = self.length return self.stddev * np.random.randn(length) @@ -221,7 +236,7 @@ class RandomBinary(Lifted): def __init__(self, prob: float = 0.1) -> None: self.prob = prob - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return 1.0 * (np.random.rand(length) < self.prob) @@ -247,7 +262,7 @@ def __init__(self, one_to_zero: float, zero_to_one: float) -> None: self.probs[0] = zero_to_one self.probs[1] = one_to_zero - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): out = np.ones(length, dtype=np.int) # initial state is 1 uu = np.random.rand(length) for i in range(1, length): @@ -272,7 +287,7 @@ class ConstantVec(Lifted): def __init__(self, constant) -> None: self.constant = constant - def __call__(self, x, length, *args, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return self.constant * np.ones(length) @@ -281,7 +296,7 @@ class NormalizeMax(Lifted): def __init__(self, input) -> None: self.input = input - def __call__(self, x, *args, **kwargs): + def __call__(self, x: Env, *args, **kwargs): inp = resolve(self.input, x, *args, kwargs) return inp / np.max(inp) @@ -351,7 +366,15 @@ def __init__(self, fun, cat_field="cat", cat_idx=0) -> None: self.cat_field = cat_field self.cat_idx = cat_idx - def __call__(self, x, field_name, global_state, **kwargs): + def __call__( + self, + x: Env, + length: int, + field_name: str, + global_state: Dict, + *args, + **kwargs + ): c = x[self.cat_field][self.cat_idx] if field_name not in global_state: global_state[field_name] = np.empty( @@ -368,7 +391,7 @@ class Expr(Lifted): def __init__(self, expr: str) -> None: self.expr = expr - def __call__(self, x, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return eval(self.expr, globals(), dict(x=x, **kwargs)) @@ -378,7 +401,7 @@ def __init__(self, period_fun: Callable, phase_fun: Callable) -> None: self.period_fun = period_fun self.phase_fun = phase_fun - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return ( np.sin( 2.0 @@ -395,7 +418,7 @@ class Add(Lifted): def __init__(self, inputs) -> None: self.inputs = inputs - def __call__(self, x, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return sum([x[k] for k in self.inputs]) @@ -404,7 +427,7 @@ class Mul(Lifted): def __init__(self, inputs) -> None: self.inputs = inputs - def __call__(self, x, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return functools.reduce(operator.mul, [x[k] for k in self.inputs]) @@ -414,7 +437,7 @@ def __init__(self, source_name, nan_indicator_name) -> None: self.source_name = source_name self.nan_indicator_name = nan_indicator_name - def __call__(self, x, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): out = x[self.source_name] out[x[self.nan_indicator_name] == 1] = np.nan return out @@ -426,7 +449,7 @@ def __init__(self, source_name, nan_indicator_name) -> None: self.source_name = source_name self.nan_indicator_name = nan_indicator_name - def __call__(self, x, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): out = x[self.source_name] out[x[self.nan_indicator_name] == 0] = np.nan return out @@ -434,10 +457,10 @@ def __call__(self, x, **kwargs): class Stack(Lifted): @validated() - def __init__(self, inputs: List[Callable]) -> None: + def __init__(self, inputs: List[ValueOrCallable]) -> None: self.inputs = inputs - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): inputs = [resolve(z, x, length, **kwargs) for z in self.inputs] return np.stack(inputs, axis=0) @@ -447,7 +470,7 @@ class StackPrefix(Lifted): def __init__(self, prefix: str) -> None: self.prefix = prefix - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): inputs = [v for k, v in x.items() if k.startswith(self.prefix)] return np.stack(inputs, axis=0) @@ -457,18 +480,18 @@ class Ref(Lifted): def __init__(self, field_name: str) -> None: self.field_name = field_name - def __call__(self, x, *args, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): return x[self.field_name] class RandomUniform(Lifted): @validated() - def __init__(self, low: float = 0, high: float = 1, shape=(0, )) -> None: + def __init__(self, low: float = 0, high: float = 1, shape=(0,)) -> None: self.low = low self.high = high self.shape = shape - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): s = np.array(self.shape) s[s == 0] = length return np.random.uniform(self.low, self.high, s) @@ -481,7 +504,7 @@ def __init__(self, low: int, high: int, length=None) -> None: self.high = high self.length = length - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): length = self.length if self.length is not None else length return np.random.randint(self.low, self.high, length) @@ -491,25 +514,27 @@ class RandomChangepoints(Lifted): def __init__(self, max_num_changepoints: int) -> None: self.max_num_changepoints = max_num_changepoints - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): num_changepoints = np.random.randint(0, self.max_num_changepoints + 1) - change_idx = np.sort(np.random.randint(low=1, high=length - 1, size=(num_changepoints, ))) + change_idx = np.sort( + np.random.randint(low=1, high=length - 1, size=(num_changepoints,)) + ) change_ranges = np.concatenate([change_idx, [length]]) out = np.zeros(length, dtype=np.int) for i in range(0, num_changepoints): - out[change_ranges[i]:change_ranges[i+1]] = i + 1 + out[change_ranges[i] : change_ranges[i + 1]] = i + 1 return out class Repeated(Lifted): @validated() - def __init__(self, pattern) -> None: + def __init__(self, pattern: ValueOrCallable) -> None: self.pattern = pattern - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): pattern = resolve(self.pattern, x, length, **kwargs) repeats = length // len(pattern) + 1 - out = np.tile(pattern, (repeats, )) + out = np.tile(pattern, (repeats,)) return out[:length] @@ -519,7 +544,7 @@ def __init__(self, input: Lifted, filter) -> None: self.filter = filter self.input = input - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): fil = resolve(self.filter, x, length, **kwargs) inp = resolve(self.input, x, length, **kwargs) out = np.convolve(inp, fil, mode="same") @@ -532,7 +557,7 @@ def __init__(self, source: Lifted, dilation: int) -> None: self.source = source self.dilation = dilation - def __call__(self, x, length, **kwargs): + def __call__(self, x: Env, length: int, *args, **kwargs): inner = self.source(x, length // self.dilation + 1, **kwargs) out = np.repeat(inner, self.dilation) return out[:length] @@ -540,7 +565,7 @@ def __call__(self, x, length, **kwargs): class Choose(Lifted): @validated() - def __init__(self, options: Lifted, selector: Lifted): + def __init__(self, options: Lifted, selector: Lifted) -> None: self.options = options self.selector = selector @@ -554,10 +579,10 @@ def __call__(self, x, length, **kwargs): class Eval(Lifted): @validated() - def __init__(self, env, op: Lifted): - self.env = env + def __init__(self, recipe: Recipe, op: Lifted) -> None: + self.recipe = recipe self.op = op - def __call__(self, x, *args, **kwargs): - xx = evaluate(self.env, *args, **kwargs) + def __call__(self, x: Env, *args, **kwargs): + xx = evaluate(self.recipe, *args, **kwargs) return self.op(xx, *args, **kwargs) diff --git a/test/dataset/artificial/test_recipe.py b/test/dataset/artificial/test_recipe.py index a7f1d26997..32059fce81 100644 --- a/test/dataset/artificial/test_recipe.py +++ b/test/dataset/artificial/test_recipe.py @@ -41,9 +41,10 @@ RandomCat, RandomGaussian, RandomSymmetricDirichlet, + Ref, SmoothSeasonality, Stack, - evaluate_recipe, + evaluate, generate, take_as_list, ) @@ -70,7 +71,7 @@ Mul(["foo", "foo"]), NanWhere("foo", "foo"), NanWhereNot("foo", "foo"), - Stack(["foo", "foo"]), + Stack([Ref("foo"), Ref("foo")]), RandomGaussian() + RandomGaussian(), RandomGaussian() * RandomGaussian(), RandomGaussian() / RandomGaussian(), @@ -78,7 +79,7 @@ ) def test_call_and_repr(func) -> None: global_state = {} - x = evaluate_recipe(BASE_RECIPE, length=10, global_state=global_state) + x = evaluate(BASE_RECIPE, length=10, global_state=global_state) kwargs = dict(foo=42, bar=23) np.random.seed(0) ret = func( @@ -99,6 +100,7 @@ def test_call_and_repr(func) -> None: global_state=global_state.copy(), **kwargs, ) + print(ret) np.testing.assert_allclose(ret2, ret) @@ -108,7 +110,7 @@ def test_call_and_repr(func) -> None: [ ("target", LinearTrend() + RandomGaussian()), ("binary_causal", BinaryMarkovChain(0.01, 0.1)), - ("feat_dynamic_real", Stack(["binary_causal"])), + ("feat_dynamic_real", Stack([Ref("binary_causal")])), ("feat_static_cat", RandomCat([10])), ( "feat_static_real", From fae1e0b6821f6d0e3e6f5daf4d181670409571bc Mon Sep 17 00:00:00 2001 From: Jan Gasthaus Date: Mon, 16 Sep 2019 10:28:34 +0200 Subject: [PATCH 3/5] Fix recipe generation --- src/gluonts/dataset/artificial/recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gluonts/dataset/artificial/recipe.py b/src/gluonts/dataset/artificial/recipe.py index fd6795f4bb..75e4adf23e 100644 --- a/src/gluonts/dataset/artificial/recipe.py +++ b/src/gluonts/dataset/artificial/recipe.py @@ -382,7 +382,7 @@ def __call__( dtype=np.object, ) if global_state[field_name][c] is None: - global_state[field_name][c] = self.fun(x, **kwargs) + global_state[field_name][c] = self.fun(x, length, field_name, *args, **kwargs) return global_state[field_name][c] From 9df14ddc537471fdd96442e18a175b8a0a1ad049 Mon Sep 17 00:00:00 2001 From: Jan Gasthaus Date: Mon, 16 Sep 2019 14:28:14 +0200 Subject: [PATCH 4/5] Make black happy --- src/gluonts/dataset/artificial/recipe.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gluonts/dataset/artificial/recipe.py b/src/gluonts/dataset/artificial/recipe.py index 75e4adf23e..cdd079db69 100644 --- a/src/gluonts/dataset/artificial/recipe.py +++ b/src/gluonts/dataset/artificial/recipe.py @@ -382,7 +382,9 @@ def __call__( dtype=np.object, ) if global_state[field_name][c] is None: - global_state[field_name][c] = self.fun(x, length, field_name, *args, **kwargs) + global_state[field_name][c] = self.fun( + x, length, field_name, *args, **kwargs + ) return global_state[field_name][c] From 4421d2de0224fe3d76a50ed2b08274f2f7ed0052 Mon Sep 17 00:00:00 2001 From: Jan Gasthaus Date: Mon, 16 Sep 2019 17:05:54 +0200 Subject: [PATCH 5/5] fix missing length argument --- src/gluonts/dataset/artificial/recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gluonts/dataset/artificial/recipe.py b/src/gluonts/dataset/artificial/recipe.py index cdd079db69..541a4db64d 100644 --- a/src/gluonts/dataset/artificial/recipe.py +++ b/src/gluonts/dataset/artificial/recipe.py @@ -394,7 +394,7 @@ def __init__(self, expr: str) -> None: self.expr = expr def __call__(self, x: Env, length: int, *args, **kwargs): - return eval(self.expr, globals(), dict(x=x, **kwargs)) + return eval(self.expr, globals(), dict(x=x, length=length, **kwargs)) class SmoothSeasonality(Lifted):