Skip to content

Commit 5d35288

Browse files
authored
22690: Adds react_series_stationary to the client, MINOR (#346)
Adds the new method to the client which will be able to hit the new method added to the engine
1 parent b80671f commit 5d35288

File tree

3 files changed

+462
-0
lines changed

3 files changed

+462
-0
lines changed

howso/client/base.py

+313
Original file line numberDiff line numberDiff line change
@@ -3036,6 +3036,319 @@ def _react_series(self, trainee_id: str, params: dict):
30363036

30373037
return ret, in_size, out_size
30383038

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+
30393352
def react_into_features(
30403353
self,
30413354
trainee_id: str,

howso/client/pandas/client.py

+30
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,36 @@ def react_series(
190190
response['action'] = format_dataframe(response.get("action"), feature_attributes)
191191
return response
192192

193+
def react_series_stationary(
194+
self,
195+
trainee_id: str,
196+
*args,
197+
**kwargs,
198+
) -> Reaction:
199+
"""
200+
Base: :meth:`howso.client.AbstractHowsoClient.react_series_stationary`.
201+
202+
Parameters
203+
----------
204+
trainee_id : str
205+
The trainee id.
206+
207+
Returns
208+
-------
209+
Reaction:
210+
A MutableMapping (dict-like) with these keys -> values:
211+
action -> pandas.DataFrame
212+
A DataFrame of action values.
213+
214+
details -> dict or list
215+
An aggregated list of any requested details.
216+
"""
217+
trainee_id = self._resolve_trainee(trainee_id).id
218+
feature_attributes = self.resolve_feature_attributes(trainee_id)
219+
response = super().react_series_stationary(trainee_id, *args, **kwargs)
220+
response['action'] = format_dataframe(response.get("action"), feature_attributes)
221+
return response
222+
193223
def react(self, trainee_id, *args, **kwargs) -> Reaction:
194224
"""
195225
Base: :func:`howso.client.AbstractHowsoClient.react`.

0 commit comments

Comments
 (0)