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,60 @@
1
+ """Research oriented plotting utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Collection, Sequence
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+ import plotly.express as px
10
+ import seaborn as sns
11
+
12
+
13
+ def _require_columns(
14
+ data: pd.DataFrame, required: Collection[str], context: str
15
+ ) -> None:
16
+ """Raise a clear error when required plotting columns are missing."""
17
+ missing = sorted(set(required) - set(data.columns))
18
+ if missing:
19
+ names = ", ".join(missing)
20
+ raise ValueError(f"{context} requires columns: {names}")
21
+
22
+
23
+ def _finite_residuals(residuals: Sequence[float]) -> list[float]:
24
+ """Return residuals as finite floats for diagnostic plotting."""
25
+ values = np.asarray(list(residuals), dtype=float)
26
+ if values.size == 0:
27
+ raise ValueError("residuals must contain at least one value")
28
+ if not np.isfinite(values).all():
29
+ raise ValueError("residuals must contain only finite values")
30
+ return values.tolist()
31
+
32
+
33
+ def publication_figure(data: pd.DataFrame, x: str, y: str, hue: str | None = None):
34
+ """Generate a publication quality figure using seaborn."""
35
+ required = {x, y}
36
+ if hue is not None:
37
+ required.add(hue)
38
+ _require_columns(data, required, "publication_figure")
39
+ ax = sns.lineplot(data=data, x=x, y=y, hue=hue)
40
+ ax.set_title("Publication Quality Figure")
41
+ return ax.get_figure()
42
+
43
+
44
+ def model_diagnostics(residuals: Sequence[float]):
45
+ """Return a Plotly histogram of model residuals."""
46
+ return px.histogram(
47
+ x=_finite_residuals(residuals), nbins=30, title="Model Residuals"
48
+ )
49
+
50
+
51
+ def performance_comparison(scores: pd.DataFrame) -> px.bar:
52
+ """Bar chart comparing performance metrics across models."""
53
+ _require_columns(scores, {"metric", "model", "score"}, "performance_comparison")
54
+ return px.bar(scores, x="model", y="score", color="metric")
55
+
56
+
57
+ def statistical_tests(results: pd.DataFrame) -> px.scatter:
58
+ """Scatter plot visualizing statistical test outcomes."""
59
+ _require_columns(results, {"model", "pvalue", "test"}, "statistical_tests")
60
+ return px.scatter(results, x="test", y="pvalue", color="model")
@@ -0,0 +1,137 @@
1
+ """Lightweight Flask web interface for running analyses."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import tempfile
8
+ from functools import wraps
9
+ from pathlib import Path
10
+ from typing import Callable
11
+
12
+ from flask import (
13
+ Flask,
14
+ Response,
15
+ abort,
16
+ current_app,
17
+ render_template_string,
18
+ request,
19
+ send_file,
20
+ )
21
+ from werkzeug.datastructures import FileStorage
22
+ from werkzeug.utils import secure_filename
23
+
24
+ UPLOAD_DIR = Path(tempfile.gettempdir()) / "sensor-modeling-uploads"
25
+
26
+
27
+ def _configured_upload_dir() -> Path:
28
+ """Return the active upload directory, creating it if needed."""
29
+ upload_dir = Path(current_app.config["UPLOAD_DIR"]).expanduser().resolve()
30
+ upload_dir.mkdir(parents=True, exist_ok=True)
31
+ return upload_dir
32
+
33
+
34
+ def _check_auth(username: str, password: str) -> bool:
35
+ """Validate provided credentials against environment variables."""
36
+ expected_user = os.environ.get("SM_USER")
37
+ expected_pass = os.environ.get("SM_PASS")
38
+ if expected_user is None or expected_pass is None:
39
+ return False
40
+ return username == expected_user and password == expected_pass
41
+
42
+
43
+ def _auth_failed() -> Response: # pragma: no cover - simple response
44
+ """Return a 401 response requesting basic authentication."""
45
+ return Response(
46
+ "Authentication required",
47
+ 401,
48
+ {"WWW-Authenticate": 'Basic realm="Login"'},
49
+ )
50
+
51
+
52
+ def _uploaded_data_file() -> FileStorage:
53
+ """Return the uploaded data file or abort with a clear client error."""
54
+ if "data" not in request.files:
55
+ abort(400, "missing data file")
56
+ file = request.files["data"]
57
+ if not file.filename:
58
+ abort(400, "invalid filename")
59
+ return file
60
+
61
+
62
+ def requires_auth(f: Callable) -> Callable:
63
+ """Enforce basic authentication on endpoints."""
64
+
65
+ @wraps(f)
66
+ def decorated(*args, **kwargs):
67
+ auth = request.authorization
68
+ if not auth or not _check_auth(auth.username, auth.password):
69
+ return _auth_failed()
70
+ return f(*args, **kwargs)
71
+
72
+ return decorated
73
+
74
+
75
+ app = Flask(__name__)
76
+ app.config["MAX_CONTENT_LENGTH"] = 2 * 1024 * 1024 # 2MB uploads
77
+
78
+
79
+ @app.route("/", methods=["GET"])
80
+ @requires_auth
81
+ def index(): # pragma: no cover - simple template
82
+ """Render a minimal upload form."""
83
+ return render_template_string(
84
+ "<h1>Sensor Modeling</h1>"
85
+ "<form action='{{ url_for(\"run\") }}' method='post' "
86
+ "enctype='multipart/form-data'>"
87
+ "<input type='file' name='data'>"
88
+ "<input type='text' name='param'>"
89
+ "<input type='submit'>"
90
+ "</form>"
91
+ )
92
+
93
+
94
+ @app.route("/run", methods=["POST"])
95
+ @requires_auth
96
+ def run():
97
+ """Persist uploaded data and return a placeholder result."""
98
+ file = _uploaded_data_file()
99
+ param = request.form.get("param", "")
100
+ filename = secure_filename(file.filename)
101
+ if not filename:
102
+ abort(400, "invalid filename")
103
+ upload_dir = _configured_upload_dir()
104
+ path = (upload_dir / filename).resolve()
105
+ if path.parent != upload_dir: # path traversal guard
106
+ abort(400, "invalid path")
107
+ file.save(path)
108
+ # Placeholder analysis step
109
+ result_path = path.with_suffix(".txt")
110
+ with open(result_path, "w", encoding="utf-8") as fh:
111
+ fh.write(f"processed with param={param}\n")
112
+ return send_file(result_path, as_attachment=True)
113
+
114
+
115
+ def create_app(upload_dir: str | os.PathLike[str] | None = None) -> Flask:
116
+ """Create the Flask application."""
117
+ configured_dir = upload_dir or os.environ.get("SM_UPLOAD_DIR") or UPLOAD_DIR
118
+ app.config["UPLOAD_DIR"] = str(configured_dir)
119
+ return app
120
+
121
+
122
+ def main() -> None:
123
+ """Run the Flask development server for the web app."""
124
+ parser = argparse.ArgumentParser(description="Run the sensor visualization app")
125
+ parser.add_argument("--host", default="127.0.0.1", help="Host interface to bind")
126
+ parser.add_argument("--port", type=int, default=5000, help="Port to bind")
127
+ parser.add_argument(
128
+ "--debug",
129
+ action="store_true",
130
+ help="Run the Flask development server in debug mode",
131
+ )
132
+ args = parser.parse_args()
133
+ create_app().run(host=args.host, port=args.port, debug=args.debug)
134
+
135
+
136
+ if __name__ == "__main__":
137
+ main()