Repository: ruleminer/InterpreTS (language: python)

Issue:
Development update 
* Implementation of basic features
* Implementation of tests for basic features
* Implementation of examples for basic features
* Pypi package configuration



Discussion / hints:

Fix patch:
diff --git a/github/workflows/dependabot.yml b/.github/dependabot.yml
similarity index 100%
rename from github/workflows/dependabot.yml
rename to .github/dependabot.yml
diff --git a/github/workflows/generate_docs.yml b/.github/workflows/generate_docs.yml
similarity index 80%
rename from github/workflows/generate_docs.yml
rename to .github/workflows/generate_docs.yml
index 1f5f3cc..88c29cd 100644
--- a/github/workflows/generate_docs.yml
+++ b/.github/workflows/generate_docs.yml
@@ -1,8 +1,10 @@
 name: Generate Documentation
 
 on:
+  push:
+    branches:
+      - main
   release:
-    types: [published]
 
 jobs:
   build-docs:
@@ -21,7 +23,7 @@ jobs:
         run: |
           python -m pip install --upgrade pip
           pip install -r requirements.txt
-          pip install sphinx-book-theme
+          pip install pydata-sphinx-theme
 
       - name: Build documentation
         run: |
@@ -31,5 +33,5 @@ jobs:
         if: success()
         uses: peaceiris/actions-gh-pages@v3
         with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
-          publish_dir: docs/build
+          github_token: ${{ secrets.GH_PAGES_TOKEN }}
+          publish_dir: build
diff --git a/github/workflows/publish_to_pypi.yml b/.github/workflows/publish_to_pypi.yml
similarity index 83%
rename from github/workflows/publish_to_pypi.yml
rename to .github/workflows/publish_to_pypi.yml
index bcc2726..63ed30b 100644
--- a/github/workflows/publish_to_pypi.yml
+++ b/.github/workflows/publish_to_pypi.yml
@@ -28,6 +28,5 @@ jobs:
 
       - name: Publish package
         env:
-          TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
-          TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+          TWINE_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
         run: twine upload dist/*
diff --git a/docs/README.md b/docs/README.md
index b34c9c7..37b0948 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -24,65 +24,64 @@ Follow these steps to install InterpreTS and its dependencies:
 1. **Clone the Repository**  
    Clone the InterpreTS repository to your local machine:
    
-   ```bash
-   git clone https://github.com/ruleminer/InterpreTS.git
-   cd InterpreTS
-   ```
+```bash
+git clone https://github.com/ruleminer/InterpreTS.git
+cd InterpreTS
+```
 
 2. Install dependencies: Install the required packages listed in the `requirements.txt` file:
 
-    ```python
-    pip install -r requirements.txt
-    ```
+ ```python
+ pip install -r requirements.txt
+ ```
 
 3. Install InterpreTS: Run the following command to install InterpreTS:
 
-    ```python
-    pip install interpreTS
-    ```
-
+ ```python
+ pip install interpreTS
+ ```
 
 ## Verifying Installation
 Once installed, you can verify the installation by running a simple feature extraction example:
 
-    ```python
-    from interpreTS.core.feature_extractor import FeatureExtractor, Features
-    import pandas as pd
+ ```python
+ from interpreTS.core.feature_extractor import FeatureExtractor, Features
+ import pandas as pd
 
-    # Sample time series data
-    data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
-    extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])
-    features = extractor.extract_features(data)
-    print("Extracted Features:\n", features)
-    ```
+ # Sample time series data
+ data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
+ extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])
+ features = extractor.extract_features(data)
+ print("Extracted Features:\n", features)
+ ```
 
 ## Additional Usage Example with Time Series Data
 You can also use InterpreTS with time-indexed data:
 
-    ```python
+ ```python
 
-    from interpreTS.core.time_series_data import TimeSeriesData
-    from interpreTS.core.feature_extractor import FeatureExtractor, Features
-    import pandas as pd
+ from interpreTS.core.time_series_data import TimeSeriesData
+ from interpreTS.core.feature_extractor import FeatureExtractor, Features
+ import pandas as pd
 
-    # Time-indexed data
-    data_with_date = pd.Series(
-        [5, 3, 6, 2, 7, 4, 8, 3, 9, 1],
-        index=pd.date_range("2023-01-01", periods=10, freq="D")
-    )
-    ts_data = TimeSeriesData(data_with_date)
+ # Time-indexed data
+ data_with_date = pd.Series(
+     [5, 3, 6, 2, 7, 4, 8, 3, 9, 1],
+     index=pd.date_range("2023-01-01", periods=10, freq="D")
+ )
+ ts_data = TimeSeriesData(data_with_date)
 
-    # Feature extraction
-    extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])
-    features = extractor.extract_features(ts_data.data)
-    print("\nExtracted Features from Time Series Data:\n", features)
-    ```
+ # Feature extraction
+ extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])
+ features = extractor.extract_features(ts_data.data)
+ print("\nExtracted Features from Time Series Data:\n", features)
+ ```
 
 ## Documentation
 
-Complete documentation is available in the [docs folder](./docs).
+Complete documentation is available on [GitHub Pages](https://ruleminer.github.io/InterpreTS/).
 
 
 ## Issues and Support
 
-For any issues, please consult our [Issue Tracker](https://github.com/ruleminer/InterpreTS/issues) on GitHub.
\ No newline at end of file
+For any issues, please consult our [Issue Tracker](https://github.com/ruleminer/InterpreTS/issues) on GitHub.
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 5ef0293..761dedb 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -18,7 +18,7 @@
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
 
 extensions = ['sphinx.ext.autodoc']
-html_theme = 'sphinx_book_theme'
+html_theme = 'pydata_sphinx_theme'
 templates_path = ['_templates']
 exclude_patterns = ['setup.py']
 suppress_warnings = ["autodoc.import_object", "autodoc"]
@@ -26,5 +26,4 @@
 # -- Options for HTML output -------------------------------------------------
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
 
-html_theme = 'alabaster'
 html_static_path = ['_static']
diff --git a/github/workflows/security_check.yml b/github/workflows/security_check.yml
deleted file mode 100644
index 2fa56c1..0000000
--- a/github/workflows/security_check.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Security Check - Bandit
-
-on: [push, pull_request]
-
-jobs:
-  security:
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Checkout repository
-        uses: actions/checkout@v3
-
-      - name: Set up Python
-        uses: actions/setup-python@v3
-        with:
-          python-version: '3.x'
-
-      - name: Install Bandit
-        run: |
-          python -m pip install --upgrade pip
-          pip install bandit
-
-      - name: Run Bandit
-        run: |
-          bandit -r .
diff --git a/interpreTS/core/features/variability_in_sub_periods.py b/interpreTS/core/features/variability_in_sub_periods.py
new file mode 100644
index 0000000..151a146
--- /dev/null
+++ b/interpreTS/core/features/variability_in_sub_periods.py
@@ -0,0 +1,66 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_variability_in_sub_periods(data, window_size, step_size=None, ddof=0):
+    """
+    Calculate the variance within sub-periods of a time series, providing a measure of variability.
+    
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the variability is to be calculated.
+    window_size : int
+        The size of each sub-period window (number of points in each window).
+    step_size : int, optional
+        The step size between sub-periods. If None, it defaults to window_size (non-overlapping windows).
+    ddof : int, optional
+        The degrees of freedom to use when calculating variance within each sub-period. Default is 0 (population variance).
+        
+    Returns
+    -------
+    pd.Series
+        A series of variance values representing the variability in each sub-period.
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values or if window_size is larger than the data length.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+    >>> calculate_variability_in_sub_periods(data, window_size=5)
+    0    2.5
+    1    2.5
+    dtype: float64
+    """
+    # Validate the time series data without requiring a DateTime index
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Convert numpy array to pandas Series if necessary
+    if isinstance(data, np.ndarray):
+        data = pd.Series(data)
+    
+    # Handle edge cases
+    if len(data) < window_size:
+        raise ValueError("Window size must be smaller than or equal to the length of the data.")
+    
+    if step_size is None:
+        step_size = window_size  # Default to non-overlapping windows
+
+    # Initialize a list to hold variability values
+    variability_measures = []
+
+    # Calculate variance for each sub-period
+    for start in range(0, len(data) - window_size + 1, step_size):
+        sub_period = data[start:start + window_size]
+        
+        # Append the calculated variance for the sub-period
+        variability_measures.append(np.var(sub_period, ddof=ddof))
+    
+    # Return as a Pandas Series for easier handling of the result
+    return pd.Series(variability_measures)
\ No newline at end of file


Test patch:
diff --git a/github/workflows/python-tests.yml b/.github/workflows/python-tests.yml
similarity index 100%
rename from github/workflows/python-tests.yml
rename to .github/workflows/python-tests.yml
diff --git a/tests/core/features/test_variability_in_sub_periods.py b/tests/core/features/test_variability_in_sub_periods.py
new file mode 100644
index 0000000..9584567
--- /dev/null
+++ b/tests/core/features/test_variability_in_sub_periods.py
@@ -0,0 +1,85 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.variability_in_sub_periods import calculate_variability_in_sub_periods
+
+def test_calculate_variability_in_sub_periods_strong_variability():
+    """
+    Test that calculate_variability_in_sub_periods identifies strong variability
+    in a time series with large fluctuations.
+    """
+    data = pd.Series([1, 10, 1, 10, 1, 10, 1, 10])
+    result = calculate_variability_in_sub_periods(data, window_size=4)
+    assert all(val > 10 for val in result), "The variability should be high for data with strong fluctuations"
+
+def test_calculate_variability_in_sub_periods_weak_variability():
+    """
+    Test that calculate_variability_in_sub_periods identifies weak variability
+    in a time series with small fluctuations.
+    """
+    data = pd.Series([1, 1.1, 1.2, 1.1, 1, 1.05, 1.1, 1.15])
+    result = calculate_variability_in_sub_periods(data, window_size=4)
+    assert all(val < 0.1 for val in result), "The variability should be low for data with weak fluctuations"
+
+def test_calculate_variability_in_sub_periods_no_variability():
+    """
+    Test that calculate_variability_in_sub_periods identifies no variability
+    in a time series with constant values.
+    """
+    data = pd.Series([5, 5, 5, 5, 5, 5, 5, 5])
+    result = calculate_variability_in_sub_periods(data, window_size=4)
+    assert all(val == 0 for val in result), "The variability should be zero for constant data"
+
+def test_calculate_variability_in_sub_periods_empty_series():
+    """
+    Test that calculate_variability_in_sub_periods returns an empty list for an empty series.
+    """
+    data = pd.Series([])
+    result = calculate_variability_in_sub_periods(data, window_size=4)
+    assert result.empty, "The result should be empty for an empty series"
+
+def test_calculate_variability_in_sub_periods_insufficient_data():
+    """
+    Test that calculate_variability_in_sub_periods returns an empty result when data
+    is insufficient for the specified window size.
+    """
+    data = pd.Series([5, 6])
+    result = calculate_variability_in_sub_periods(data, window_size=4)
+    assert result.empty, "The result should be empty for data shorter than the window size"
+
+def test_calculate_variability_in_sub_periods_with_numpy_array():
+    """
+    Test that calculate_variability_in_sub_periods works correctly with a numpy array.
+    """
+    data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+    result = calculate_variability_in_sub_periods(data, window_size=5)
+    assert len(result) == 2, "The result should have two values for a numpy array with a window size of 5"
+    assert all(isinstance(val, float) for val in result), "Each result should be a float (variance value)"
+
+def test_calculate_variability_in_sub_periods_with_overlap():
+    """
+    Test that calculate_variability_in_sub_periods handles overlapping sub-periods correctly.
+    """
+    data = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+    result = calculate_variability_in_sub_periods(data, window_size=5, step_size=2)
+    expected_length = (len(data) - 5) // 2 + 1  # Expected number of overlapping windows
+    assert len(result) == expected_length, "Overlap calculation failed"
+    assert all(isinstance(v, float) for v in result), "Each result should be a float (variance value)"
+
+def test_calculate_variability_in_sub_periods_high_variance():
+    """
+    Test that calculate_variability_in_sub_periods identifies high variance in sub-periods
+    with data that fluctuates greatly within each window.
+    """
+    data = pd.Series([1, 100, 1, 100, 1, 100, 1, 100, 1, 100])
+    result = calculate_variability_in_sub_periods(data, window_size=3)
+    assert all(val > 1000 for val in result), "The variability should be high for high-fluctuation data"
+
+def test_calculate_variability_in_sub_periods_with_small_window_size():
+    """
+    Test that calculate_variability_in_sub_periods correctly calculates variability with a small window size.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    result = calculate_variability_in_sub_periods(data, window_size=2)
+    assert len(result) == len(data) - 1, "The number of results should be data length - 1 with a window size of 2"
+    assert result.iloc[0] == 0.5, "The variance of the first window should be 0.5"
\ No newline at end of file