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.
- sensor_modeling/__init__.py +45 -0
- sensor_modeling/alerts/__init__.py +26 -0
- sensor_modeling/alerts/alert.py +532 -0
- sensor_modeling/analysis/__init__.py +43 -0
- sensor_modeling/analysis/_frame.py +19 -0
- sensor_modeling/analysis/behavioral_analysis.py +57 -0
- sensor_modeling/analysis/behavioral_metrics.py +66 -0
- sensor_modeling/analysis/comparison.py +164 -0
- sensor_modeling/analysis/dependency_network.py +408 -0
- sensor_modeling/analysis/granger_causality.py +314 -0
- sensor_modeling/analysis/pipeline.py +168 -0
- sensor_modeling/analysis/reporting.py +109 -0
- sensor_modeling/baseline/__init__.py +30 -0
- sensor_modeling/baseline/adaptive.py +520 -0
- sensor_modeling/baseline/features.py +224 -0
- sensor_modeling/change_point/__init__.py +13 -0
- sensor_modeling/change_point/_validation.py +31 -0
- sensor_modeling/change_point/adaptive_normalization.py +55 -0
- sensor_modeling/change_point/embedding_cpd.py +60 -0
- sensor_modeling/change_point/energy_efficient.py +57 -0
- sensor_modeling/change_point/genetic_optimization.py +65 -0
- sensor_modeling/cli.py +416 -0
- sensor_modeling/context/__init__.py +33 -0
- sensor_modeling/context/occupancy.py +529 -0
- sensor_modeling/data/__init__.py +5 -0
- sensor_modeling/data/loaders.py +146 -0
- sensor_modeling/data/preprocessing.py +83 -0
- sensor_modeling/data/synthetic.py +121 -0
- sensor_modeling/data/validation.py +81 -0
- sensor_modeling/evaluation/__init__.py +92 -0
- sensor_modeling/evaluation/ablation.py +303 -0
- sensor_modeling/evaluation/attribution.py +474 -0
- sensor_modeling/evaluation/detection.py +297 -0
- sensor_modeling/evaluation/metrics.py +541 -0
- sensor_modeling/evaluation/provenance.py +309 -0
- sensor_modeling/examples/__init__.py +1 -0
- sensor_modeling/examples/demos/__init__.py +1 -0
- sensor_modeling/examples/demos/ambient_pipeline_demo.py +418 -0
- sensor_modeling/examples/demos/bernoulli_ar_demo.py +356 -0
- sensor_modeling/examples/demos/cpd_ar_demo.py +25 -0
- sensor_modeling/examples/demos/cpd_benchmark.py +42 -0
- sensor_modeling/examples/demos/hmm_granger_demo.py +30 -0
- sensor_modeling/examples/demos/nhpp_pelt_demo.py +80 -0
- sensor_modeling/examples/tutorials/__init__.py +1 -0
- sensor_modeling/fusion/__init__.py +46 -0
- sensor_modeling/fusion/defaults.py +296 -0
- sensor_modeling/fusion/emissions.py +339 -0
- sensor_modeling/fusion/estimate.py +375 -0
- sensor_modeling/fusion/filter.py +323 -0
- sensor_modeling/health/__init__.py +31 -0
- sensor_modeling/health/monitor.py +590 -0
- sensor_modeling/health/status.py +74 -0
- sensor_modeling/hmm/__init__.py +15 -0
- sensor_modeling/hmm/adaptive_hmm.py +22 -0
- sensor_modeling/hmm/base.py +134 -0
- sensor_modeling/hmm/circadian_hmm.py +22 -0
- sensor_modeling/hmm/heterogeneous_hmm.py +22 -0
- sensor_modeling/hmm/hierarchical_hmm.py +35 -0
- sensor_modeling/hmm/scaled_dirichlet_hmm.py +23 -0
- sensor_modeling/interop/__init__.py +57 -0
- sensor_modeling/interop/fhir.py +418 -0
- sensor_modeling/interop/privacy.py +308 -0
- sensor_modeling/models/__init__.py +12 -0
- sensor_modeling/models/bernoulli_ar/__init__.py +6 -0
- sensor_modeling/models/bernoulli_ar/base_model.py +569 -0
- sensor_modeling/models/bernoulli_ar/multivariate_model.py +411 -0
- sensor_modeling/models/change_point_detection/__init__.py +10 -0
- sensor_modeling/models/change_point_detection/deep.py +65 -0
- sensor_modeling/models/change_point_detection/pelt.py +159 -0
- sensor_modeling/models/nhpp_pelt/__init__.py +5 -0
- sensor_modeling/models/nhpp_pelt/bspline.py +96 -0
- sensor_modeling/models/nhpp_pelt/cli.py +243 -0
- sensor_modeling/models/nhpp_pelt/diagnostics.py +234 -0
- sensor_modeling/models/nhpp_pelt/io.py +58 -0
- sensor_modeling/models/nhpp_pelt/model.py +408 -0
- sensor_modeling/models/nhpp_pelt/optimizer.py +142 -0
- sensor_modeling/models/nhpp_pelt/plotting.py +218 -0
- sensor_modeling/models/nhpp_pelt/quad.py +72 -0
- sensor_modeling/models/nhpp_pelt/regularization.py +121 -0
- sensor_modeling/models/nhpp_pelt/utils.py +174 -0
- sensor_modeling/observations/__init__.py +59 -0
- sensor_modeling/observations/adapters.py +195 -0
- sensor_modeling/observations/ingest.py +269 -0
- sensor_modeling/observations/observation.py +270 -0
- sensor_modeling/observations/registry.py +262 -0
- sensor_modeling/observations/stream.py +342 -0
- sensor_modeling/observations/types.py +107 -0
- sensor_modeling/observations/units.py +117 -0
- sensor_modeling/online/__init__.py +36 -0
- sensor_modeling/online/benchmarks.py +242 -0
- sensor_modeling/online/pipeline.py +485 -0
- sensor_modeling/simulation/__init__.py +54 -0
- sensor_modeling/simulation/faults.py +191 -0
- sensor_modeling/simulation/household.py +862 -0
- sensor_modeling/states/__init__.py +23 -0
- sensor_modeling/states/markov.py +105 -0
- sensor_modeling/states/ontology.py +238 -0
- sensor_modeling/utils/__init__.py +41 -0
- sensor_modeling/utils/data_io.py +199 -0
- sensor_modeling/utils/logging_config.py +10 -0
- sensor_modeling/utils/missing.py +188 -0
- sensor_modeling/utils/plotting.py +98 -0
- sensor_modeling/utils/validation.py +117 -0
- sensor_modeling/visualization/__init__.py +3 -0
- sensor_modeling/visualization/clinical.py +67 -0
- sensor_modeling/visualization/interactive.py +208 -0
- sensor_modeling/visualization/research.py +60 -0
- sensor_modeling/visualization/web_app.py +137 -0
- sensor_modeling-0.2.0.dist-info/METADATA +683 -0
- sensor_modeling-0.2.0.dist-info/RECORD +114 -0
- sensor_modeling-0.2.0.dist-info/WHEEL +5 -0
- sensor_modeling-0.2.0.dist-info/entry_points.txt +18 -0
- sensor_modeling-0.2.0.dist-info/licenses/LICENSE +21 -0
- 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
|
+
]
|