diff --git a/model_manager/sientia/models.py b/model_manager/sientia/models.py index fd3cacc..267efca 100644 --- a/model_manager/sientia/models.py +++ b/model_manager/sientia/models.py @@ -10,6 +10,19 @@ from sklearn.preprocessing import StandardScaler class LinearRegressionModel(BaseEstimator, TransformerMixin): + """ + Linear Regression Model for Time Series Analysis. + + Thread-safety: This class is NOT thread-safe during fit() operations. + Do not call fit() on the same instance from multiple threads simultaneously. + After fitting, predict() is thread-safe for read-only operations. + + For multi-threaded environments: + - Fit the model in a single thread + - Share the fitted instance across threads for prediction only + - Or create separate instances per thread + """ + def __init__( self, target_variable: str = '', @@ -99,6 +112,21 @@ class LinearRegressionModel(BaseEstimator, TransformerMixin): class DataPreprocessor(BaseEstimator, TransformerMixin): + """ + Data Preprocessor for Time Series Analysis. + + Thread-safety: This class is NOT thread-safe during fit() operations. + Do not call fit() on the same instance from multiple threads simultaneously. + After fitting, transform() is thread-safe for read-only operations IF the + input DataFrames are not shared between threads. + + For multi-threaded environments: + - Fit the preprocessor in a single thread + - Share the fitted instance across threads for transform() only + - Ensure each thread passes its own DataFrame copy to transform() + - Or create separate instances per thread + """ + def __init__( self, date_column: str = '', @@ -176,9 +204,11 @@ class DataPreprocessor(BaseEstimator, TransformerMixin): self.static_threshold = static_threshold self.low_lim = low_lim self.upp_lim = upp_lim - # self.window = window + # self.window = window # Not implemented yet self.scaler_name = scaler_name self.scaler_params = scaler_params + self.feature_names_order: list[str] = [] # Initialize to avoid AttributeError + if self.scaler_name == 'Standard Scaler': self.scaler = StandardScaler() elif self.scaler_name == 'None': @@ -299,11 +329,16 @@ class DataPreprocessor(BaseEstimator, TransformerMixin): Returns: pandas.DataFrame: The treated data + + Note: + This method modifies input_data in-place. Ensure the caller passes + a copy if the original DataFrame needs to be preserved. """ if lag_dict: for var, lag in lag_dict.items(): if lag > 0: input_data[var] = input_data[var].shift(lag) + # WARNING: Modifies DataFrame in-place input_data.dropna(inplace=True) return input_data @@ -366,9 +401,13 @@ class DataPreprocessor(BaseEstimator, TransformerMixin): Returns: pandas.DataFrame: The treated data + + Note: + This method modifies input_data in-place. """ if self.ar_var: input_data[self.ar_var] = input_data[self.target_variable].shift(1) + # WARNING: Modifies DataFrame in-place input_data.dropna(inplace=True) return input_data @@ -381,12 +420,16 @@ class DataPreprocessor(BaseEstimator, TransformerMixin): Returns: pandas.DataFrame: The treated data + + Note: + This method modifies input_data in-place. """ if self.created_lags: for var, lag in self.created_lags.items(): if lag > 0 and var in input_data.columns: new_col = f'{var}_lag{lag}' input_data[new_col] = input_data[var].shift(lag) + # WARNING: Modifies DataFrame in-place input_data.dropna(inplace=True) return input_data