@@ -3036,6 +3036,319 @@ def _react_series(self, trainee_id: str, params: dict):
3036
3036
3037
3037
return ret , in_size , out_size
3038
3038
3039
+ def react_series_stationary (
3040
+ self ,
3041
+ trainee_id : str ,
3042
+ action_features : Collection [str ],
3043
+ * ,
3044
+ batch_size : t .Optional [int ] = None ,
3045
+ context_features : t .Optional [Collection [str ]] = None ,
3046
+ desired_conviction : t .Optional [float ] = None ,
3047
+ initial_batch_size : t .Optional [int ] = None ,
3048
+ input_is_substituted : bool = False ,
3049
+ progress_callback : t .Optional [Callable ] = None ,
3050
+ series_context_features : t .Optional [Collection [str ]] = None ,
3051
+ series_context_values : t .Optional [TabularData3D ] = None ,
3052
+ series_id_features : t .Optional [Collection [str ]] = None ,
3053
+ series_id_values : t .Optional [TabularData2D ] = None ,
3054
+ use_case_weights : t .Optional [bool ] = None ,
3055
+ use_derived_ts_features : bool = True ,
3056
+ use_regional_residuals : bool = True ,
3057
+ weight_feature : t .Optional [str ] = None ,
3058
+ ) -> Reaction :
3059
+ r"""
3060
+ React to series data predicting stationary feature values.
3061
+
3062
+ Parameters
3063
+ ----------
3064
+ trainee_id : str
3065
+ The ID of the Trainee.
3066
+ action_features : collection of str
3067
+ List of feature names specifying the features whose values to predict
3068
+ for each specified series.
3069
+ batch_size: int, optional
3070
+ Define the number of series to react to at once. If left
3071
+ unspecified, the batch size will be determined automatically.
3072
+ context_features : collection of str, optional
3073
+ List of features names specifying what features will be used as contexts
3074
+ to predict the values of the action features.
3075
+ desired_conviction : float, optional
3076
+ If specified will execute a generative react. If not
3077
+ specified will executed a discriminative react. Conviction is the
3078
+ ratio of expected surprisal to generated surprisal for each
3079
+ feature generated, valid values are in the range of
3080
+ :math:`(0, \infty)`.
3081
+ initial_batch_size: int, optional
3082
+ The number of series to react to in the first batch. If unspecified,
3083
+ the number will be determined automatically. The number of series
3084
+ in following batches will be automatically adjusted. This value is
3085
+ ignored if ``batch_size`` is specified.
3086
+ input_is_substituted : bool, default False
3087
+ If True, assumes provided nominal feature values have
3088
+ already been substituted.
3089
+ progress_callback : callable, optional
3090
+ A callback method that will be called before each
3091
+ batched call to react series stationary and at the end of reacting.
3092
+ The method is given a ProgressTimer containing metrics on the
3093
+ progress and timing of the react series operation, and the batch result.
3094
+ series_context_features : list of str, optional
3095
+ The list of feature names corresponding to the values in each row of
3096
+ ``series_context_values``. This value is ignored if
3097
+ ``series_context_values`` is not specified.
3098
+ series_context_values : list of list of list of object, optional
3099
+ 3d list of feature values defining a list of series, which are lists
3100
+ of lists of values. When specified, the values are treated as a
3101
+ series whose stationary feature values are to be predicted
3102
+ series_id_features : list of str, optional
3103
+ List of feature names corresponding to the values in each row of
3104
+ ``series_id_values``. This value is ignored if ``series_id_values``
3105
+ is not specified. If specified, all series ID features should be
3106
+ contained within the given list.
3107
+ series_id_values : list of list of object, optional
3108
+ 2d list of ID feature values. Each sublist should specify ID
3109
+ feature values that can uniquely identify the cases making up a
3110
+ single series.
3111
+ use_case_weights : bool, optional
3112
+ If True, then the Trainee will use case weights identified by the
3113
+ name given in ``weight_feature``. If False, case weights will not
3114
+ be used. If unspecified, case weights will be used if the Trainee
3115
+ has them.
3116
+ use_derived_ts_features : bool, default True
3117
+ If True, then time-series features derived from features specified
3118
+ as contexts will additionally be added as context features.
3119
+ use_regional_residuals : bool, default True
3120
+ If False, global residuals will be used in generative predictions.
3121
+ If True, regional residuals will be computed and used instead. This
3122
+ may increase runtime noticeable.
3123
+ weight_feature : str, optional
3124
+ The name of the weight feature to be used. Should be used in
3125
+ combination with ``use_case_weights``.
3126
+
3127
+ Returns
3128
+ -------
3129
+ Reaction
3130
+ A MutableMapping (dict-like) with these keys -> values:
3131
+ action -> pandas.DataFrame
3132
+ A DataFrame of action values.
3133
+ details -> dict or list
3134
+ A dict containing details.
3135
+
3136
+ Raises
3137
+ ------
3138
+ ValueError
3139
+ If `action_features` is not a list of strings.
3140
+ If `context_features` is not a list of strings.
3141
+ If `series_context_features` is not a list of strings.
3142
+ If `series_id_features` is not a list of strings.
3143
+
3144
+ If both `series_id_values` and `series_context_values` are
3145
+ specified.
3146
+ """
3147
+ trainee_id = self ._resolve_trainee (trainee_id ).id
3148
+ feature_attributes = self .resolve_feature_attributes (trainee_id )
3149
+ util .validate_list_shape (action_features , 1 , "action_features" , "str" )
3150
+ util .validate_list_shape (context_features , 1 , "context_features" , "str" )
3151
+ util .validate_list_shape (series_context_features , 1 , "series_context_features" , "str" )
3152
+ util .validate_list_shape (series_id_features , 1 , "series_id_features" , "str" )
3153
+
3154
+ if (series_id_values is not None and series_context_values is not None ):
3155
+ raise ValueError ((
3156
+ "`series_id_values` and `series_context_values` cannot both be "
3157
+ "specified."
3158
+ ))
3159
+
3160
+ if series_id_values is not None :
3161
+ total_size = len (series_id_values )
3162
+ elif series_context_values is not None :
3163
+ total_size = len (series_context_values )
3164
+ else :
3165
+ raise ValueError ((
3166
+ "Either `series_id_values` or `series_context_values` must be specified."
3167
+ ))
3168
+
3169
+ serialized_series_context_values = None
3170
+ if series_context_values is not None :
3171
+ serialized_series_context_values = []
3172
+ for series in series_context_values :
3173
+ if series_context_features is None :
3174
+ series_context_features = internals .get_features_from_data (
3175
+ data = series ,
3176
+ data_parameter = "series_context_values" ,
3177
+ features_parameter = "series_context_features" )
3178
+ serialized_series_context_values .append (
3179
+ serialize_cases (series , series_context_features , feature_attributes ))
3180
+
3181
+ if series_id_values is not None and series_id_features is None :
3182
+ series_id_features = internals .get_features_from_data (
3183
+ series_id_values ,
3184
+ data_parameter = 'series_id_values' ,
3185
+ features_parameter = 'series_id_features' )
3186
+ serialized_series_id_values = serialize_cases (series_id_values , series_id_features , feature_attributes )
3187
+
3188
+ react_stationary_params = {
3189
+ "action_features" : action_features ,
3190
+ "context_features" : context_features ,
3191
+ "desired_conviction" : desired_conviction ,
3192
+ "input_is_substituted" : input_is_substituted ,
3193
+ "series_context_features" : series_context_features ,
3194
+ "series_context_values" : serialized_series_context_values ,
3195
+ "series_id_features" : series_id_features ,
3196
+ "series_id_values" : serialized_series_id_values ,
3197
+ "use_case_weights" : use_case_weights ,
3198
+ "use_derived_ts_features" : use_derived_ts_features ,
3199
+ "use_regional_residuals" : use_regional_residuals ,
3200
+ "weight_feature" : weight_feature ,
3201
+ }
3202
+
3203
+ if self ._should_react_batch (react_stationary_params , total_size ):
3204
+ if self .configuration .verbose :
3205
+ print (f'Batch stationary series reacting on trainee with id: { trainee_id } ' )
3206
+ response = self ._batch_react_series_stationary (
3207
+ trainee_id ,
3208
+ react_stationary_params ,
3209
+ total_size = total_size ,
3210
+ batch_size = batch_size ,
3211
+ initial_batch_size = initial_batch_size ,
3212
+ progress_callback = progress_callback )
3213
+ else :
3214
+ if self .configuration .verbose :
3215
+ print (f'Stationary series reacting on trainee with id: { trainee_id } ' )
3216
+ with ProgressTimer (total_size ) as progress :
3217
+ if isinstance (progress_callback , Callable ):
3218
+ progress_callback (progress , None )
3219
+ response , _ , _ = self ._react_series_stationary (trainee_id , react_stationary_params )
3220
+ progress .update (total_size )
3221
+
3222
+ if isinstance (progress_callback , Callable ):
3223
+ progress_callback (progress , response )
3224
+
3225
+ if response is None :
3226
+ response = dict ()
3227
+ self ._auto_persist_trainee (trainee_id )
3228
+ response = internals .format_react_response (response )
3229
+ return Reaction (response .get ('action' ), response .get ('details' ))
3230
+
3231
+ def _batch_react_series_stationary ( # noqa: C901
3232
+ self ,
3233
+ trainee_id : str ,
3234
+ params : dict ,
3235
+ * ,
3236
+ batch_size : t .Optional [int ] = None ,
3237
+ initial_batch_size : t .Optional [int ] = None ,
3238
+ total_size : int ,
3239
+ progress_callback : t .Optional [Callable ] = None
3240
+ ):
3241
+ """
3242
+ Make react series stationary requests in batch.
3243
+
3244
+ Parameters
3245
+ ----------
3246
+ trainee_id : str
3247
+ The ID of the Trainee to react to.
3248
+ params : dict
3249
+ The engine react series stationary parameters.
3250
+ batch_size: int, optional
3251
+ Define the number of series to react to at once. If left
3252
+ unspecified, the batch size will be determined automatically.
3253
+ initial_batch_size: int, optional
3254
+ The number of series to react to in the first batch. If unspecified,
3255
+ the number will be determined automatically. The number of series
3256
+ in following batches will be automatically adjusted. This value is
3257
+ ignored if ``batch_size`` is specified.
3258
+ total_size : int
3259
+ The total size of the data that will be batched.
3260
+ progress_callback : callable, optional
3261
+ A function to be called during batching to retrieve or
3262
+ report progress metrics.
3263
+
3264
+ Returns
3265
+ -------
3266
+ dict
3267
+ The `react_series_stationary` response.
3268
+ """
3269
+ temp_result = None
3270
+ accumulated_result = {'action_values' : []}
3271
+
3272
+ series_id_values = params .get ('series_id_values' )
3273
+ series_context_values = params .get ('series_context_values' )
3274
+
3275
+ with ProgressTimer (total_size ) as progress :
3276
+ batch_scaler = None
3277
+ gen_batch_size = None
3278
+ if not batch_size :
3279
+ if not initial_batch_size :
3280
+ start_batch_size = max (self ._get_trainee_thread_count (trainee_id ), 1 )
3281
+ else :
3282
+ start_batch_size = initial_batch_size
3283
+ batch_scaler = self .batch_scaler_class (start_batch_size , progress )
3284
+ gen_batch_size = batch_scaler .gen_batch_size ()
3285
+ batch_size = next (gen_batch_size , None )
3286
+
3287
+ while not progress .is_complete and batch_size is not None :
3288
+ if isinstance (progress_callback , Callable ):
3289
+ progress_callback (progress , temp_result )
3290
+ batch_start = progress .current_tick
3291
+ batch_end = progress .current_tick + batch_size
3292
+
3293
+ if series_id_values is not None :
3294
+ params ['series_id_values' ] = series_id_values [batch_start :batch_end ]
3295
+ if series_context_values is not None :
3296
+ params ['series_context_values' ] = series_context_values [batch_start :batch_end ]
3297
+
3298
+ temp_result , in_size , out_size = self ._react_series_stationary (trainee_id , params )
3299
+
3300
+ internals .accumulate_react_result (accumulated_result , temp_result )
3301
+ if batch_scaler is None or gen_batch_size is None :
3302
+ progress .update (batch_size )
3303
+ else :
3304
+ batch_size = batch_scaler .send (
3305
+ gen_batch_size ,
3306
+ batch_scaler .SendOptions (None , (in_size , out_size )))
3307
+
3308
+ # Final call to callback on completion
3309
+ if isinstance (progress_callback , Callable ):
3310
+ progress_callback (progress , temp_result )
3311
+
3312
+ return accumulated_result
3313
+
3314
+ def _react_series_stationary (self , trainee_id : str , params : dict ):
3315
+ """
3316
+ Make a single react series stationary request.
3317
+
3318
+ Parameters
3319
+ ----------
3320
+ trainee_id : str
3321
+ The id of the trainee.
3322
+ params : dict
3323
+ The engine react series stationary parameters.
3324
+
3325
+ Returns
3326
+ -------
3327
+ dict
3328
+ The react series stationary response.
3329
+ int
3330
+ The request payload size.
3331
+ int
3332
+ The response payload size.
3333
+ """
3334
+ batch_result , in_size , out_size = self .execute_sized (trainee_id , "react_series_stationary" , params )
3335
+
3336
+ if batch_result is None or batch_result .get ('action_values' ) is None :
3337
+ raise ValueError ('Invalid parameters passed to react_series_stationary.' )
3338
+
3339
+ ret = dict ()
3340
+ batch_result = util .replace_doublemax_with_infinity (batch_result )
3341
+
3342
+ # batch_result always has action_features and action_values
3343
+ ret ['action_features' ] = batch_result .pop ('action_features' ) or []
3344
+ ret ['action_values' ] = batch_result .pop ('action_values' )
3345
+
3346
+ # ensure all the details items are output as well
3347
+ for k , v in batch_result .items ():
3348
+ ret [k ] = [] if v is None else v
3349
+
3350
+ return ret , in_size , out_size
3351
+
3039
3352
def react_into_features (
3040
3353
self ,
3041
3354
trainee_id : str ,
0 commit comments