Repository: ruleminer/InterpreTS (language: python)

Issue:
Add Mean feature, validation fixes and warning filters



Discussion / hints:

Fix patch:
diff --git a/src/core/features/histogram_dominant.py b/src/core/features/histogram_dominant.py
new file mode 100644
index 0000000..6b00164
--- /dev/null
+++ b/src/core/features/histogram_dominant.py
@@ -0,0 +1,49 @@
+import pandas as pd
+import numpy as np
+from src.utils.data_validation import validate_time_series_data
+
+def calculate_dominant(data, bins=10):
+    """
+    Calculate the dominant value (mode) of a time series histogram.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the dominant value is to be calculated.
+    bins : int, optional
+        The number of bins to use for creating the histogram, by default 10.
+
+    Returns
+    -------
+    float
+        The dominant value (mode) of the histogram.
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 1, 2, 3, 3, 3, 4, 5])
+    >>> calculate_dominant(data)
+    3.0
+    """
+    # Validate the time series data
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Check if data is empty after validation
+    if len(data) == 0:
+        return np.nan
+    
+    # Calculate histogram and find the bin with the maximum frequency
+    counts, bin_edges = np.histogram(data, bins=bins)
+    max_bin_index = np.argmax(counts)
+    
+    # Calculate the center of the bin with the highest frequency
+    dominant_value = (bin_edges[max_bin_index] + bin_edges[max_bin_index + 1]) / 2
+    
+    return dominant_value
\ No newline at end of file
diff --git a/src/core/features/trend_strength.py b/src/core/features/trend_strength.py
new file mode 100644
index 0000000..c8f1f3c
--- /dev/null
+++ b/src/core/features/trend_strength.py
@@ -0,0 +1,50 @@
+import pandas as pd
+import numpy as np
+from scipy.stats import linregress
+from src.utils.data_validation import validate_time_series_data
+
+def calculate_trend_strength(data):
+    """
+    Calculate the strength of the trend in a time series using linear regression.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the trend strength is to be calculated.
+        
+    Returns
+    -------
+    float
+        The R-squared value representing the strength of the trend (0 to 1).
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 4, 5])
+    >>> calculate_trend_strength(data)
+    1.0
+    """
+    # Validate the time series data
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Handle empty or insufficient data by returning NaN
+    if len(data) < 2:
+        return np.nan
+    
+    # Generate an index as a proxy for time
+    x = np.arange(len(data))
+    
+    # Fit a linear regression and calculate the R-squared value
+    slope, intercept, r_value, p_value, std_err = linregress(x, data)
+    
+    # R-squared value as trend strength
+    trend_strength = r_value ** 2
+    
+    return trend_strength
\ No newline at end of file


Test patch:
diff --git a/tests/core/features/test_histogram_dominant.py b/tests/core/features/test_histogram_dominant.py
new file mode 100644
index 0000000..65cd0c9
--- /dev/null
+++ b/tests/core/features/test_histogram_dominant.py
@@ -0,0 +1,50 @@
+import pytest
+import pandas as pd
+import numpy as np
+from src.core.features.histogram_dominant import calculate_dominant
+
+def test_calculate_dominant_basic():
+    """
+    Test that calculate_dominant correctly identifies the dominant value
+    in a simple time series.
+    """
+    data = pd.Series([1, 2, 2, 3, 4, 4, 4, 5])
+    assert calculate_dominant(data) == 4.0, "The dominant value should be 4.0"
+
+def test_calculate_dominant_empty_series():
+    """
+    Test that calculate_dominant returns NaN for an empty time series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_dominant(data)), "The dominant value of an empty series should be NaN"
+
+def test_calculate_dominant_numpy_array():
+    """
+    Test that calculate_dominant correctly identifies the dominant value in a numpy array.
+    """
+    data = np.array([1, 1, 2, 3, 3, 3, 4, 5])
+    assert calculate_dominant(data) == 3.0, "The dominant value should be 3.0 for the numpy array"
+
+def test_calculate_dominant_multiple_modes():
+    """
+    Test that calculate_dominant returns one of the most frequent values
+    when there are multiple modes in the data.
+    """
+    data = pd.Series([1, 2, 2, 3, 3, 4])
+    dominant_value = calculate_dominant(data)
+    assert dominant_value in [2.5, 3.5], "The dominant value should be one of the modes"
+
+def test_calculate_dominant_bins_parameter():
+    """
+    Test that calculate_dominant respects the 'bins' parameter for histogram granularity.
+    """
+    data = pd.Series([1, 1, 2, 2, 3, 3, 4, 4, 5, 5])
+    # With fewer bins, the dominant should approximate the most frequent bin
+    assert calculate_dominant(data, bins=3) in [1.5, 4.5], "The dominant value should approximate the largest bin center"
+
+def test_calculate_dominant_negative_values():
+    """
+    Test that calculate_dominant correctly identifies the dominant value for a series with negative values.
+    """
+    data = pd.Series([-5, -4, -4, -3, -3, -3, -2, -1])
+    assert calculate_dominant(data) == -3.0, "The dominant value should be -3.0"
\ No newline at end of file
diff --git a/tests/core/features/test_trend_strength.py b/tests/core/features/test_trend_strength.py
new file mode 100644
index 0000000..1e655a2
--- /dev/null
+++ b/tests/core/features/test_trend_strength.py
@@ -0,0 +1,54 @@
+import pytest
+import pandas as pd
+import numpy as np
+from src.core.features.trend_strength import calculate_trend_strength
+
+def test_calculate_trend_strength_increasing():
+    """
+    Test that calculate_trend_strength identifies a strong positive trend.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert calculate_trend_strength(data) == pytest.approx(1.0), "The trend strength should be close to 1.0 for a perfect increasing trend"
+
+def test_calculate_trend_strength_decreasing():
+    """
+    Test that calculate_trend_strength identifies a strong negative trend.
+    """
+    data = pd.Series([5, 4, 3, 2, 1])
+    assert calculate_trend_strength(data) == pytest.approx(1.0), "The trend strength should be close to 1.0 for a perfect decreasing trend"
+
+def test_calculate_trend_strength_no_trend():
+    """
+    Test that calculate_trend_strength returns a low value for a series with no trend.
+    """
+    data = pd.Series([1, 1, 1, 1, 1])
+    assert calculate_trend_strength(data) == pytest.approx(0.0), "The trend strength should be 0.0 for no trend"
+
+def test_calculate_trend_strength_random_data():
+    """
+    Test that calculate_trend_strength returns a low value for random data.
+    """
+    np.random.seed(0)
+    data = pd.Series(np.random.randn(100))
+    assert calculate_trend_strength(data) < 0.2, "The trend strength should be low for random data"
+
+def test_calculate_trend_strength_empty_series():
+    """
+    Test that calculate_trend_strength returns NaN for an empty series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_trend_strength(data)), "The trend strength of an empty series should be NaN"
+
+def test_calculate_trend_strength_insufficient_data():
+    """
+    Test that calculate_trend_strength returns NaN for a single data point.
+    """
+    data = pd.Series([5])
+    assert pd.isna(calculate_trend_strength(data)), "The trend strength should be NaN for insufficient data"
+
+def test_calculate_trend_strength_numpy_array():
+    """
+    Test that calculate_trend_strength works with a numpy array.
+    """
+    data = np.array([1, 2, 3, 4, 5])
+    assert calculate_trend_strength(data) == pytest.approx(1.0), "The trend strength should be close to 1.0 for a perfect trend"
\ No newline at end of file