pmcore/estimation/nonparametric/
predictions.rs1use std::path::Path;
2
3use anyhow::{bail, Result};
4use pharmsol::{prelude::simulator::Prediction, Censor, Data, Predictions as PredTrait};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 estimation::nonparametric::{theta::Theta, weights::Weights},
9 estimation::nonparametric::{weighted_median, Posterior},
10};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct NPPredictionRow {
14 id: String,
15 time: f64,
16 outeq: usize,
17 block: usize,
18 obs: Option<f64>,
19 cens: Censor,
20 pop_mean: f64,
21 pop_median: f64,
22 post_mean: f64,
23 post_median: f64,
24}
25
26impl NPPredictionRow {
27 pub fn id(&self) -> &str {
28 &self.id
29 }
30 pub fn time(&self) -> f64 {
31 self.time
32 }
33 pub fn outeq(&self) -> usize {
34 self.outeq
35 }
36 pub fn block(&self) -> usize {
37 self.block
38 }
39 pub fn obs(&self) -> Option<f64> {
40 self.obs
41 }
42 pub fn pop_mean(&self) -> f64 {
43 self.pop_mean
44 }
45 pub fn pop_median(&self) -> f64 {
46 self.pop_median
47 }
48 pub fn post_mean(&self) -> f64 {
49 self.post_mean
50 }
51 pub fn post_median(&self) -> f64 {
52 self.post_median
53 }
54
55 pub fn censoring(&self) -> Censor {
56 self.cens
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct NPPredictions {
62 predictions: Vec<NPPredictionRow>,
63}
64
65impl IntoIterator for NPPredictions {
66 type Item = NPPredictionRow;
67 type IntoIter = std::vec::IntoIter<NPPredictionRow>;
68
69 fn into_iter(self) -> Self::IntoIter {
70 self.predictions.into_iter()
71 }
72}
73
74impl Default for NPPredictions {
75 fn default() -> Self {
76 NPPredictions::new()
77 }
78}
79
80impl NPPredictions {
81 pub fn new() -> Self {
82 NPPredictions {
83 predictions: Vec::new(),
84 }
85 }
86
87 pub fn add(&mut self, row: NPPredictionRow) {
88 self.predictions.push(row);
89 }
90
91 pub fn predictions(&self) -> &[NPPredictionRow] {
92 &self.predictions
93 }
94
95 pub fn write(&self, path: &Path) -> Result<()> {
101 tracing::debug!("Writing predictions...");
102
103 super::create_parent_dir(path)?;
104
105 let mut writer = csv::WriterBuilder::new()
106 .has_headers(true)
107 .from_path(path)?;
108
109 for row in &self.predictions {
110 writer.serialize(row)?;
111 }
112
113 writer.flush()?;
114 Ok(())
115 }
116
117 pub fn calculate(
118 equation: &impl pharmsol::prelude::simulator::Equation,
119 data: &Data,
120 theta: &Theta,
121 w: &Weights,
122 posterior: &Posterior,
123 idelta: f64,
124 tad: f64,
125 ) -> Result<Self> {
126 let mut container = NPPredictions::new();
127
128 let data = data.clone().expand(idelta, tad);
129 let subjects = data.subjects();
130
131 if subjects.len() != posterior.matrix().nrows() {
132 bail!("Number of subjects and number of posterior means do not match");
133 };
134
135 for (subject_index, subject) in subjects.iter().enumerate() {
136 let mut predictions: Vec<Vec<Prediction>> = Vec::new();
137
138 for spp in theta.matrix().row_iter() {
139 let spp_values = spp.iter().cloned().collect::<Vec<f64>>();
140 let pred = equation
141 .simulate_subject_dense(subject, &spp_values, None)?
142 .0
143 .get_predictions();
144 predictions.push(pred);
145 }
146
147 if predictions.is_empty() {
148 continue;
149 }
150
151 let mut pop_mean: Vec<f64> = vec![0.0; predictions.first().unwrap().len()];
152 for (i, outer_pred) in predictions.iter().enumerate() {
153 for (j, pred) in outer_pred.iter().enumerate() {
154 pop_mean[j] += pred.prediction() * w[i];
155 }
156 }
157
158 let mut pop_median: Vec<f64> = Vec::new();
159 for j in 0..predictions.first().unwrap().len() {
160 let mut values: Vec<f64> = Vec::new();
161 let mut weights: Vec<f64> = Vec::new();
162
163 for (i, outer_pred) in predictions.iter().enumerate() {
164 values.push(outer_pred[j].prediction());
165 weights.push(w[i]);
166 }
167
168 let median_val = weighted_median(&values, &weights);
169 pop_median.push(median_val);
170 }
171
172 let mut posterior_mean: Vec<f64> = vec![0.0; predictions.first().unwrap().len()];
173 for (i, outer_pred) in predictions.iter().enumerate() {
174 for (j, pred) in outer_pred.iter().enumerate() {
175 posterior_mean[j] += pred.prediction() * posterior.matrix()[(subject_index, i)];
176 }
177 }
178
179 let mut posterior_median: Vec<f64> = Vec::new();
180 for j in 0..predictions.first().unwrap().len() {
181 let mut values: Vec<f64> = Vec::new();
182 let mut weights: Vec<f64> = Vec::new();
183
184 for (i, outer_pred) in predictions.iter().enumerate() {
185 values.push(outer_pred[j].prediction());
186 weights.push(posterior.matrix()[(subject_index, i)]);
187 }
188
189 let median_val = weighted_median(&values, &weights);
190 posterior_median.push(median_val);
191 }
192
193 if let Some(first_spp_preds) = predictions.first() {
194 for (j, p) in first_spp_preds.iter().enumerate() {
195 let row = NPPredictionRow {
196 id: subject.id().clone(),
197 time: p.time(),
198 outeq: p.outeq(),
199 block: p.occasion(),
200 obs: p.observation(),
201 cens: p.censoring(),
202 pop_mean: pop_mean[j],
203 pop_median: pop_median[j],
204 post_mean: posterior_mean[j],
205 post_median: posterior_median[j],
206 };
207 container.add(row);
208 }
209 }
210 }
211
212 Ok(container)
213 }
214}