def regress_level_on_first_known(y: Y_TYPE, s: dict, k, a: A_TYPE = None, t: T_TYPE = None, e: E_TYPE = None) -> ([float], Any, Any): """ Very basic online regression skater, mostly for testing - Only one known in advance variable is utilized - Last value is ignored, unless a is None in which case we return 0.0 - Empirical std is returned """ y0 = wrap(y)[0] # Ignore contemporaneous, exogenous variables if a: a0 = wrap(a)[0] # Ignore all but the first known-in-advance variable if not s.get('k'): # First invocation s = {'p': {}} # Prediction parade s['r'] = {} # Regression state, not to be confused with hyper-param r s['k'] = k s['o'] = { } # The "observance" will quarantine 'a' until it can be matched else: assert s['k'] == k # Immutability if a is None: return [0] * k, [1.0] * k, s else: a_t, s['o'] = observance(y=[y0], o=s['o'], k=k, a=[a0]) # Update the observance if a_t is not None: # This is the contemporaneous 'a', which was supplied k calls ago. if not s['r']: # When first calling the online regression algorithm we avoid the degenerate case # by sending it two observations. y_noise = 0.1 * (1e-6 + abs(y0)) * np.random.randn() x_noise = 0.1 * (1e-6 + abs(a0)) * np.random.randn() x = [a_t[0] - x_noise, a_t[0] + x_noise] y = [y0 - y_noise, y0 + y_noise] s['r'] = regress_one_helper(x=x, y=y, r=s['r']) else: s['r'] = regress_one_helper(x=a_t, y=[y0], r=s['r']) # Predict using contemporaneous alpha's x = [ s['r']['alpha'] + s['r']['beta'] * ak[0] for ak in s['o']['a'] ] # Push prediction into the parade and get the current bias/stderr bias, x_std, s['p'] = parade(p=s['p'], x=x, y=y0) return x, x_std, s # TODO: Use the std implied by regression instead else: x = [y0] * k bias, x_std, s['p'] = parade(p=s['p'], x=x, y=y0) return x, x_std, s
def moving_average_r1(y: Y_TYPE, s, k: int = 1, a: A_TYPE = None, t: T_TYPE = None, e: E_TYPE = None, r: R_TYPE = None): """ Exponential moving average, with empirical std r weight to place on existing anchor point """ assert r is not None y0 = wrap(y)[0] if not s.get('p'): s = {'p': {}, 'x': y0, 'rho': r} assert 0 <= s['rho'] <= 1, 'Expecting rho=r to be between 0 and 1' else: assert abs(r - s['rho']) < 1e-6, 'rho=r is immutable' if y0 is None: return None, s, None else: s['x'] = s['rho'] * s['x'] + (1 - s['rho']) * y0 # Make me better ! x = [s['x'] * k] bias, x_std, s['p'] = parade(p=s['p'], x=x, y=y0) # Update prediction queue return [s['x']] * k, x_std, s
def empirical_last_value(y :Y_TYPE, s:dict, k:int =1, a:A_TYPE =None, t:T_TYPE =None, e:E_TYPE =None)->([float] , Any , Any): """ Last value cache, with empirical std """ if not s.get('p'): s = {'p':{}} # Initialize prediction parade if y is None: return None, None, s else: y0 = wrap(y)[0] # Ignore the rest x = [y0]*k # What a great prediction ! bias, x_std, s['p'] = parade(p=s['p'], x=x, y=y0) # update residual queue return x, x_std, s
def fbprophet_skater_factory(y: Y_TYPE, s: dict, k: int, a: A_TYPE = None, t: T_TYPE = None, e: E_TYPE = None, emp_mass: float = 0.0, emp_std_mass: float = 0.0, freq=None, recursive: bool = False, model_params: dict = None, n_max: int = None) -> ([float], Any, Any): """ Prophet skater with running prediction error moments Hyper-parameters are explicit here, whereas they are determined from r in actual skaters. Params of note: a: value of known-in-advance vars k step in advance (not contemporaneous with y) """ assert 0 <= emp_mass <= 1 assert 0 <= emp_std_mass <= 1 if freq is None: freq = PROPHET_META['freq'] if n_max is None: n_max = PROPHET_META['n_max'] y = wrap(y) a = wrap(a) if not s.get('y'): s = {'p': {}, # parade 'y': list(), # historical y 'a': list(), # list of a known k steps in advance 't': list(), 'k': k} else: # Assert immutability of k, dimensions of y,a if s['y']: assert len(y) == len(s['y'][0]) assert k == s['k'] if s['a']: assert len(a) == len(s['a'][0]) if y is None: return None, s, None else: s['y'].append(y) if a is not None: s['a'].append(a) if t is not None: assert isinstance(t,float), 'epoch time please' s['t'].append(t) if len(s['y']) > max(2 * k + 5, PROPHET_META['n_warm']): # Offset y, t, a are supplied to prophet interface t_arg = s['t'][k:] if t is not None else None a_arg = s['a'] y_arg = s['y'][k:] x, x_std, forecast, model = prophet_iskater_factory(y=y_arg, k=k, a=a_arg, t=t_arg, freq=freq, n_max=n_max, recursive=recursive, model_params=model_params) s['m'] = True # Flag indicating a model has been fit (there is no point keeping the model itself, however) else: x = [y[0]] * k x_std = None # Get running mean prediction errors from the prediction parade x_resid, x_resid_std, s['p'] = parade(p=s['p'], x=x, y=y[0]) x_resid = nonecast(x_resid,y[0]) x_resid_std = nonecast(x_resid_std,1.0) # Compute center of mass between bias-corrected and uncorrected predictions x_corrected = np.array(x_resid) + np.array(x) x_center = nonecenter(m=[emp_mass, 1 - emp_mass], x=[x_corrected, x]) x_std_center = nonecenter(m=[emp_std_mass, 1 - emp_std_mass], x=[x_resid_std, x_std]) return x_center, x_std_center, s
def residual_chaser_factory(y: Y_TYPE, s: dict, k: int = 1, a: A_TYPE = None, t: T_TYPE = None, e: E_TYPE = None, f1=None, f2=None, chase=1.0, threshold=1.0, r1=None, r2=None) -> ([float], Any, Any): """ Last value cache, with empirical std and self-correction f1 - A skater making the primary prediction f2 - A skater designed to predict residuals chase - Fraction of f2's residual prediction to use threshold - Number of standard deviations the residual prediction must exceed before we chase it. r1 - hyper-params for f1, if any r2 - hyper-params for f2, if any """ y0 = wrap(y)[0] if not s.get('p1'): s = {'p1': {}, 'x': y0, 's1': {}, 's2': {}, 'n_obs': 0} if y0 is None: return None, None, s else: # Use the first skater to predict if r1 is None: x1, x1_std, s['s1'] = f1(y=y, s=s['s1'], k=k, a=a, t=t, e=e) else: x1, x1_std, s['s1'] = f1(y=y, s=s['s1'], k=k, a=a, t=t, e=e, r=r1) x1_error_mean, x1_error_std, s['p1'] = parade( p=s['p1'], x=x1, y=y0) # Update prediction queue s['n_obs'] += 1 # Use the second skater to predict mean residual k-steps ahead xke = x1_error_mean[-1] if r2 is None: xke_hat_mean, xke_hat_std, s['s2'] = f2(y=[xke], s=s['s2'], k=k, a=a, t=t, e=e) else: xke_hat_mean, xke_hat_std, s['s2'] = f2(y=[xke], s=s['s2'], k=k, a=a, t=t, e=e, r=r2) # If the bias prediction is confident, adjust x1 chasing it towards the bias corrected value if s['n_obs'] > 10: for j in range(len(x1)): if abs(xke_hat_mean[j]) > threshold * xke_hat_std[j]: x1[j] = x1[j] + chase * xke_hat_mean[j] return x1, x1_std, s