tsqn-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tsqn/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """tsqn: Robust time-series estimators based on the Qn scale."""
2
+
3
+ from .gph import GPHResult, GPH_estimate, gph_estimate
4
+ from .periodogram import PerQn, PerioMrob, per_qn, perio_mrob
5
+ from .robacf import RobACFResult, plot_robacf, robacf
6
+ from .robust_cov import (
7
+ corMatQn,
8
+ corQn,
9
+ cor_mat_qn,
10
+ cor_qn,
11
+ covMatQn,
12
+ covQn,
13
+ cov_mat_qn,
14
+ cov_qn,
15
+ )
16
+
17
+ __all__ = [
18
+ "GPHResult",
19
+ "GPH_estimate",
20
+ "gph_estimate",
21
+ "PerQn",
22
+ "PerioMrob",
23
+ "per_qn",
24
+ "perio_mrob",
25
+ "RobACFResult",
26
+ "plot_robacf",
27
+ "robacf",
28
+ "corMatQn",
29
+ "corQn",
30
+ "cor_mat_qn",
31
+ "cor_qn",
32
+ "covMatQn",
33
+ "covQn",
34
+ "cov_mat_qn",
35
+ "cov_qn",
36
+ ]
37
+
tsqn/_qn.py ADDED
@@ -0,0 +1,19 @@
1
+ """Qn robust scale wrapper utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+ from statsmodels.robust.scale import qn_scale as _sm_qn_scale
7
+
8
+
9
+ def qn_scale(x: np.ndarray | list[float]) -> float:
10
+ """Compute the robust Qn scale for a one-dimensional sample."""
11
+ arr = np.asarray(x, dtype=float)
12
+ if arr.ndim != 1:
13
+ raise ValueError("Input must be one-dimensional.")
14
+ if arr.size < 2:
15
+ raise ValueError("At least two observations are required.")
16
+ if np.isnan(arr).any():
17
+ raise ValueError("Input contains NaN values.")
18
+ # Delegate to statsmodels to match the robustbase-style Qn constant.
19
+ return float(_sm_qn_scale(arr))
tsqn/_validation.py ADDED
@@ -0,0 +1,62 @@
1
+ """Shared validation helpers used across the tsqn package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+
8
+ def as_1d_float_array(
9
+ values: np.ndarray | list[float],
10
+ *,
11
+ name: str,
12
+ min_size: int = 1,
13
+ allow_nan: bool = False,
14
+ ) -> np.ndarray:
15
+ """Convert input to a validated 1D float NumPy array.
16
+
17
+ Args:
18
+ values: Input values expected to represent one numeric series.
19
+ name: Argument name used in error messages.
20
+ min_size: Minimum required number of elements.
21
+ allow_nan: Whether NaN values are allowed.
22
+
23
+ Returns:
24
+ A one-dimensional float array.
25
+ """
26
+ arr = np.asarray(values, dtype=float)
27
+ if arr.ndim != 1:
28
+ raise ValueError(f"{name} must be one-dimensional.")
29
+ if arr.size < min_size:
30
+ raise ValueError(f"{name} must have at least {min_size} observations.")
31
+ if not allow_nan and np.isnan(arr).any():
32
+ raise ValueError(f"{name} contains NaN values.")
33
+ return arr
34
+
35
+
36
+ def as_2d_float_array(
37
+ values: np.ndarray | list[list[float]],
38
+ *,
39
+ name: str,
40
+ min_rows: int = 1,
41
+ allow_nan: bool = False,
42
+ ) -> np.ndarray:
43
+ """Convert input to a validated 2D float NumPy array.
44
+
45
+ Args:
46
+ values: Input values expected to represent a matrix.
47
+ name: Argument name used in error messages.
48
+ min_rows: Minimum required number of rows.
49
+ allow_nan: Whether NaN values are allowed.
50
+
51
+ Returns:
52
+ A two-dimensional float array.
53
+ """
54
+ arr = np.asarray(values, dtype=float)
55
+ if arr.ndim != 2:
56
+ raise ValueError(f"{name} must be a 2D matrix.")
57
+ if arr.shape[0] < min_rows:
58
+ raise ValueError(f"{name} must have at least {min_rows} rows.")
59
+ if not allow_nan and np.isnan(arr).any():
60
+ raise ValueError(f"{name} contains NaN values.")
61
+ return arr
62
+
tsqn/correlation.py ADDED
@@ -0,0 +1,44 @@
1
+ """Robust correlation estimator based on the Qn robust scale."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ from ._qn import qn_scale
8
+ from ._validation import as_1d_float_array
9
+
10
+
11
+ def cor_qn(x: np.ndarray | list[float], y: np.ndarray | list[float]) -> float:
12
+ """Compute the Ma-Genton robust correlation estimator using Qn.
13
+
14
+ Args:
15
+ x: First numeric series.
16
+ y: Second numeric series.
17
+
18
+ Returns:
19
+ Robust correlation estimate between ``x`` and ``y``.
20
+ """
21
+ x_arr = as_1d_float_array(x, name="x", min_size=2)
22
+ y_arr = as_1d_float_array(y, name="y", min_size=2)
23
+ if x_arr.size != y_arr.size:
24
+ raise ValueError("x and y are unequal sizes")
25
+
26
+ # The robust self-correlation of a series is exactly one.
27
+ if np.array_equal(x_arr, y_arr):
28
+ return 1.0
29
+
30
+ alpha_qn = qn_scale(x_arr)
31
+ beta_qn = qn_scale(y_arr)
32
+ if alpha_qn == 0.0 or beta_qn == 0.0:
33
+ raise ValueError("Qn scale is zero for one of the input series.")
34
+
35
+ x_alpha = x_arr / alpha_qn
36
+ y_beta = y_arr / beta_qn
37
+ qn1 = qn_scale(x_alpha + y_beta)
38
+ qn2 = qn_scale(x_alpha - y_beta)
39
+ den = (qn1 * qn1) + (qn2 * qn2)
40
+ if den == 0.0:
41
+ raise ValueError("Unable to compute robust correlation due to zero denominator.")
42
+ cor_qn_val = ((qn1 * qn1) - (qn2 * qn2)) / den
43
+ return float(cor_qn_val)
44
+
tsqn/covariance.py ADDED
@@ -0,0 +1,42 @@
1
+ """Robust covariance estimator based on the Qn robust scale."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ from ._qn import qn_scale
8
+ from ._validation import as_1d_float_array
9
+
10
+
11
+ def cov_qn(x: np.ndarray | list[float], y: np.ndarray | list[float]) -> float:
12
+ """Compute the Ma-Genton robust covariance estimator using Qn.
13
+
14
+ Args:
15
+ x: First numeric series.
16
+ y: Second numeric series.
17
+
18
+ Returns:
19
+ Robust covariance estimate between ``x`` and ``y``.
20
+ """
21
+ x_arr = as_1d_float_array(x, name="x", min_size=2)
22
+ y_arr = as_1d_float_array(y, name="y", min_size=2)
23
+ if x_arr.size != y_arr.size:
24
+ raise ValueError("x and y are unequal sizes")
25
+
26
+ alpha_qn = qn_scale(x_arr)
27
+ beta_qn = qn_scale(y_arr)
28
+
29
+ # The robust variance is the squared robust scale when both series are identical.
30
+ if np.array_equal(x_arr, y_arr):
31
+ return float(alpha_qn * alpha_qn)
32
+
33
+ if alpha_qn == 0.0 or beta_qn == 0.0:
34
+ raise ValueError("Qn scale is zero for one of the input series.")
35
+
36
+ x_alpha = x_arr / alpha_qn
37
+ y_beta = y_arr / beta_qn
38
+ qn1 = qn_scale(x_alpha + y_beta)
39
+ qn2 = qn_scale(x_alpha - y_beta)
40
+ cov_qn_val = ((alpha_qn * beta_qn) / 4.0) * ((qn1 * qn1) - (qn2 * qn2))
41
+ return float(cov_qn_val)
42
+