diff --git a/examples/2-complex_systems.ipynb b/examples/2-complex_systems.ipynb index 54f657b..f53a308 100644 --- a/examples/2-complex_systems.ipynb +++ b/examples/2-complex_systems.ipynb @@ -1,21 +1,8 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "initial_id", - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "" - ] - } - ], + "cells": [], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "aeon", "language": "python", "name": "python3" }, @@ -29,7 +16,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.6" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/s2generator/__init__.py b/s2generator/__init__.py index f70e3cc..925327f 100644 --- a/s2generator/__init__.py +++ b/s2generator/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -__version__ = "0.0.10" +__version__ = "0.0.11" __all__ = [ "Node", diff --git a/s2generator/excitation/forecast_pfn.py b/s2generator/excitation/forecast_pfn.py index 7464d4c..ba5f818 100644 --- a/s2generator/excitation/forecast_pfn.py +++ b/s2generator/excitation/forecast_pfn.py @@ -246,6 +246,7 @@ def make_series_trend( days = (dates - dates[0]).days if series.scale.linear is not None: values += shift_axis(days, series.offset.linear) * series.scale.linear + if series.scale.exp is not None: values *= np.power(series.scale.exp, shift_axis(days, series.offset.exp)) @@ -368,11 +369,16 @@ def make_series( if random_walk: values = get_random_walk_series(rng=rng, length=len(dates)) else: + # generate the trend component of the series values_trend = make_series_trend(series=series, dates=dates) + + # generate the seasonal component of the series and multiply it with the trend values_seasonal = make_series_seasonal(rng=rng, series=series, dates=dates) + # combine the trend and seasonal components to get the base series values values = values_trend * values_seasonal["seasonal"] + # generate the noise term of the series and add it to the base series values weibull_noise_term = weibull_noise( rng=rng, k=series.noise_config.k, @@ -424,6 +430,7 @@ def __init__( transition: Optional[bool] = True, start_time: Optional[str] = "1885-01-01", end_time: Optional[str] = None, + exp_trend: Optional[bool] = True, random_walk: bool = False, dtype: np.dtype = np.float64, ) -> None: @@ -431,17 +438,12 @@ def __init__( Initializes the time series generator with configuration parameters. :param is_sub_day: Enable sub-daily frequency components (minutes/hours) - :type is_sub_day: Optional[bool] :param transition: Enable smooth transitions between frequency components - :type transition: Optional[bool] :param start_time: Start timestamp for generated series (ISO format: YYYY-MM-DD) - :type start_time: Optional[str] :param end_time: End timestamp for generated series. Uses current date if None. - :type end_time: Optional[str] :param random_walk: Enable random walk transformation for non-stationary series - :type random_walk: bool + :param exp_trend: Enable exponential trend transformation :param dtype: Numerical precision for output series - :type dtype: np.dtype """ super().__init__(dtype=dtype) self.is_sub_day = is_sub_day @@ -484,6 +486,9 @@ def __init__( # Global series configuration self._time_series_config: Optional[SeriesConfig] = None + # Whether to apply exponential trend transformation + self.exp_trend = exp_trend + def __call__( self, rng: np.random.RandomState, @@ -748,7 +753,7 @@ def generate_series( freq_index: int = None, start: pd.Timestamp = None, options: Optional[dict] = None, - random_walk: bool = False, # TODO: 是否可以添加为类属性 + random_walk: bool = False, ) -> Dict[str, Union[pd.Series, np.ndarray, pd.DataFrame, DatetimeIndex]]: """ Function to construct synthetic series configs and generate synthetic series. @@ -793,7 +798,9 @@ def generate_series( self._scale_config = self.get_component_scale_config( base=1.0, linear=rng.normal(loc=0.0, scale=0.01), - exp=rng.normal(loc=1.0, scale=0.005 / timescale), + exp=rng.normal(loc=1.0, scale=0.001 / timescale) + if self.exp_trend + else None, annual=self._annual, monthly=self._monthly, weekly=self._weekly, @@ -805,7 +812,7 @@ def generate_series( self._offset_config = self.get_component_scale_config( base=0.0, linear=rng.normal(loc=-0.1, scale=0.5), - exp=rng.normal(loc=-0.1, scale=0.5), + exp=rng.normal(loc=-0.1, scale=0.5 / timescale), annual=self._annual, monthly=self._monthly, weekly=self._weekly, @@ -859,6 +866,9 @@ def _select_ndarray_from_dict( :param options: Options dict for generating series. :return: The selected time series data with `np.ndarray`. """ + + # Generate two time series segments using the `generate_series` method. + # These segments will be used to create a transition series if the transition option is enabled. series1 = self.generate_series( rng=rng, length=length, @@ -867,7 +877,6 @@ def _select_ndarray_from_dict( options=options, random_walk=self.random_walk, ) - series2 = self.generate_series( rng=rng, length=length, @@ -877,6 +886,9 @@ def _select_ndarray_from_dict( random_walk=self.random_walk, ) + # If transition is enabled, + # compute the transition coefficients and create a blended series that transitions from series1 to series2. + # Otherwise, use series1 directly. if self.transition: coefficients = get_transition_coefficients(context_length=length) values = ( @@ -930,14 +942,28 @@ def generate( ) # Generate each dimension independently - for i in range(input_dimension): - time_series[:, i] = self._select_ndarray_from_dict( + index = 0 + while index < input_dimension: + generated_series = self._select_ndarray_from_dict( rng=rng, length=n_inputs_points, freq_index=freq_index, start=start, options=options, ) + + # Here, we check if NaN values ​​exist. + # If they do, we regenerate until no NaN values ​​are found. + if np.any(np.isnan(generated_series)): + continue # Skip this iteration and regenerate the series + + # If the generated series does not contain NaN values, + # assign them to the output array and continue generating the next dimension. + time_series[:, index] = generated_series + + # Increment the index to generate the next dimension + index += 1 + return time_series @property diff --git a/setup.py b/setup.py index 1d53599..5a5ebf2 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="S2Generator", packages=setuptools.find_packages(), - version="0.0.10", + version="0.0.11", description="A series-symbol (S2) dual-modality data generation mechanism, enabling the unrestricted creation of high-quality time series data paired with corresponding symbolic representations.", # 包的简短描述 url="https://github.com/wwhenxuan/S2Generator", author="whenxuan, johnfan12, changewam", diff --git a/tests/test_augmentation.py b/tests/test_augmentation.py index 675494f..d8a78de 100644 --- a/tests/test_augmentation.py +++ b/tests/test_augmentation.py @@ -258,5 +258,6 @@ def test_wiener_filter(self) -> None: msg="Filtered series is identical to original series in `test_wiener_filter` method", ) + if __name__ == "__main__": unittest.main()