sensor-modeling 0.2.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.
Files changed (114) hide show
  1. sensor_modeling/__init__.py +45 -0
  2. sensor_modeling/alerts/__init__.py +26 -0
  3. sensor_modeling/alerts/alert.py +532 -0
  4. sensor_modeling/analysis/__init__.py +43 -0
  5. sensor_modeling/analysis/_frame.py +19 -0
  6. sensor_modeling/analysis/behavioral_analysis.py +57 -0
  7. sensor_modeling/analysis/behavioral_metrics.py +66 -0
  8. sensor_modeling/analysis/comparison.py +164 -0
  9. sensor_modeling/analysis/dependency_network.py +408 -0
  10. sensor_modeling/analysis/granger_causality.py +314 -0
  11. sensor_modeling/analysis/pipeline.py +168 -0
  12. sensor_modeling/analysis/reporting.py +109 -0
  13. sensor_modeling/baseline/__init__.py +30 -0
  14. sensor_modeling/baseline/adaptive.py +520 -0
  15. sensor_modeling/baseline/features.py +224 -0
  16. sensor_modeling/change_point/__init__.py +13 -0
  17. sensor_modeling/change_point/_validation.py +31 -0
  18. sensor_modeling/change_point/adaptive_normalization.py +55 -0
  19. sensor_modeling/change_point/embedding_cpd.py +60 -0
  20. sensor_modeling/change_point/energy_efficient.py +57 -0
  21. sensor_modeling/change_point/genetic_optimization.py +65 -0
  22. sensor_modeling/cli.py +416 -0
  23. sensor_modeling/context/__init__.py +33 -0
  24. sensor_modeling/context/occupancy.py +529 -0
  25. sensor_modeling/data/__init__.py +5 -0
  26. sensor_modeling/data/loaders.py +146 -0
  27. sensor_modeling/data/preprocessing.py +83 -0
  28. sensor_modeling/data/synthetic.py +121 -0
  29. sensor_modeling/data/validation.py +81 -0
  30. sensor_modeling/evaluation/__init__.py +92 -0
  31. sensor_modeling/evaluation/ablation.py +303 -0
  32. sensor_modeling/evaluation/attribution.py +474 -0
  33. sensor_modeling/evaluation/detection.py +297 -0
  34. sensor_modeling/evaluation/metrics.py +541 -0
  35. sensor_modeling/evaluation/provenance.py +309 -0
  36. sensor_modeling/examples/__init__.py +1 -0
  37. sensor_modeling/examples/demos/__init__.py +1 -0
  38. sensor_modeling/examples/demos/ambient_pipeline_demo.py +418 -0
  39. sensor_modeling/examples/demos/bernoulli_ar_demo.py +356 -0
  40. sensor_modeling/examples/demos/cpd_ar_demo.py +25 -0
  41. sensor_modeling/examples/demos/cpd_benchmark.py +42 -0
  42. sensor_modeling/examples/demos/hmm_granger_demo.py +30 -0
  43. sensor_modeling/examples/demos/nhpp_pelt_demo.py +80 -0
  44. sensor_modeling/examples/tutorials/__init__.py +1 -0
  45. sensor_modeling/fusion/__init__.py +46 -0
  46. sensor_modeling/fusion/defaults.py +296 -0
  47. sensor_modeling/fusion/emissions.py +339 -0
  48. sensor_modeling/fusion/estimate.py +375 -0
  49. sensor_modeling/fusion/filter.py +323 -0
  50. sensor_modeling/health/__init__.py +31 -0
  51. sensor_modeling/health/monitor.py +590 -0
  52. sensor_modeling/health/status.py +74 -0
  53. sensor_modeling/hmm/__init__.py +15 -0
  54. sensor_modeling/hmm/adaptive_hmm.py +22 -0
  55. sensor_modeling/hmm/base.py +134 -0
  56. sensor_modeling/hmm/circadian_hmm.py +22 -0
  57. sensor_modeling/hmm/heterogeneous_hmm.py +22 -0
  58. sensor_modeling/hmm/hierarchical_hmm.py +35 -0
  59. sensor_modeling/hmm/scaled_dirichlet_hmm.py +23 -0
  60. sensor_modeling/interop/__init__.py +57 -0
  61. sensor_modeling/interop/fhir.py +418 -0
  62. sensor_modeling/interop/privacy.py +308 -0
  63. sensor_modeling/models/__init__.py +12 -0
  64. sensor_modeling/models/bernoulli_ar/__init__.py +6 -0
  65. sensor_modeling/models/bernoulli_ar/base_model.py +569 -0
  66. sensor_modeling/models/bernoulli_ar/multivariate_model.py +411 -0
  67. sensor_modeling/models/change_point_detection/__init__.py +10 -0
  68. sensor_modeling/models/change_point_detection/deep.py +65 -0
  69. sensor_modeling/models/change_point_detection/pelt.py +159 -0
  70. sensor_modeling/models/nhpp_pelt/__init__.py +5 -0
  71. sensor_modeling/models/nhpp_pelt/bspline.py +96 -0
  72. sensor_modeling/models/nhpp_pelt/cli.py +243 -0
  73. sensor_modeling/models/nhpp_pelt/diagnostics.py +234 -0
  74. sensor_modeling/models/nhpp_pelt/io.py +58 -0
  75. sensor_modeling/models/nhpp_pelt/model.py +408 -0
  76. sensor_modeling/models/nhpp_pelt/optimizer.py +142 -0
  77. sensor_modeling/models/nhpp_pelt/plotting.py +218 -0
  78. sensor_modeling/models/nhpp_pelt/quad.py +72 -0
  79. sensor_modeling/models/nhpp_pelt/regularization.py +121 -0
  80. sensor_modeling/models/nhpp_pelt/utils.py +174 -0
  81. sensor_modeling/observations/__init__.py +59 -0
  82. sensor_modeling/observations/adapters.py +195 -0
  83. sensor_modeling/observations/ingest.py +269 -0
  84. sensor_modeling/observations/observation.py +270 -0
  85. sensor_modeling/observations/registry.py +262 -0
  86. sensor_modeling/observations/stream.py +342 -0
  87. sensor_modeling/observations/types.py +107 -0
  88. sensor_modeling/observations/units.py +117 -0
  89. sensor_modeling/online/__init__.py +36 -0
  90. sensor_modeling/online/benchmarks.py +242 -0
  91. sensor_modeling/online/pipeline.py +485 -0
  92. sensor_modeling/simulation/__init__.py +54 -0
  93. sensor_modeling/simulation/faults.py +191 -0
  94. sensor_modeling/simulation/household.py +862 -0
  95. sensor_modeling/states/__init__.py +23 -0
  96. sensor_modeling/states/markov.py +105 -0
  97. sensor_modeling/states/ontology.py +238 -0
  98. sensor_modeling/utils/__init__.py +41 -0
  99. sensor_modeling/utils/data_io.py +199 -0
  100. sensor_modeling/utils/logging_config.py +10 -0
  101. sensor_modeling/utils/missing.py +188 -0
  102. sensor_modeling/utils/plotting.py +98 -0
  103. sensor_modeling/utils/validation.py +117 -0
  104. sensor_modeling/visualization/__init__.py +3 -0
  105. sensor_modeling/visualization/clinical.py +67 -0
  106. sensor_modeling/visualization/interactive.py +208 -0
  107. sensor_modeling/visualization/research.py +60 -0
  108. sensor_modeling/visualization/web_app.py +137 -0
  109. sensor_modeling-0.2.0.dist-info/METADATA +683 -0
  110. sensor_modeling-0.2.0.dist-info/RECORD +114 -0
  111. sensor_modeling-0.2.0.dist-info/WHEEL +5 -0
  112. sensor_modeling-0.2.0.dist-info/entry_points.txt +18 -0
  113. sensor_modeling-0.2.0.dist-info/licenses/LICENSE +21 -0
  114. sensor_modeling-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,15 @@
1
+ """Hidden Markov models for sensor behavior analysis."""
2
+
3
+ from .adaptive_hmm import AdaptiveHMM
4
+ from .circadian_hmm import CircadianHMM
5
+ from .heterogeneous_hmm import HeterogeneousHMM
6
+ from .hierarchical_hmm import HierarchicalHMM
7
+ from .scaled_dirichlet_hmm import ScaledDirichletHMM
8
+
9
+ __all__ = [
10
+ "HierarchicalHMM",
11
+ "ScaledDirichletHMM",
12
+ "HeterogeneousHMM",
13
+ "AdaptiveHMM",
14
+ "CircadianHMM",
15
+ ]
@@ -0,0 +1,22 @@
1
+ """Adaptive HMM incorporating personal experience.
2
+
3
+ This model adapts transition dynamics based on user-specific experience, allowing
4
+ personalization of state persistence. The implementation follows the BaseHMM
5
+ interface while exposing an ``adaptation_rate`` parameter.
6
+ """
7
+
8
+ from .base import BaseHMM
9
+
10
+
11
+ class AdaptiveHMM(BaseHMM):
12
+ """Adaptive Hidden Markov Model with experience-based updates."""
13
+
14
+ def __init__(
15
+ self,
16
+ n_states: int = 3,
17
+ adaptation_rate: float = 0.5,
18
+ n_iter: int = 10,
19
+ random_state: int | None = None,
20
+ ):
21
+ super().__init__(n_states=n_states, n_iter=n_iter, random_state=random_state)
22
+ self.adaptation_rate = adaptation_rate
@@ -0,0 +1,134 @@
1
+ import logging
2
+
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ from sklearn.base import BaseEstimator
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class BaseHMM(BaseEstimator):
11
+ """Simple Hidden Markov Model base class.
12
+
13
+ Provides a lightweight implementation that supports both batch and online
14
+ learning through :meth:`fit` and :meth:`partial_fit`. Missing data are
15
+ imputed using column means. Visualization helpers are provided for state
16
+ transition and emission statistics, and model selection utilities compute
17
+ AIC and BIC metrics.
18
+ """
19
+
20
+ def __init__(
21
+ self, n_states: int = 2, n_iter: int = 10, random_state: int | None = None
22
+ ):
23
+ self.n_states = n_states
24
+ self.n_iter = n_iter
25
+ self.random_state = random_state
26
+ self.trans_mat_ = None
27
+ self.means_ = None
28
+ self.fitted_ = False
29
+
30
+ # ------------------------------------------------------------------
31
+ def _handle_missing(self, X: np.ndarray) -> np.ndarray:
32
+ """Impute missing values with column means."""
33
+ X = np.asarray(X, dtype=float)
34
+ if np.isnan(X).any():
35
+ col_means = np.nanmean(X, axis=0)
36
+ inds = np.where(np.isnan(X))
37
+ X[inds] = np.take(col_means, inds[1])
38
+ return X
39
+
40
+ # ------------------------------------------------------------------
41
+ def fit(self, X: np.ndarray, y=None):
42
+ """Batch learning using a k-means style EM procedure."""
43
+ X = self._handle_missing(X)
44
+ rng = np.random.RandomState(self.random_state)
45
+ means = X[rng.choice(len(X), self.n_states, replace=False)]
46
+
47
+ for _ in range(self.n_iter):
48
+ dists = ((X[:, None, :] - means[None, :, :]) ** 2).sum(axis=2)
49
+ states = dists.argmin(axis=1)
50
+ for k in range(self.n_states):
51
+ if np.any(states == k):
52
+ means[k] = X[states == k].mean(axis=0)
53
+
54
+ self.means_ = means
55
+ trans = np.zeros((self.n_states, self.n_states))
56
+ for i, j in zip(states[:-1], states[1:]):
57
+ trans[i, j] += 1
58
+ trans = trans / np.maximum(trans.sum(axis=1, keepdims=True), 1)
59
+ self.trans_mat_ = trans
60
+ self.fitted_ = True
61
+ return self
62
+
63
+ # ------------------------------------------------------------------
64
+ def partial_fit(self, X: np.ndarray, y=None):
65
+ """Online update that refines parameters with new data."""
66
+ if not self.fitted_:
67
+ return self.fit(X, y)
68
+ X = self._handle_missing(X)
69
+ dists = ((X[:, None, :] - self.means_[None, :, :]) ** 2).sum(axis=2)
70
+ states = dists.argmin(axis=1)
71
+ for k in range(self.n_states):
72
+ if np.any(states == k):
73
+ self.means_[k] = (self.means_[k] + X[states == k].mean(axis=0)) / 2
74
+ for i, j in zip(states[:-1], states[1:]):
75
+ self.trans_mat_[i, j] += 1
76
+ self.trans_mat_ = self.trans_mat_ / np.maximum(
77
+ self.trans_mat_.sum(axis=1, keepdims=True), 1
78
+ )
79
+ return self
80
+
81
+ # ------------------------------------------------------------------
82
+ def predict(self, X: np.ndarray) -> np.ndarray:
83
+ """Predict most likely state sequence."""
84
+ if not self.fitted_:
85
+ raise ValueError("Model must be fitted before prediction.")
86
+ X = self._handle_missing(X)
87
+ dists = ((X[:, None, :] - self.means_[None, :, :]) ** 2).sum(axis=2)
88
+ return dists.argmin(axis=1)
89
+
90
+ # ------------------------------------------------------------------
91
+ def score(self, X: np.ndarray, y=None) -> float:
92
+ """Pseudo log-likelihood based on squared distances to state means."""
93
+ if not self.fitted_:
94
+ raise ValueError("Model must be fitted before scoring.")
95
+ X = self._handle_missing(X)
96
+ dists = ((X[:, None, :] - self.means_[None, :, :]) ** 2).sum(axis=2)
97
+ return -dists.min(axis=1).sum()
98
+
99
+ # ------------------------------------------------------------------
100
+ def compute_aic(self, X: np.ndarray) -> float:
101
+ """Calculate Akaike Information Criterion."""
102
+ ll = self.score(X)
103
+ n_params = self.n_states * X.shape[1] + self.n_states * (self.n_states - 1)
104
+ return 2 * n_params - 2 * ll
105
+
106
+ # ------------------------------------------------------------------
107
+ def compute_bic(self, X: np.ndarray) -> float:
108
+ """Calculate Bayesian Information Criterion."""
109
+ ll = self.score(X)
110
+ n_params = self.n_states * X.shape[1] + self.n_states * (self.n_states - 1)
111
+ n = len(X)
112
+ return n_params * np.log(n) - 2 * ll
113
+
114
+ # ------------------------------------------------------------------
115
+ def plot_state_transitions(self, ax=None):
116
+ """Visualize the state transition matrix."""
117
+ if ax is None:
118
+ _, ax = plt.subplots()
119
+ im = ax.imshow(self.trans_mat_, cmap="Blues")
120
+ ax.set_title("State Transition Matrix")
121
+ ax.set_xlabel("To state")
122
+ ax.set_ylabel("From state")
123
+ plt.colorbar(im, ax=ax)
124
+ return ax
125
+
126
+ # ------------------------------------------------------------------
127
+ def plot_emissions(self, ax=None):
128
+ """Visualize state emission means."""
129
+ if ax is None:
130
+ _, ax = plt.subplots()
131
+ ax.plot(self.means_)
132
+ ax.set_title("State Emission Means")
133
+ ax.set_xlabel("State")
134
+ return ax
@@ -0,0 +1,22 @@
1
+ """Circadian HMM for rhythm monitoring applications.
2
+
3
+ Captures periodic behavioral patterns by biasing transitions towards circadian
4
+ cycles. The model exposes a ``period`` parameter defining the expected rhythm in
5
+ samples.
6
+ """
7
+
8
+ from .base import BaseHMM
9
+
10
+
11
+ class CircadianHMM(BaseHMM):
12
+ """Hidden Markov Model with circadian regularization."""
13
+
14
+ def __init__(
15
+ self,
16
+ n_states: int = 3,
17
+ period: int = 24,
18
+ n_iter: int = 10,
19
+ random_state: int | None = None,
20
+ ):
21
+ super().__init__(n_states=n_states, n_iter=n_iter, random_state=random_state)
22
+ self.period = period
@@ -0,0 +1,22 @@
1
+ """Heterogeneous HMM for multi-source data integration.
2
+
3
+ Moreno-Pino et al. (2022) proposed integrating heterogeneous sensor streams in a
4
+ single hidden Markov model. Here we provide a minimal interface-compatible
5
+ version that accepts concatenated feature representations from multiple sources.
6
+ """
7
+
8
+ from .base import BaseHMM
9
+
10
+
11
+ class HeterogeneousHMM(BaseHMM):
12
+ """HMM that integrates multiple sensor modalities."""
13
+
14
+ def __init__(
15
+ self,
16
+ n_states: int = 3,
17
+ source_weights: list[float] | None = None,
18
+ n_iter: int = 10,
19
+ random_state: int | None = None,
20
+ ):
21
+ super().__init__(n_states=n_states, n_iter=n_iter, random_state=random_state)
22
+ self.source_weights = source_weights
@@ -0,0 +1,35 @@
1
+ """Hierarchical HMM for multi-level activity modeling.
2
+
3
+ Implements a basic hierarchical hidden Markov model following the ideas of
4
+ Asghari & Nazerfard (2019). The hierarchy is represented by grouping states
5
+ into higher-level clusters while sharing the learning and inference routines
6
+ from :class:`~sensor_modeling.hmm.base.BaseHMM`.
7
+ """
8
+
9
+ from .base import BaseHMM
10
+
11
+
12
+ class HierarchicalHMM(BaseHMM):
13
+ """Hierarchical Hidden Markov Model.
14
+
15
+ Parameters
16
+ ----------
17
+ n_states : int
18
+ Number of latent states.
19
+ levels : int, optional
20
+ Number of hierarchy levels.
21
+ n_iter : int, optional
22
+ Number of EM iterations.
23
+ random_state : int, optional
24
+ Random seed for initialization.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ n_states: int = 3,
30
+ levels: int = 2,
31
+ n_iter: int = 10,
32
+ random_state: int | None = None,
33
+ ):
34
+ super().__init__(n_states=n_states, n_iter=n_iter, random_state=random_state)
35
+ self.levels = levels
@@ -0,0 +1,23 @@
1
+ """Scaled Dirichlet HMM with variational inference.
2
+
3
+ Implements the variational inference approach of Manouchehri & Bouguila (2023)
4
+ for scaled Dirichlet mixture emissions. This simplified version focuses on the
5
+ interface compatibility and leverages :class:`~sensor_modeling.hmm.base.BaseHMM`
6
+ for core learning routines.
7
+ """
8
+
9
+ from .base import BaseHMM
10
+
11
+
12
+ class ScaledDirichletHMM(BaseHMM):
13
+ """HMM with scaled Dirichlet emissions."""
14
+
15
+ def __init__(
16
+ self,
17
+ n_states: int = 3,
18
+ concentration: float = 1.0,
19
+ n_iter: int = 10,
20
+ random_state: int | None = None,
21
+ ):
22
+ super().__init__(n_states=n_states, n_iter=n_iter, random_state=random_state)
23
+ self.concentration = concentration
@@ -0,0 +1,57 @@
1
+ """Interoperability exports that keep measurement and inference distinct.
2
+
3
+ Once a behavioural conclusion is written into a clinical record it looks like
4
+ every other entry there. This package exports measurements, derived features,
5
+ inferred states and algorithmic alerts as distinguishable resources carrying
6
+ explicit provenance, so a downstream reader can always tell which is which.
7
+
8
+ The exports are FHIR-*style* prototypes for interoperability research. They
9
+ are not validated FHIR profiles and their codes are not drawn from any
10
+ recognised terminology.
11
+ """
12
+
13
+ from .fhir import (
14
+ CODE_SYSTEM,
15
+ PROVENANCE_DERIVED_FEATURE,
16
+ PROVENANCE_INFERRED,
17
+ PROVENANCE_MEASURED,
18
+ RESEARCH_NOTE,
19
+ alert_resource,
20
+ bundle,
21
+ measured_only,
22
+ observation_resource,
23
+ state_resource,
24
+ summarise_provenance,
25
+ )
26
+ from .privacy import (
27
+ DEFAULT_REDACTED_KEYS,
28
+ Pseudonymiser,
29
+ RedactionPolicy,
30
+ SaltError,
31
+ identifiers_in,
32
+ redact,
33
+ redact_bundle,
34
+ research_identifier,
35
+ )
36
+
37
+ __all__ = [
38
+ "CODE_SYSTEM",
39
+ "DEFAULT_REDACTED_KEYS",
40
+ "PROVENANCE_DERIVED_FEATURE",
41
+ "PROVENANCE_INFERRED",
42
+ "PROVENANCE_MEASURED",
43
+ "RESEARCH_NOTE",
44
+ "Pseudonymiser",
45
+ "RedactionPolicy",
46
+ "SaltError",
47
+ "alert_resource",
48
+ "bundle",
49
+ "identifiers_in",
50
+ "measured_only",
51
+ "observation_resource",
52
+ "redact",
53
+ "redact_bundle",
54
+ "research_identifier",
55
+ "state_resource",
56
+ "summarise_provenance",
57
+ ]