|
27 | 27 | [java-time.api :as jt])) |
28 | 28 |
|
29 | 29 |
|
30 | | -;; # Exploring HRV |
| 30 | +;; # Exploring HRV - DRAFT 🛠 |
31 | 31 |
|
32 | 32 | (ns data-analysis.heart-rate-variability.exploring-heart-rate-variability |
33 | 33 | (:require [tech.v3.datatype :as dtype] |
|
75 | 75 | (plotly/layer-bar {:=y :ppi}))) |
76 | 76 |
|
77 | 77 |
|
78 | | - |
79 | | -(def compute-spectrograms |
| 78 | +(def compute-measures |
80 | 79 | (fn [ppi-ds {:keys [sampling-rate |
81 | 80 | window-size-in-sec ]}] |
82 | 81 | (let [spline (interp/interpolation |
|
121 | 120 |
|
122 | 121 |
|
123 | 122 | (comment |
124 | | - (compute-spectrograms my-ppi |
125 | | - {:sampling-rate 10 |
126 | | - :window-size-in-sec 60})) |
127 | | - |
| 123 | + (compute-measures my-ppi |
| 124 | + {:sampling-rate 10 |
| 125 | + :window-size-in-sec 60})) |
128 | 126 |
|
129 | 127 |
|
130 | 128 | ;; [An Overview of Heart Rate Variability Metrics and Norms](https://pmc.ncbi.nlm.nih.gov/articles/PMC5624990/) |
|
145 | 143 | tcc/sum)))) |
146 | 144 |
|
147 | 145 |
|
148 | | - |
149 | | -(defn plot-with-power-spectrum [{:keys [sampling-rate |
150 | | - resampled-ppi |
151 | | - spectrograms]}] |
| 146 | +(defn plot-with-measures [{:keys [sampling-rate |
| 147 | + resampled-ppi |
| 148 | + spectrograms]}] |
152 | 149 | (when spectrograms |
153 | | - (kind/fragment |
154 | | - (let [n (-> spectrograms first :magnitude count) |
155 | | - Nyquist-freq (/ sampling-rate 2.0) |
156 | | - freq-resolution (/ Nyquist-freq n) |
157 | | - times (map (comp str :t) spectrograms) |
158 | | - freqs (tcc/* (range n) |
159 | | - freq-resolution)] |
160 | | - [(-> resampled-ppi |
161 | | - (plotly/base {:=height 300 :=width 700}) |
162 | | - (plotly/layer-bar (merge {:=x :t |
163 | | - :=y :ppi} |
164 | | - (when (:label resampled-ppi) |
165 | | - {:=color :label |
166 | | - :=color-type :nominal})))) |
167 | | - (kind/plotly |
168 | | - {:data [{:x times |
169 | | - :y freqs |
170 | | - :z (-> (mapv :magnitude spectrograms) |
171 | | - (tensor/transpose [1 0])) |
172 | | - :type :heatmap |
173 | | - :showscale false}] |
174 | | - :layout {:height 300 |
175 | | - :width 700 |
176 | | - :margin {:t 25} |
177 | | - :xaxis {:title {:text "t"}} |
178 | | - :yaxis {:title {:text "freq"}}}}) |
179 | | - (-> {:freq freqs |
180 | | - :mean-power (-> spectrograms |
181 | | - (->> (map :magnitude)) |
182 | | - tensor/->tensor |
183 | | - (tensor/reduce-axis dfn/mean 0))} |
184 | | - tc/dataset |
185 | | - (plotly/base {:=height 300 :=width 700}) |
186 | | - (plotly/layer-bar {:=x :freq |
187 | | - :=y :mean-power})) |
188 | | - (-> {:t times |
189 | | - :LF-to-HF (->> spectrograms |
190 | | - (map (partial LF-to-HF freqs)))} |
191 | | - tc/dataset |
192 | | - (plotly/base {:=height 300 :=width 700}) |
193 | | - (plotly/layer-line {:=x :t |
194 | | - :=y :LF-to-HF}) |
195 | | - plotly/plot |
196 | | - (assoc-in [:layout :yaxis :range] [0 4]) |
197 | | - (assoc-in [:layout :yaxis :title] {:text "LF/HF"}))])))) |
| 150 | + (let [n (-> spectrograms first :magnitude count) |
| 151 | + Nyquist-freq (/ sampling-rate 2.0) |
| 152 | + freq-resolution (/ Nyquist-freq n) |
| 153 | + times (map (comp str :t) spectrograms) |
| 154 | + freqs (tcc/* (range n) |
| 155 | + freq-resolution)] |
| 156 | + {:resampled-ppi (-> resampled-ppi |
| 157 | + (plotly/base {:=height 300 :=width 700}) |
| 158 | + (plotly/layer-bar (merge {:=x :t |
| 159 | + :=y :ppi} |
| 160 | + (when (:label resampled-ppi) |
| 161 | + {:=color :label |
| 162 | + :=color-type :nominal})))) |
| 163 | + :power-spectrum (kind/plotly |
| 164 | + {:data [{:x times |
| 165 | + :y freqs |
| 166 | + :z (-> (mapv :magnitude spectrograms) |
| 167 | + (tensor/transpose [1 0])) |
| 168 | + :type :heatmap |
| 169 | + :showscale false}] |
| 170 | + :layout {:height 300 |
| 171 | + :width 700 |
| 172 | + :margin {:t 25} |
| 173 | + :xaxis {:title {:text "t"}} |
| 174 | + :yaxis {:title {:text "freq"}}}}) |
| 175 | + :mean-power-spectrum (-> {:freq freqs |
| 176 | + :mean-power (-> spectrograms |
| 177 | + (->> (map :magnitude)) |
| 178 | + tensor/->tensor |
| 179 | + (tensor/reduce-axis dfn/mean 0))} |
| 180 | + tc/dataset |
| 181 | + (plotly/base {:=height 300 :=width 700}) |
| 182 | + (plotly/layer-bar {:=x :freq |
| 183 | + :=y :mean-power})) |
| 184 | + :LF-to-HF-series (-> {:t times |
| 185 | + :LF-to-HF (->> spectrograms |
| 186 | + (map (partial LF-to-HF freqs)))} |
| 187 | + tc/dataset |
| 188 | + (plotly/base {:=height 300 :=width 700}) |
| 189 | + (plotly/layer-line {:=x :t |
| 190 | + :=y :LF-to-HF}) |
| 191 | + plotly/plot |
| 192 | + (assoc-in [:layout :yaxis :range] [0 4]) |
| 193 | + (assoc-in [:layout :yaxis :title] {:text "LF/HF"}))}))) |
198 | 194 |
|
199 | 195 |
|
200 | 196 | (delay |
201 | 197 | (-> my-ppi |
202 | | - (compute-spectrograms {:sampling-rate 10 |
203 | | - :window-size-in-sec 60}) |
204 | | - plot-with-power-spectrum)) |
| 198 | + (compute-measures {:sampling-rate 10 |
| 199 | + :window-size-in-sec 60}) |
| 200 | + plot-with-measures)) |
205 | 201 |
|
206 | 202 |
|
207 | 203 | ;; ## Analysing ECG data |
|
327 | 323 | (tcc/shift (:t %) 1))) |
328 | 324 | (tc/drop-rows [0])))) |
329 | 325 |
|
330 | | - |
331 | 326 | ;; ### Plotting the PPI |
332 | 327 |
|
333 | 328 | (delay |
|
341 | 336 | (plotly/layer-bar {:=x :t |
342 | 337 | :=y :ppi}))) |
343 | 338 |
|
344 | | -;; ## Spectrograms again |
| 339 | +;; ### Measures again |
345 | 340 |
|
346 | 341 | (def WESAD-spectrograms |
347 | 342 | (memoize |
348 | 343 | (fn [{:keys [ppi-params spectrogram-params]}] |
349 | 344 | (-> ppi-params |
350 | 345 | extract-ppi |
351 | | - (compute-spectrograms spectrogram-params))))) |
| 346 | + (compute-measures spectrogram-params))))) |
352 | 347 |
|
353 | 348 |
|
354 | 349 | (delay |
|
357 | 352 | :spectrogram-params {:sampling-rate 10 |
358 | 353 | :window-size-in-sec 120}} |
359 | 354 | WESAD-spectrograms |
360 | | - plot-with-power-spectrum)) |
| 355 | + plot-with-measures)) |
| 356 | + |
| 357 | +;; ## A subject's journey |
| 358 | + |
| 359 | +(def id->label |
| 360 | + [:transient, :baseline, |
| 361 | + :stress, :amusement, :meditation, |
| 362 | + :ignore :ignore :ignore]) |
361 | 363 |
|
| 364 | +(def label-intervals |
| 365 | + (memoize |
| 366 | + (fn [subject] |
| 367 | + (-> (labelled-dataset subject) |
| 368 | + :label |
| 369 | + (->> (partition-by identity) |
| 370 | + (map (fn [part] |
| 371 | + [(-> part first int id->label) |
| 372 | + (count part)]))) |
| 373 | + tc/dataset |
| 374 | + (tc/rename-columns [:label :n]) |
| 375 | + (tc/add-column :offset #(cons 0 (reductions + (:n %)))) |
| 376 | + (tc/select-columns [:offset :n :label]))))) |
362 | 377 |
|
| 378 | + |
| 379 | +(delay |
| 380 | + (label-intervals 5)) |
| 381 | + |
| 382 | + |
| 383 | +(delay |
| 384 | + (let [subject 5] |
| 385 | + (-> (label-intervals subject) |
| 386 | + (tc/select-rows #(not= (:label %) :ignore)) |
| 387 | + #_(tc/select-rows #(= (:label %) :meditation)) |
| 388 | + (tc/rows :as-maps) |
| 389 | + (->> (map (fn [{:keys [offset n label]}] |
| 390 | + [label |
| 391 | + (try (-> {:ppi-params {:subject-id subject |
| 392 | + :row-interval [offset (+ offset n)]} |
| 393 | + :spectrogram-params {:sampling-rate 10 |
| 394 | + :window-size-in-sec 120}} |
| 395 | + WESAD-spectrograms |
| 396 | + plot-with-measures |
| 397 | + ;; :LF-to-HF-series |
| 398 | + ) |
| 399 | + (catch Exception e 'unavailable))])))))) |
0 commit comments