comparison NmrPreprocessing_script.R @ 7:122df1bf0a8c draft default tip

planemo upload for repository https://github.com/workflow4metabolomics/nmr_preprocessing commit 3d328007dd7716848ec2eeb6c2a472f27eeb2995
author workflow4metabolomics
date Fri, 11 Jul 2025 08:33:38 +0000
parents 5b06800f3449
children
comparison
equal deleted inserted replaced
6:6e837e9352a2 7:122df1bf0a8c
1 ## ========================== 1 ## ==========================
2 # Internal functions 2 # Internal functions
3 ## ========================== 3 ## ==========================
4 4
5 # beginTreatment 5 # beginTreatment
6 beginTreatment <- function(name, Signal_data = NULL, Signal_info = NULL, 6 beginTreatment <- function(name, Signal_data = NULL, Signal_info = NULL,
7 force.real = FALSE) { 7 force.real = FALSE) {
8 8 cat("Begin", name, "\n")
9 cat("Begin", name, "\n") 9
10 10
11 11 # Formatting the Signal_data and Signal_info -----------------------
12 # Formatting the Signal_data and Signal_info ----------------------- 12
13 13 vec <- is.vector(Signal_data)
14 vec <- is.vector(Signal_data) 14 if (vec) {
15 if (vec) { 15 Signal_data <- vec2mat(Signal_data)
16 Signal_data <- vec2mat(Signal_data) 16 }
17 } 17 if (is.vector(Signal_info)) {
18 if (is.vector(Signal_info)) { 18 Signal_info <- vec2mat(Signal_info)
19 Signal_info <- vec2mat(Signal_info) 19 }
20 } 20 if (!is.null(Signal_data)) {
21 if (!is.null(Signal_data)) { 21 if (!is.matrix(Signal_data)) {
22 if (!is.matrix(Signal_data)) { 22 stop("Signal_data is not a matrix.")
23 stop("Signal_data is not a matrix.") 23 }
24 } 24 if (!is.complex(Signal_data) && !is.numeric(Signal_data)) {
25 if (!is.complex(Signal_data) && !is.numeric(Signal_data)) { 25 stop("Signal_data contains non-numerical values.")
26 stop("Signal_data contains non-numerical values.") 26 }
27 } 27 }
28 } 28 if (!is.null(Signal_info) && !is.matrix(Signal_info)) {
29 if (!is.null(Signal_info) && !is.matrix(Signal_info)) { 29 stop("Signal_info is not a matrix.")
30 stop("Signal_info is not a matrix.") 30 }
31 } 31
32 32
33 33 Original_data <- Signal_data
34 Original_data <- Signal_data 34
35 35 # Extract the real part of the spectrum ---------------------------
36 # Extract the real part of the spectrum --------------------------- 36
37 37 if (force.real) {
38 if (force.real) { 38 if (is.complex(Signal_data)) {
39 if (is.complex(Signal_data)) { 39 Signal_data <- Re(Signal_data)
40 Signal_data <- Re(Signal_data) 40 } else {
41 } else { 41 # The signal is numeric Im(Signal_data) is zero anyway so let's avoid
42 # The signal is numeric Im(Signal_data) is zero anyway so let's avoid 42 # using complex(real=...,imaginary=0) which would give a complex signal
43 # using complex(real=...,imaginary=0) which would give a complex signal 43 # in endTreatment()
44 # in endTreatment() 44 force.real <- FALSE
45 force.real <- FALSE 45 }
46 } 46 }
47 } 47
48 48
49 49 # Return the formatted data and metadata entries --------------------
50 # Return the formatted data and metadata entries -------------------- 50
51 51 return(list(
52 return(list(start = proc.time(), vec = vec, force.real = force.real, 52 start = proc.time(), vec = vec, force.real = force.real,
53 Original_data = Original_data, Signal_data = Signal_data, Signal_info = Signal_info)) 53 Original_data = Original_data, Signal_data = Signal_data, Signal_info = Signal_info
54 } 54 ))
55 55 }
56 # endTreatment 56
57 # endTreatment
57 endTreatment <- function(name, begin_info, Signal_data) { 58 endTreatment <- function(name, begin_info, Signal_data) {
58 59 # begin_info: object outputted from beginTreatment
59 # begin_info: object outputted from beginTreatment 60
60 61
61 62 # Formatting the entries and printing process time -----------------------
62 # Formatting the entries and printing process time ----------------------- 63 end_time <- proc.time() # record it as soon as possible
63 end_time <- proc.time() # record it as soon as possible 64 start_time <- begin_info[["start"]]
64 start_time <- begin_info[["start"]] 65 delta_time <- end_time - start_time
65 delta_time <- end_time - start_time 66 delta <- delta_time[]
66 delta <- delta_time[] 67 cat("End", name, "\n")
67 cat("End", name, "\n") 68 cat(
68 cat("It lasted", round(delta["user.self"], 3), "s user time,", round(delta["sys.self"],3), 69 "It lasted", round(delta["user.self"], 3), "s user time,", round(delta["sys.self"], 3),
69 "s system time and", round(delta["elapsed"], 3), "s elapsed time.\n") 70 "s system time and", round(delta["elapsed"], 3), "s elapsed time.\n"
70 71 )
71 72
72 if (begin_info[["force.real"]]) { 73
73 # The imaginary part is left untouched 74 if (begin_info[["force.real"]]) {
74 i <- complex(real = 0, imaginary = 1) 75 # The imaginary part is left untouched
75 Signal_data <- Signal_data + i * Im(begin_info[["Original_data"]]) 76 i <- complex(real = 0, imaginary = 1)
76 } 77 Signal_data <- Signal_data + i * Im(begin_info[["Original_data"]])
77 78 }
78 if (begin_info[["vec"]]) { 79
79 Signal_data <- Signal_data[1, ] 80 if (begin_info[["vec"]]) {
80 } 81 Signal_data <- Signal_data[1, ]
81 82 }
82 # Return the formatted data and metadata entries -------------------- 83
83 return(Signal_data) 84 # Return the formatted data and metadata entries --------------------
84 } 85 return(Signal_data)
85 86 }
86 # checkArg 87
87 checkArg <- function(arg, checks, can.be.null=FALSE) { 88 # checkArg
88 check.list <- list(bool=c(is.logical, "a boolean"), 89 checkArg <- function(arg, checks, can.be.null = FALSE) {
89 int =c(function(x){x%%1==0}, "an integer"), 90 check.list <- list(
90 num =c(is.numeric, "a numeric"), 91 bool = c(is.logical, "a boolean"),
91 str =c(is.character, "a string"), 92 int = c(function(x) {
92 pos =c(function(x){x>0}, "positive"), 93 x %% 1 == 0
93 pos0=c(function(x){x>=0}, "positive or zero"), 94 }, "an integer"),
94 l1 =c(function(x){length(x)==1}, "of length 1") 95 num = c(is.numeric, "a numeric"),
95 ) 96 str = c(is.character, "a string"),
96 if (is.null(arg)) { 97 pos = c(function(x) {
97 if (!can.be.null) { 98 x > 0
98 stop(deparse(substitute(arg)), " is null.") 99 }, "positive"),
99 } 100 pos0 = c(function(x) {
100 } else { 101 x >= 0
101 if (is.matrix(arg)) { 102 }, "positive or zero"),
102 stop(deparse(substitute(arg)), " is not scalar.") 103 l1 = c(function(x) {
103 } 104 length(x) == 1
104 for (c in checks) { 105 }, "of length 1")
105 if (!check.list[[c]][[1]](arg)) { 106 )
106 stop(deparse(substitute(arg)), " is not ", check.list[[c]][[2]], ".") 107 if (is.null(arg)) {
107 } 108 if (!can.be.null) {
108 } 109 stop(deparse(substitute(arg)), " is null.")
109 } 110 }
110 } 111 } else {
111 112 if (is.matrix(arg)) {
112 # getArg 113 stop(deparse(substitute(arg)), " is not scalar.")
113 getArg <- function(arg, info, argname, can.be.absent=FALSE) { 114 }
114 if (is.null(arg)) { 115 for (c in checks) {
115 start <- paste("impossible to get argument", argname, "it was not given directly and"); 116 if (!check.list[[c]][[1]](arg)) {
116 if (!is.matrix(info)) { 117 stop(deparse(substitute(arg)), " is not ", check.list[[c]][[2]], ".")
117 stop(paste(start, "the info matrix was not given")) 118 }
118 } 119 }
119 if (!(argname %in% colnames(info))) { 120 }
120 if (can.be.absent) { 121 }
121 return(NULL) 122
122 } else { 123 # getArg
123 stop(paste(start, "is not in the info matrix")) 124 getArg <- function(arg, info, argname, can.be.absent = FALSE) {
124 } 125 if (is.null(arg)) {
125 } 126 start <- paste("impossible to get argument", argname, "it was not given directly and")
126 if (nrow(info) < 1) { 127 if (!is.matrix(info)) {
127 stop(paste(start, "the info matrix has no row")) 128 stop(paste(start, "the info matrix was not given"))
128 } 129 }
129 arg <- info[1,argname] 130 if (!(argname %in% colnames(info))) {
130 if (is.na(arg)) { 131 if (can.be.absent) {
131 stop(paste(start, "it is NA in the info matrix")) 132 return(NULL)
132 } 133 } else {
133 } 134 stop(paste(start, "is not in the info matrix"))
134 return(arg) 135 }
136 }
137 if (nrow(info) < 1) {
138 stop(paste(start, "the info matrix has no row"))
139 }
140 arg <- info[1, argname]
141 if (is.na(arg)) {
142 stop(paste(start, "it is NA in the info matrix"))
143 }
144 }
145 return(arg)
135 } 146 }
136 147
137 # binarySearch 148 # binarySearch
138 binarySearch <- function(a, target, lower = TRUE) { 149 binarySearch <- function(a, target, lower = TRUE) {
139 # search the index i in a such that a[i] == target 150 # search the index i in a such that a[i] == target
140 # if it doesn't exists and lower, it searches the closer a[i] such that a[i] < target 151 # if it doesn't exists and lower, it searches the closer a[i] such that a[i] < target
141 # if !lower, it seraches the closer a[i] such that a[i] > target 152 # if !lower, it seraches the closer a[i] such that a[i] > target
142 # a should be monotone but can be increasing or decreasing 153 # a should be monotone but can be increasing or decreasing
143 154
144 # if a is increasing INVARIANT: a[amin] < target < a[amax] 155 # if a is increasing INVARIANT: a[amin] < target < a[amax]
145 N <- length(a) 156 N <- length(a)
146 if ((a[N] - target) * (a[N] - a[1]) <= 0) { 157 if ((a[N] - target) * (a[N] - a[1]) <= 0) {
147 return(N) 158 return(N)
148 } 159 }
149 if ((a[1] - target) * (a[N] - a[1]) >= 0) { 160 if ((a[1] - target) * (a[N] - a[1]) >= 0) {
150 return(1) 161 return(1)
151 } 162 }
152 amin <- 1 163 amin <- 1
153 amax <- N 164 amax <- N
154 while (amin + 1 < amax) { 165 while (amin + 1 < amax) {
155 amid <- floor((amin + amax)/2) 166 amid <- floor((amin + amax) / 2)
156 if ((a[amid] - target) * (a[amax] - a[amid]) < 0) { 167 if ((a[amid] - target) * (a[amax] - a[amid]) < 0) {
157 amin <- amid 168 amin <- amid
158 } else if ((a[amid] - target) * (a[amax] - a[amid]) > 0) { 169 } else if ((a[amid] - target) * (a[amax] - a[amid]) > 0) {
159 amax <- amid 170 amax <- amid
160 } else { 171 } else {
161 # a[amid] == a[amax] or a[amid] == target In both cases, a[amid] == 172 # a[amid] == a[amax] or a[amid] == target In both cases, a[amid] ==
162 # target 173 # target
163 return(amid) 174 return(amid)
164 } 175 }
165 } 176 }
166 if (xor(lower, a[amin] > a[amax])) { 177 if (xor(lower, a[amin] > a[amax])) {
167 # (lower && a[amin] < a[amax]) || (!lower && a[min] > a[max]) 178 # (lower && a[amin] < a[amax]) || (!lower && a[min] > a[max])
168 # If increasing and we want the lower, we take amin 179 # If increasing and we want the lower, we take amin
169 # If decreasing and we want the bigger, we take amin too 180 # If decreasing and we want the bigger, we take amin too
170 return(amin) 181 return(amin)
171 } else { 182 } else {
172 return(amax) 183 return(amax)
173 } 184 }
174 } 185 }
175 186
176 # Interpol 187 # Interpol
177 Interpol <- function(t, y) { 188 Interpol <- function(t, y) {
178 # y: sample 189 # y: sample
179 # t : warping function 190 # t : warping function
180 191
181 m <- length(y) 192 m <- length(y)
182 # t <= m-1 193 # t <= m-1
183 # because if t > m-1, y[ti+1] will be NA when we compute g 194 # because if t > m-1, y[ti+1] will be NA when we compute g
184 valid <- 1 <= t & t <= m-1 # FIXME it was '<' in Bubble v2 195 valid <- 1 <= t & t <= m - 1 # FIXME it was '<' in Bubble v2
185 s <- (1:m)[valid] 196 s <- (1:m)[valid]
186 ti <- floor(t[s]) 197 ti <- floor(t[s])
187 tr <- t[s] - ti 198 tr <- t[s] - ti
188 g <- y[ti + 1] - y[ti] 199 g <- y[ti + 1] - y[ti]
189 f <- y[ti] + tr * g 200 f <- y[ti] + tr * g
190 list(f=f, s=s, g=g) 201 list(f = f, s = s, g = g)
191 } 202 }
192 203
193 # vec2mat 204 # vec2mat
194 vec2mat <- function(vec) { 205 vec2mat <- function(vec) {
195 return(matrix(vec, nrow = 1, dimnames = list(c(1), names(vec)))) 206 return(matrix(vec, nrow = 1, dimnames = list(c(1), names(vec))))
196
197 } 207 }
198 208
199 # binarySearch 209 # binarySearch
200 binarySearch <- function(a, target, lower = TRUE) { 210 binarySearch <- function(a, target, lower = TRUE) {
201 # search the index i in a such that a[i] == target 211 # search the index i in a such that a[i] == target
202 # if it doesn't exists and lower, it searches the closer a[i] such that a[i] < target 212 # if it doesn't exists and lower, it searches the closer a[i] such that a[i] < target
203 # if !lower, it seraches the closer a[i] such that a[i] > target 213 # if !lower, it seraches the closer a[i] such that a[i] > target
204 # a should be monotone but can be increasing or decreasing 214 # a should be monotone but can be increasing or decreasing
205 215
206 # if a is increasing INVARIANT: a[amin] < target < a[amax] 216 # if a is increasing INVARIANT: a[amin] < target < a[amax]
207 N <- length(a) 217 N <- length(a)
208 if ((a[N] - target) * (a[N] - a[1]) <= 0) { 218 if ((a[N] - target) * (a[N] - a[1]) <= 0) {
209 return(N) 219 return(N)
210 } 220 }
211 if ((a[1] - target) * (a[N] - a[1]) >= 0) { 221 if ((a[1] - target) * (a[N] - a[1]) >= 0) {
212 return(1) 222 return(1)
213 } 223 }
214 amin <- 1 224 amin <- 1
215 amax <- N 225 amax <- N
216 while (amin + 1 < amax) { 226 while (amin + 1 < amax) {
217 amid <- floor((amin + amax)/2) 227 amid <- floor((amin + amax) / 2)
218 if ((a[amid] - target) * (a[amax] - a[amid]) < 0) { 228 if ((a[amid] - target) * (a[amax] - a[amid]) < 0) {
219 amin <- amid 229 amin <- amid
220 } else if ((a[amid] - target) * (a[amax] - a[amid]) > 0) { 230 } else if ((a[amid] - target) * (a[amax] - a[amid]) > 0) {
221 amax <- amid 231 amax <- amid
222 } else { 232 } else {
223 # a[amid] == a[amax] or a[amid] == target In both cases, a[amid] == 233 # a[amid] == a[amax] or a[amid] == target In both cases, a[amid] ==
224 # target 234 # target
225 return(amid) 235 return(amid)
226 } 236 }
227 } 237 }
228 if (xor(lower, a[amin] > a[amax])) { 238 if (xor(lower, a[amin] > a[amax])) {
229 # (lower && a[amin] < a[amax]) || (!lower && a[min] > a[max]) 239 # (lower && a[amin] < a[amax]) || (!lower && a[min] > a[max])
230 # If increasing and we want the lower, we take amin 240 # If increasing and we want the lower, we take amin
231 # If decreasing and we want the bigger, we take amin too 241 # If decreasing and we want the bigger, we take amin too
232 return(amin) 242 return(amin)
233 } else { 243 } else {
234 return(amax) 244 return(amax)
235 } 245 }
236 } 246 }
237 247
238 248
239 # indexInterval 249 # indexInterval
240 indexInterval <- function (a, from, to, inclusive=TRUE) { 250 indexInterval <- function(a, from, to, inclusive = TRUE) {
241 # If inclusive and from <= to, we need to take the lower 251 # If inclusive and from <= to, we need to take the lower
242 # If not inclusive and from > to, we need to take the lower too 252 # If not inclusive and from > to, we need to take the lower too
243 lowerFrom <- xor(inclusive, from > to) 253 lowerFrom <- xor(inclusive, from > to)
244 fromIndex <- binarySearch(a, from, lowerFrom) 254 fromIndex <- binarySearch(a, from, lowerFrom)
245 toIndex <- binarySearch(a, to, !lowerFrom) 255 toIndex <- binarySearch(a, to, !lowerFrom)
246 return(fromIndex:toIndex) 256 return(fromIndex:toIndex)
247 } 257 }
248 258
249 259
250 260
251 ## ========================== 261 ## ==========================
252 # GroupDelayCorrection 262 # GroupDelayCorrection
253 ## ========================== 263 ## ==========================
254 GroupDelayCorrection <- function(Fid_data, Fid_info = NULL, group_delay = NULL) { 264 GroupDelayCorrection <- function(Fid_data, Fid_info = NULL, group_delay = NULL) {
255
256
257 # Data initialisation and checks ---------------------------------------------- 265 # Data initialisation and checks ----------------------------------------------
258 266
259 begin_info <- beginTreatment("GroupDelayCorrection", Fid_data, Fid_info) 267 begin_info <- beginTreatment("GroupDelayCorrection", Fid_data, Fid_info)
260 Fid_data <- begin_info[["Signal_data"]] 268 Fid_data <- begin_info[["Signal_data"]]
261 dimension_names <- dimnames(Fid_data) 269 dimension_names <- dimnames(Fid_data)
262 Fid_info <- begin_info[["Signal_info"]] 270 Fid_info <- begin_info[["Signal_info"]]
263 checkArg(group_delay, c("num", "pos0"), can.be.null = TRUE) 271 checkArg(group_delay, c("num", "pos0"), can.be.null = TRUE)
264 # if Fid_info and group_delay are NULL, getArg will generate an error 272 # if Fid_info and group_delay are NULL, getArg will generate an error
265 273
266 group_delay <- getArg(group_delay, Fid_info, "GRPDLY", can.be.absent = TRUE) 274 group_delay <- getArg(group_delay, Fid_info, "GRPDLY", can.be.absent = TRUE)
267 275
268 if (is.null(group_delay)) { 276 if (is.null(group_delay)) {
269 277 # See DetermineBrukerDigitalFilter.m in matNMR MATLAB library
270 # See DetermineBrukerDigitalFilter.m in matNMR MATLAB library 278 group_delay_matrix <- matrix(
271 group_delay_matrix <- matrix(c(44.75, 46, 46.311, 33.5, 36.5, 36.53, 66.625, 279 c(
272 48, 47.87, 59.0833, 50.1667, 50.229, 68.5625, 53.25, 53.289, 60.375, 280 44.75, 46, 46.311, 33.5, 36.5, 36.53, 66.625,
273 69.5, 69.551, 69.5313, 72.25, 71.6, 61.0208, 70.1667, 70.184, 70.0156, 281 48, 47.87, 59.0833, 50.1667, 50.229, 68.5625, 53.25, 53.289, 60.375,
274 72.75, 72.138, 61.3438, 70.5, 70.528, 70.2578, 73, 72.348, 61.5052, 70.6667, 282 69.5, 69.551, 69.5313, 72.25, 71.6, 61.0208, 70.1667, 70.184, 70.0156,
275 70.7, 70.3789, 72.5, 72.524, 61.5859, 71.3333, NA, 70.4395, 72.25, NA, 283 72.75, 72.138, 61.3438, 70.5, 70.528, 70.2578, 73, 72.348, 61.5052, 70.6667,
276 61.6263, 71.6667, NA, 70.4697, 72.125, NA, 61.6465, 71.8333, NA, 70.4849, 284 70.7, 70.3789, 72.5, 72.524, 61.5859, 71.3333, NA, 70.4395, 72.25, NA,
277 72.0625, NA, 61.6566, 71.9167, NA, 70.4924, 72.0313, NA), nrow = 21, 285 61.6263, 71.6667, NA, 70.4697, 72.125, NA, 61.6465, 71.8333, NA, 70.4849,
278 ncol = 3, byrow = TRUE, dimnames = list(c(2, 3, 4, 6, 8, 12, 16, 24, 286 72.0625, NA, 61.6566, 71.9167, NA, 70.4924, 72.0313, NA
279 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048), 287 ),
280 c(10, 11, 12))) 288 nrow = 21,
281 decim <- Fid_info[1, "DECIM"] 289 ncol = 3, byrow = TRUE, dimnames = list(
282 dspfvs <- Fid_info[1, "DSPFVS"] 290 c(
283 if (!(toString(decim) %in% rownames(group_delay_matrix))) { 291 2, 3, 4, 6, 8, 12, 16, 24,
284 stop(paste("Invalid DECIM", decim, "it should be one of", rownames(group_delay_matrix))) 292 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048
285 } 293 ),
286 if (!(toString(dspfvs) %in% colnames(group_delay_matrix))) { 294 c(10, 11, 12)
287 stop(paste("Invalid DSPFVS", dspfvs, "it should be one of", colnames(group_delay_matrix))) 295 )
288 } 296 )
289 group_delay <- group_delay_matrix[toString(decim), toString(dspfvs)] 297 decim <- Fid_info[1, "DECIM"]
290 if (is.na(group_delay)) { 298 dspfvs <- Fid_info[1, "DSPFVS"]
291 stop(paste("Invalid DECIM", decim, "for DSPFVS", dspfvs)) 299 if (!(toString(decim) %in% rownames(group_delay_matrix))) {
292 } 300 stop(paste("Invalid DECIM", decim, "it should be one of", rownames(group_delay_matrix)))
301 }
302 if (!(toString(dspfvs) %in% colnames(group_delay_matrix))) {
303 stop(paste("Invalid DSPFVS", dspfvs, "it should be one of", colnames(group_delay_matrix)))
304 }
305 group_delay <- group_delay_matrix[toString(decim), toString(dspfvs)]
306 if (is.na(group_delay)) {
307 stop(paste("Invalid DECIM", decim, "for DSPFVS", dspfvs))
308 }
293 } 309 }
294 m <- ncol(Fid_data) 310 m <- ncol(Fid_data)
295 n <- nrow(Fid_data) 311 n <- nrow(Fid_data)
296 312
297 # GroupDelayCorrection ---------------------------------------------- 313 # GroupDelayCorrection ----------------------------------------------
298 314
299 # We do the shifting in the Fourier domain because the shift can be non-integer. 315 # We do the shifting in the Fourier domain because the shift can be non-integer.
300 # That way we automatically have the circular behaviour of the shift and the 316 # That way we automatically have the circular behaviour of the shift and the
301 # interpolation if it is non-integer. 317 # interpolation if it is non-integer.
302 318
303 Spectrum <- t(stats::mvfft(t(Fid_data))) 319 Spectrum <- t(stats::mvfft(t(Fid_data)))
304 320
305 # Spectrum <- FourierTransform(Fid_data, Fid_info) 321 # Spectrum <- FourierTransform(Fid_data, Fid_info)
306 p <- ceiling(m/2) 322 p <- ceiling(m / 2)
307 new_index <- c((p + 1):m, 1:p) 323 new_index <- c((p + 1):m, 1:p)
308 Spectrum <- Spectrum[,new_index] 324 Spectrum <- Spectrum[, new_index]
309 Spectrum <- matrix(data = Spectrum, ncol = m, nrow = n) 325 Spectrum <- matrix(data = Spectrum, ncol = m, nrow = n)
310 326
311 Omega <- (0:(m - 1))/m 327 Omega <- (0:(m - 1)) / m
312 i <- complex(real = 0, imaginary = 1) 328 i <- complex(real = 0, imaginary = 1)
313 329
314 if (n>1) { 330 if (n > 1) {
315 Spectrum <- sweep(Spectrum, MARGIN = 2, exp(i * group_delay * 2 * pi * Omega), `*`) 331 Spectrum <- sweep(Spectrum, MARGIN = 2, exp(i * group_delay * 2 * pi * Omega), `*`)
316 Spectrum <- Spectrum[,new_index] 332 Spectrum <- Spectrum[, new_index]
317 }else { 333 } else {
318 Spectrum <- Spectrum* exp(i * group_delay * 2 * pi * Omega) 334 Spectrum <- Spectrum * exp(i * group_delay * 2 * pi * Omega)
319 Spectrum <- Spectrum[new_index] 335 Spectrum <- Spectrum[new_index]
320 Spectrum <- matrix(data = Spectrum, ncol = m, nrow = n) 336 Spectrum <- matrix(data = Spectrum, ncol = m, nrow = n)
321 } 337 }
322 338
323 339
324 Fid_data <- t(stats::mvfft(t(Spectrum), inverse = TRUE))/m 340 Fid_data <- t(stats::mvfft(t(Spectrum), inverse = TRUE)) / m
325 colnames(Fid_data) <- dimension_names[[2]] 341 colnames(Fid_data) <- dimension_names[[2]]
326 rownames(Fid_data) <- dimension_names[[1]] 342 rownames(Fid_data) <- dimension_names[[1]]
327 343
328 # Data finalisation ---------------------------------------------- 344 # Data finalisation ----------------------------------------------
329 345
330 return(endTreatment("GroupDelayCorrection", begin_info, Fid_data)) 346 return(endTreatment("GroupDelayCorrection", begin_info, Fid_data))
331 } 347 }
332 348
333 ## ========================== 349 ## ==========================
334 # SolventSuppression 350 # SolventSuppression
335 ## ========================== 351 ## ==========================
336 SolventSuppression <- function(Fid_data, lambda.ss = 1e+06, ptw.ss = TRUE, 352 SolventSuppression <- function(Fid_data, lambda.ss = 1e+06, ptw.ss = TRUE,
337 plotSolvent = F, returnSolvent = F) { 353 plotSolvent = F, returnSolvent = F) {
338 354 # Data initialisation and checks ----------------------------------------------
339 # Data initialisation and checks ---------------------------------------------- 355
340 356 begin_info <- beginTreatment("SolventSuppression", Fid_data)
341 begin_info <- beginTreatment("SolventSuppression", Fid_data) 357 Fid_data <- begin_info[["Signal_data"]]
342 Fid_data <- begin_info[["Signal_data"]] 358 checkArg(ptw.ss, c("bool"))
343 checkArg(ptw.ss, c("bool")) 359 checkArg(lambda.ss, c("num", "pos0"))
344 checkArg(lambda.ss, c("num", "pos0")) 360
345 361
346 362 # difsm function definition for the smoother -----------------------------------
347 # difsm function definition for the smoother ----------------------------------- 363
348 364 if (ptw.ss) {
349 if (ptw.ss) { 365 # Use of the function in ptw that smoothes signals with a finite difference
350 # Use of the function in ptw that smoothes signals with a finite difference 366 # penalty of order 2
351 # penalty of order 2 367 difsm <- ptw::difsm
352 difsm <- ptw::difsm 368 } else {
353 } else { 369 # Or manual implementation based on sparse matrices for large data series (cf.
354 # Or manual implementation based on sparse matrices for large data series (cf. 370 # Eilers, 2003. 'A perfect smoother')
355 # Eilers, 2003. 'A perfect smoother') 371 difsm <- function(y, d = 2, lambda) {
356 difsm <- function(y, d = 2, lambda) { 372 m <- length(y)
357 373 # Sparse identity matrix m x m
358 m <- length(y) 374 E <- Matrix::Diagonal(m)
359 # Sparse identity matrix m x m 375 D <- Matrix::diff(E, differences = d)
360 E <- Matrix::Diagonal(m) 376 A <- E + lambda.ss * Matrix::t(D) %*% D
361 D <- Matrix::diff(E, differences = d) 377 # base::chol does not take into account that A is sparse and is extremely slow
362 A <- E + lambda.ss * Matrix::t(D) %*% D 378 C <- Matrix::chol(A)
363 # base::chol does not take into account that A is sparse and is extremely slow 379 x <- Matrix::solve(C, Matrix::solve(Matrix::t(C), y))
364 C <- Matrix::chol(A) 380 return(as.numeric(x))
365 x <- Matrix::solve(C, Matrix::solve(Matrix::t(C), y)) 381 }
366 return(as.numeric(x)) 382 }
367 } 383
368 } 384 # Solvent Suppression ----------------------------------------------
369 385
370 # Solvent Suppression ---------------------------------------------- 386 n <- dim(Fid_data)[1]
371
372 n <- dim(Fid_data)[1]
373 if (returnSolvent) {
374 SolventRe <- Fid_data
375 SolventIm <- Fid_data
376 }
377 for (i in 1:n) {
378 FidRe <- Re(Fid_data[i, ])
379 FidIm <- Im(Fid_data[i, ])
380 solventRe <- difsm(y = FidRe, lambda = lambda.ss)
381 solventIm <- difsm(y = FidIm, lambda = lambda.ss)
382
383 if (plotSolvent) {
384 m <- length(FidRe)
385 graphics::plot(1:m, FidRe, type = "l", col = "red")
386 graphics::lines(1:m, solventRe, type = "l", col = "blue")
387 graphics::plot(1:m, FidIm, type = "l", col = "red")
388 graphics::lines(1:m, solventIm, type = "l", col = "blue")
389 }
390 FidRe <- FidRe - solventRe
391 FidIm <- FidIm - solventIm
392 Fid_data[i, ] <- complex(real = FidRe, imaginary = FidIm)
393 if (returnSolvent) { 387 if (returnSolvent) {
394 SolventRe[i, ] <- solventRe 388 SolventRe <- Fid_data
395 SolventIm[i, ] <- solventIm 389 SolventIm <- Fid_data
396 } 390 }
397 } 391 for (i in 1:n) {
398 392 FidRe <- Re(Fid_data[i, ])
399 393 FidIm <- Im(Fid_data[i, ])
400 # Data finalisation ---------------------------------------------- 394 solventRe <- difsm(y = FidRe, lambda = lambda.ss)
401 395 solventIm <- difsm(y = FidIm, lambda = lambda.ss)
402 Fid_data <- endTreatment("SolventSuppression", begin_info, Fid_data) 396
403 if (returnSolvent) { 397 if (plotSolvent) {
404 return(list(Fid_data = Fid_data, SolventRe = SolventRe, SolventIm = SolventIm)) 398 m <- length(FidRe)
405 } else { 399 graphics::plot(1:m, FidRe, type = "l", col = "red")
406 return(Fid_data) 400 graphics::lines(1:m, solventRe, type = "l", col = "blue")
407 } 401 graphics::plot(1:m, FidIm, type = "l", col = "red")
402 graphics::lines(1:m, solventIm, type = "l", col = "blue")
403 }
404 FidRe <- FidRe - solventRe
405 FidIm <- FidIm - solventIm
406 Fid_data[i, ] <- complex(real = FidRe, imaginary = FidIm)
407 if (returnSolvent) {
408 SolventRe[i, ] <- solventRe
409 SolventIm[i, ] <- solventIm
410 }
411 }
412
413
414 # Data finalisation ----------------------------------------------
415
416 Fid_data <- endTreatment("SolventSuppression", begin_info, Fid_data)
417 if (returnSolvent) {
418 return(list(Fid_data = Fid_data, SolventRe = SolventRe, SolventIm = SolventIm))
419 } else {
420 return(Fid_data)
421 }
408 } 422 }
409 423
410 424
411 ## ========================== 425 ## ==========================
412 # Apodization 426 # Apodization
413 # ============================= 427 # =============================
414 Apodization <- function(Fid_data, Fid_info = NULL, DT = NULL, 428 Apodization <- function(Fid_data, Fid_info = NULL, DT = NULL,
415 type.apod = c("exp","cos2", "blockexp", "blockcos2", 429 type.apod = c(
416 "gauss", "hanning", "hamming"), phase = 0, rectRatio = 1/2, 430 "exp", "cos2", "blockexp", "blockcos2",
431 "gauss", "hanning", "hamming"
432 ), phase = 0, rectRatio = 1 / 2,
417 gaussLB = 1, expLB = 1, plotWindow = F, returnFactor = F) { 433 gaussLB = 1, expLB = 1, plotWindow = F, returnFactor = F) {
418 434 # Data initialisation and checks ----------------------------------------------
419 # Data initialisation and checks ---------------------------------------------- 435 begin_info <- beginTreatment("Apodization", Fid_data, Fid_info)
420 begin_info <- beginTreatment("Apodization", Fid_data, Fid_info) 436 Fid_data <- begin_info[["Signal_data"]]
421 Fid_data <- begin_info[["Signal_data"]] 437 Fid_info <- begin_info[["Signal_info"]]
422 Fid_info <- begin_info[["Signal_info"]] 438 # Data check
423 # Data check 439 type.apod <- match.arg(type.apod)
424 type.apod <- match.arg(type.apod) 440 checkArg(DT, c("num", "pos"), can.be.null = TRUE)
425 checkArg(DT, c("num", "pos"), can.be.null = TRUE) 441 checkArg(phase, c("num"))
426 checkArg(phase, c("num")) 442
427 443 # Apodization ----------------------------------------------
428 # Apodization ---------------------------------------------- 444 DT <- getArg(DT, Fid_info, "DT") # Dwell Time
429 DT <- getArg(DT, Fid_info, "DT") # Dwell Time 445 m <- ncol(Fid_data)
430 m <- ncol(Fid_data) 446 t <- (1:m) * DT # Time
431 t <- (1:m) * DT # Time 447 rectSize <- ceiling(rectRatio * m)
432 rectSize <- ceiling(rectRatio * m) 448 gaussLB <- (gaussLB / (sqrt(8 * log(2))))
433 gaussLB <- (gaussLB/(sqrt(8 * log(2)))) 449 # Define the types of apodization:
434 # Define the types of apodization: 450 switch(type.apod,
435 switch(type.apod, exp = { 451 exp = {
436 # exponential 452 # exponential
437 Factor <- exp(-expLB * t) 453 Factor <- exp(-expLB * t)
438 }, cos2 = { 454 },
439 # cos^2 455 cos2 = {
440 c <- cos((1:m) * pi/(2 * m) - phase * pi/2) 456 # cos^2
441 Factor <- c * c 457 c <- cos((1:m) * pi / (2 * m) - phase * pi / 2)
442 }, blockexp = { 458 Factor <- c * c
443 # block and exponential 459 },
444 Factor <- c(rep.int(1, rectSize), rep.int(0, m - rectSize)) 460 blockexp = {
445 # | rectSize | 1 ___________ | \ 0 \____ 461 # block and exponential
446 Factor[(rectSize + 1):m] <- exp(-expLB * t[1:(m - rectSize)]) 462 Factor <- c(rep.int(1, rectSize), rep.int(0, m - rectSize))
447 }, blockcos2 = { 463 # | rectSize | 1 ___________ | \ 0 \____
448 # block and cos^2 464 Factor[(rectSize + 1):m] <- exp(-expLB * t[1:(m - rectSize)])
449 Factor <- c(rep.int(1, rectSize), rep.int(0, m - rectSize)) 465 },
450 c <- cos((1:(m - rectSize)) * pi/(2 * (m - rectSize))) 466 blockcos2 = {
451 Factor[(rectSize + 1):m] <- c * c 467 # block and cos^2
452 }, gauss = { 468 Factor <- c(rep.int(1, rectSize), rep.int(0, m - rectSize))
453 # gaussian 469 c <- cos((1:(m - rectSize)) * pi / (2 * (m - rectSize)))
454 Factor <- exp(-(gaussLB * t)^2/2) 470 Factor[(rectSize + 1):m] <- c * c
455 Factor <- Factor/max(Factor) 471 },
456 }, hanning = { 472 gauss = {
457 # Hanning 473 # gaussian
458 Factor <- 0.5 + 0.5 * cos((1:m) * pi/m - phase * pi) 474 Factor <- exp(-(gaussLB * t)^2 / 2)
459 }, hamming = { 475 Factor <- Factor / max(Factor)
460 # Hamming 476 },
461 Factor <- 0.54 + 0.46 * cos((1:m) * pi/m - phase * pi) 477 hanning = {
462 }) 478 # Hanning
463 if (plotWindow) { 479 Factor <- 0.5 + 0.5 * cos((1:m) * pi / m - phase * pi)
464 graphics::plot(1:m, Factor, "l") 480 },
465 # dev.off() # device independent, it is the responsability of the 481 hamming = {
466 # caller to do it 482 # Hamming
467 } 483 Factor <- 0.54 + 0.46 * cos((1:m) * pi / m - phase * pi)
468 # Apply the apodization factor on the spectra 484 }
469 Fid_data <- sweep(Fid_data, MARGIN = 2, Factor, `*`) 485 )
470 486 if (plotWindow) {
471 # Data finalisation ---------------------------------------------- 487 graphics::plot(1:m, Factor, "l")
472 Fid_data <- endTreatment("Apodization", begin_info, Fid_data) 488 # dev.off() # device independent, it is the responsability of the
473 if (returnFactor) { 489 # caller to do it
474 return(list(Fid_data = Fid_data, Factor = Factor)) 490 }
475 } else { 491 # Apply the apodization factor on the spectra
476 return(Fid_data) 492 Fid_data <- sweep(Fid_data, MARGIN = 2, Factor, `*`)
477 } 493
494 # Data finalisation ----------------------------------------------
495 Fid_data <- endTreatment("Apodization", begin_info, Fid_data)
496 if (returnFactor) {
497 return(list(Fid_data = Fid_data, Factor = Factor))
498 } else {
499 return(Fid_data)
500 }
478 } 501 }
479 502
480 503
481 ## ==================================================== 504 ## ====================================================
482 # FourierTransform 505 # FourierTransform
483 ## ==================================================== 506 ## ====================================================
484 507
485 508
486 # fftshift1D2D 509 # fftshift1D2D
487 fftshift1D2D <- function(x) { 510 fftshift1D2D <- function(x) {
488 vec <- F 511 vec <- F
489 if (is.vector(x)) { 512 if (is.vector(x)) {
490 x <- vec2mat(x) 513 x <- vec2mat(x)
491 vec <- T 514 vec <- T
492 } 515 }
493 m <- dim(x)[2] 516 m <- dim(x)[2]
494 p <- ceiling(m/2) 517 p <- ceiling(m / 2)
495 new_index <- c((p + 1):m, 1:p) 518 new_index <- c((p + 1):m, 1:p)
496 y <- x[, new_index, drop = vec] 519 y <- x[, new_index, drop = vec]
497 } 520 }
498 521
499 # FourierTransform 522 # FourierTransform
500 FourierTransform <- function(Fid_data, Fid_info = NULL, SW_h = NULL, SW = NULL, O1 = NULL, reverse.axis = TRUE) { 523 FourierTransform <- function(Fid_data, Fid_info = NULL, SW_h = NULL, SW = NULL, O1 = NULL, reverse.axis = TRUE) {
501 524 # Data initialisation and checks ----------------------------------------------
502 # Data initialisation and checks ---------------------------------------------- 525 begin_info <- beginTreatment("FourierTransform", Fid_data, Fid_info)
503 begin_info <- beginTreatment("FourierTransform", Fid_data, Fid_info) 526 Fid_data <- begin_info[["Signal_data"]]
504 Fid_data <- begin_info[["Signal_data"]] 527 Fid_info <- begin_info[["Signal_info"]]
505 Fid_info <- begin_info[["Signal_info"]] 528
506 529 m <- ncol(Fid_data)
507 m <- ncol(Fid_data) 530 n <- nrow(Fid_data)
508 n <- nrow(Fid_data) 531
509 532 if (is.null(SW_h)) {
510 if (is.null(SW_h)) { 533 SW_h <- getArg(SW_h, Fid_info, "SW_h")
511 SW_h <- getArg(SW_h, Fid_info, "SW_h") 534 }
512 } 535
513 536 if (is.null(SW)) {
514 if (is.null(SW)) { 537 SW <- getArg(SW, Fid_info, "SW") # Sweep Width in ppm (semi frequency scale in ppm)
515 SW <- getArg(SW, Fid_info, "SW") # Sweep Width in ppm (semi frequency scale in ppm) 538 }
516 } 539
517 540
518 541 if (is.null(O1)) {
519 if (is.null(O1)) { 542 O1 <- getArg(O1, Fid_info, "O1")
520 O1 <- getArg(O1, Fid_info, "O1") 543 }
521 } 544
522 545
523 546 checkArg(reverse.axis, c("bool"))
524 checkArg(reverse.axis, c("bool")) 547
525 548 # Fourier Transformation ----------------------------------------------
526 # Fourier Transformation ---------------------------------------------- 549 # mvfft does the unnormalized fourier transform (see ?mvfft), so we need divide
527 # mvfft does the unnormalized fourier transform (see ?mvfft), so we need divide 550 # by m. It does not matter a lot in our case since the spectrum will be
528 # by m. It does not matter a lot in our case since the spectrum will be 551 # normalized.
529 # normalized. 552
530 553 # FT
531 # FT 554 RawSpect_data <- fftshift1D2D(t(stats::mvfft(t(Fid_data))))
532 RawSpect_data <- fftshift1D2D(t(stats::mvfft(t(Fid_data)))) 555 # recover the frequencies values
533 # recover the frequencies values 556 f <- ((0:(m - 1)) - floor(m / 2)) * Fid_info[1, "SW_h"] / (m - 1)
534 f <- ((0:(m - 1)) - floor(m/2)) * Fid_info[1, "SW_h"]/(m-1) 557
535 558 if (reverse.axis == TRUE) {
536 if(reverse.axis == TRUE) { 559 revind <- rev(1:m)
537 revind <- rev(1:m) 560 RawSpect_data <- RawSpect_data[, revind] # reverse the spectrum
538 RawSpect_data <- RawSpect_data[,revind] # reverse the spectrum 561 }
539 } 562
540 563 RawSpect_data <- matrix(RawSpect_data, nrow = n, ncol = m)
541 RawSpect_data <- matrix(RawSpect_data, nrow = n, ncol = m) 564 colnames(RawSpect_data) <- f
542 colnames(RawSpect_data) <- f 565 rownames(RawSpect_data) <- rownames(Fid_data)
543 rownames(RawSpect_data) <- rownames(Fid_data) 566
544 567 # PPM conversion ----------------------------------------------
545 # PPM conversion ---------------------------------------------- 568
546 569 # The Sweep Width has to be the same since the column names are the same
547 # The Sweep Width has to be the same since the column names are the same 570
548 571 ppmInterval <- SW / (m - 1)
549 ppmInterval <- SW/(m-1) 572
550 573 O1index <- round((m + 1) / 2 + O1 * (m - 1) / SW_h)
551 O1index = round((m+1)/2+O1*(m - 1) / SW_h) 574
552 575 end <- O1index - m
553 end <- O1index - m 576 start <- O1index - 1
554 start <- O1index -1 577 ppmScale <- (start:end) * ppmInterval
555 ppmScale <- (start:end) * ppmInterval 578 RawSpect_data <- matrix(RawSpect_data,
556 RawSpect_data <- matrix(RawSpect_data, nrow = n, ncol = -(end - start) + 1, dimnames = 579 nrow = n, ncol = -(end - start) + 1, dimnames =
557 list(rownames(RawSpect_data), ppmScale)) 580 list(rownames(RawSpect_data), ppmScale)
558 581 )
559 582
560 # Data finalisation ---------------------------------------------- 583
561 return(endTreatment("FourierTransform", begin_info, RawSpect_data)) 584 # Data finalisation ----------------------------------------------
585 return(endTreatment("FourierTransform", begin_info, RawSpect_data))
562 } 586 }
563 587
564 ## ==================================================== 588 ## ====================================================
565 # InternalReferencing 589 # InternalReferencing
566 ## ==================================================== 590 ## ====================================================
567 591
568 InternalReferencing <- function(Spectrum_data, Fid_info, method = c("max", "thres"), 592 InternalReferencing <- function(Spectrum_data, Fid_info, method = c("max", "thres"),
569 range = c("nearvalue", "all", "window"), ppm.value = 0, 593 range = c("nearvalue", "all", "window"), ppm.value = 0,
570 direction = "left", shiftHandling = c("zerofilling", "cut", 594 direction = "left", shiftHandling = c(
571 "NAfilling", "circular"), c = 2, pc = 0.02, fromto.RC = NULL, 595 "zerofilling", "cut",
596 "NAfilling", "circular"
597 ), c = 2, pc = 0.02, fromto.RC = NULL,
572 ppm.ir = TRUE, rowindex_graph = NULL) { 598 ppm.ir = TRUE, rowindex_graph = NULL) {
573 599 # Data initialisation and checks ----------------------------------------------
574 600
575 601 begin_info <- beginTreatment("InternalReferencing", Spectrum_data, Fid_info)
576 # Data initialisation and checks ---------------------------------------------- 602 Spectrum_data <- begin_info[["Signal_data"]]
577 603 Fid_info <- begin_info[["Signal_info"]]
578 begin_info <- beginTreatment("InternalReferencing", Spectrum_data, Fid_info) 604
579 Spectrum_data <- begin_info[["Signal_data"]] 605
580 Fid_info <- begin_info[["Signal_info"]] 606 ######## Check input arguments
581 607
582 608 range <- match.arg(range)
583 ######## Check input arguments 609 shiftHandling <- match.arg(shiftHandling)
584 610 method <- match.arg(method)
585 range <- match.arg(range) 611 plots <- NULL
586 shiftHandling <- match.arg(shiftHandling) 612
587 method <- match.arg(method) 613 checkArg(ppm.ir, c("bool"))
588 plots <- NULL 614 checkArg(unlist(fromto.RC), c("num"), can.be.null = TRUE)
589 615 checkArg(pc, c("num"))
590 checkArg(ppm.ir, c("bool")) 616 checkArg(ppm.value, c("num"))
591 checkArg(unlist(fromto.RC), c("num"), can.be.null = TRUE) 617 checkArg(rowindex_graph, "num", can.be.null = TRUE)
592 checkArg(pc, c("num")) 618
593 checkArg(ppm.value, c("num")) 619 # fromto.RC : if range == "window",
594 checkArg(rowindex_graph, "num", can.be.null = TRUE) 620 # fromto.RC defines the spectral window where to search for the peak
595 621 if (!is.null(fromto.RC)) {
596 # fromto.RC : if range == "window", 622 diff <- diff(unlist(fromto.RC))[1:length(diff(unlist(fromto.RC))) %% 2 != 0]
597 # fromto.RC defines the spectral window where to search for the peak 623 for (i in 1:length(diff)) {
598 if (!is.null(fromto.RC)) { 624 if (diff[i] >= 0) {
599 diff <- diff(unlist(fromto.RC))[1:length(diff(unlist(fromto.RC)))%%2 !=0] 625 fromto <- c(fromto.RC[[i]][2], fromto.RC[[i]][1])
600 for (i in 1:length(diff)) { 626 fromto.RC[[i]] <- fromto
601 if (diff[i] >= 0) { 627 }
602 fromto <- c(fromto.RC[[i]][2], fromto.RC[[i]][1]) 628 }
603 fromto.RC[[i]] <- fromto 629 }
604 } 630
605 } 631
606 } 632 # findTMSPpeak function ----------------------------------------------
607 633 # If method == "tresh", findTMSPpeak will find the position of the first
608 634 # peak (from left or right) which is higher than a predefined threshold
609 # findTMSPpeak function ---------------------------------------------- 635 # and is computed as: c*(cumulated_mean/cumulated_sd)
610 # If method == "tresh", findTMSPpeak will find the position of the first 636 findTMSPpeak <- function(ft, c = 2, direction = "left") {
611 # peak (from left or right) which is higher than a predefined threshold 637 ft <- Re(ft) # extraction de la partie réelle
612 # and is computed as: c*(cumulated_mean/cumulated_sd) 638 N <- length(ft)
613 findTMSPpeak <- function(ft, c = 2, direction = "left") { 639 if (direction == "left") {
614 ft <- Re(ft) # extraction de la partie réelle 640 newindex <- rev(1:N)
615 N <- length(ft) 641 ft <- rev(ft)
616 if (direction == "left") { 642 }
617 newindex <- rev(1:N) 643 thres <- 99999
618 ft <- rev(ft) 644 i <- 1000 # Start at point 1000 to find the peak
619 } 645 vect <- ft[1:i]
620 thres <- 99999 646
621 i <- 1000 # Start at point 1000 to find the peak 647 while (vect[i] <= (c * thres)) {
622 vect <- ft[1:i] 648 cumsd <- stats::sd(vect)
623 649 cummean <- mean(vect)
624 while (vect[i] <= (c * thres)) { 650 thres <- cummean + 3 * cumsd
625 cumsd <- stats::sd(vect) 651 i <- i + 1
626 cummean <- mean(vect) 652 vect <- ft[1:i]
627 thres <- cummean + 3 * cumsd 653 }
628 i <- i + 1 654 if (direction == "left") {
629 vect <- ft[1:i] 655 v <- newindex[i]
630 } 656 } else {
631 if (direction == "left") { 657 v <- i
632 v <- newindex[i] 658 }
633 } else {v <- i} 659
634 660 if (is.na(v)) {
635 if (is.na(v)) { 661 warning("No peak found, need to lower the threshold.")
636 warning("No peak found, need to lower the threshold.") 662 return(NA)
637 return(NA) 663 } else {
638 } else { 664 # recherche dans les 1% de points suivants du max trouve pour etre au sommet du
639 # recherche dans les 1% de points suivants du max trouve pour etre au sommet du 665 # pic
640 # pic 666 d <- which.max(ft[v:(v + N * 0.01)])
641 d <- which.max(ft[v:(v + N * 0.01)]) 667 new.peak <- v + d - 1 # nouveau pic du TMSP si d > 0
642 new.peak <- v + d - 1 # nouveau pic du TMSP si d > 0 668
643 669 if (names(which.max(ft[v:(v + N * 0.01)])) != names(which.max(ft[v:(v + N * 0.03)]))) {
644 if (names(which.max(ft[v:(v + N * 0.01)])) != names(which.max(ft[v:(v + N * 0.03)]))) { 670 # recherche dans les 3% de points suivants du max trouve pour eviter un faux
645 # recherche dans les 3% de points suivants du max trouve pour eviter un faux 671 # positif
646 # positif 672 warning("the TMSP peak might be located further away, increase the threshold to check.")
647 warning("the TMSP peak might be located further away, increase the threshold to check.") 673 }
648 } 674 return(new.peak)
649 return(new.peak) 675 }
650 } 676 }
651 } 677
652 678
653 679 # Define the search zone ----------------------------------------
654 # Define the search zone ---------------------------------------- 680
655 681 n <- nrow(Spectrum_data)
656 n <- nrow(Spectrum_data) 682 m <- ncol(Spectrum_data)
657 m <- ncol(Spectrum_data) 683
658 684 # The Sweep Width (SW) has to be the same since the column names are the same
659 # The Sweep Width (SW) has to be the same since the column names are the same 685 SW <- Fid_info[1, "SW"] # Sweep Width in ppm
660 SW <- Fid_info[1, "SW"] # Sweep Width in ppm 686 ppmInterval <- SW / (m - 1) # size of a ppm interval
661 ppmInterval <- SW/(m-1) # size of a ppm interval 687
662 688 # range: How the search zone is defined ("all", "nearvalue" or "window")
663 # range: How the search zone is defined ("all", "nearvalue" or "window") 689 if (range == "all") {
664 if (range == "all") { 690 Data <- Spectrum_data
665 691 } else { # range = "nearvalue" or "window"
666 Data <- Spectrum_data 692 # Need to define colindex (column indexes) to apply indexInterval on it
667 693
668 } else { # range = "nearvalue" or "window" 694 if (range == "nearvalue") {
669 # Need to define colindex (column indexes) to apply indexInterval on it 695 fromto.RC <- list(c(-(SW * pc) / 2 + ppm.value, (SW * pc) / 2 + ppm.value)) # automatic fromto values in ppm
670 696 colindex <- as.numeric(colnames(Spectrum_data))
671 if (range == "nearvalue") { 697 } else {
672 698 # range == "window"
673 fromto.RC <- list(c(-(SW * pc)/2 + ppm.value, (SW * pc)/2 + ppm.value)) # automatic fromto values in ppm 699 # fromto.RC is already user-defined
674 colindex <- as.numeric(colnames(Spectrum_data)) 700 if (ppm.ir == TRUE) {
675 701 colindex <- as.numeric(colnames(Spectrum_data))
676 } else { 702 } else {
677 # range == "window" 703 colindex <- 1:m
678 # fromto.RC is already user-defined 704 }
679 if (ppm.ir == TRUE) { 705 }
680 colindex <- as.numeric(colnames(Spectrum_data)) 706
681 } else { 707 # index intervals taking into account the different elements in the list fromto.RC
682 colindex <- 1:m 708 Int <- vector("list", length(fromto.RC))
683 }
684 }
685
686 # index intervals taking into account the different elements in the list fromto.RC
687 Int <- vector("list", length(fromto.RC))
688 for (i in 1:length(fromto.RC)) {
689 Int[[i]] <- indexInterval(colindex, from = fromto.RC[[i]][1],
690 to = fromto.RC[[i]][2], inclusive = TRUE)
691 }
692
693 # define Data as the cropped spectrum including the index intervals
694 # outside the research zone, the intensities are set to the minimal
695 # intensity of the research zone
696
697 if (n > 1){
698 Data <- apply(Re(Spectrum_data[,unlist(Int)]),1, function(x) rep(min(x), m))
699 Data <- t(Data)
700 Data[,unlist(Int)] <- Re(Spectrum_data[,unlist(Int)])
701 } else {
702 Data <- rep(min(Re(Spectrum_data)) ,m)
703 Data[unlist(Int)] <- Re(Spectrum_data[unlist(Int)])
704 }
705
706 }
707
708
709 # Apply the peak location search method ('thres' or 'max') on spectra
710 # -----------------------------------------------------------------------
711
712 if (method == "thres") {
713 TMSPpeaks <- apply(Data, 1, findTMSPpeak, c = c, direction = direction)
714 } else { # method == "max
715 TMSPpeaks <- apply(Re(Data), 1, which.max)
716 }
717
718
719 # Shift spectra according to the TMSPpeaks found --------------------------------
720 # Depends on the shiftHandling
721
722 # TMSPpeaks is a column index
723 maxpeak <- max(TMSPpeaks) # max accross spectra
724 minpeak <- min(TMSPpeaks) # min accross spectra
725
726
727 if (shiftHandling %in% c("zerofilling", "NAfilling", "cut")) {
728 fill <- NA
729 if (shiftHandling == "zerofilling") {
730 fill <- 0
731 }
732
733 start <- maxpeak - 1
734 end <- minpeak - m
735
736 # ppm values of each interval for the whole spectral range of the spectral matrix
737 ppmScale <- (start:end) * ppmInterval
738
739 # check if ppm.value is in the ppmScale interval
740 if(ppm.value < min(ppmScale) | ppm.value > max(ppmScale)) {
741 warning("ppm.value = ", ppm.value, " is not in the ppm interval [",
742 round(min(ppmScale),2), ",", round(max(ppmScale),2), "], and is set to its default ppm.value 0")
743 ppm.value = 0
744 }
745
746 # if ppm.value != 0, ppmScale is adapted
747 ppmScale <- ppmScale + ppm.value
748
749 # create the spectral matrix with realigned spectra
750 Spectrum_data_calib <- matrix(fill, nrow = n, ncol = -(end - start) + 1,
751 dimnames = list(rownames(Spectrum_data), ppmScale))
752
753 # fills in Spectrum_data_calib with shifted spectra
754 for (i in 1:n) {
755 shift <- (1 - TMSPpeaks[i]) + start
756 Spectrum_data_calib[i, (1 + shift):(m + shift)] <- Spectrum_data[i, ]
757 }
758
759 if (shiftHandling == "cut") {
760 Spectrum_data_calib = as.matrix(stats::na.omit(t(Spectrum_data_calib)))
761 Spectrum_data_calib = t(Spectrum_data_calib)
762 base::attr(Spectrum_data_calib, "na.action") <- NULL
763 }
764
765
766 } else {
767 # circular
768 start <- 1 - maxpeak
769 end <- m - maxpeak
770
771 ppmScale <- (start:end) * ppmInterval
772
773 # check if ppm.value in is the ppmScale interval
774 if(ppm.value < min(ppmScale) | ppm.value > max(ppmScale)) {
775 warning("ppm.value = ", ppm.value, " is not in the ppm interval [",
776 round(min(ppmScale),2), ",", round(max(ppmScale),2), "], and is set to its default ppm.value 0")
777 ppm.value = 0
778 }
779
780 # if ppm.value != 0, ppmScale is adapted
781 ppmScale <- ppmScale + ppm.value
782
783 # create the spectral matrix with realigned spectra
784 Spectrum_data_calib <- matrix(nrow=n, ncol=end-start+1,
785 dimnames=list(rownames(Spectrum_data), ppmScale))
786
787 # fills in Spectrum_data_calib with shifted spectra
788 for (i in 1:n) {
789 shift <- (maxpeak-TMSPpeaks[i])
790 Spectrum_data_calib[i,(1+shift):m] <- Spectrum_data[i,1:(m-shift)]
791 if (shift > 0) {
792 Spectrum_data_calib[i,1:shift] <- Spectrum_data[i,(m-shift+1):m]
793 }
794 }
795 }
796
797
798
799
800 # Plot of the spectra (depending on rowindex_graph) ---------------------------------------------------
801
802 ppm = xstart = value = xend = Legend = NULL # only for R CMD check
803
804
805 # with the search zone for TMSP and the location of the peaks just found
806 if (!is.null(rowindex_graph)) {
807
808 if (range == "window") {
809 if (ppm.ir == TRUE) {
810 fromto <- fromto.RC
811 } else {
812 fromto <- list()
813 idcol <- as.numeric(colnames(Spectrum_data))
814 for (i in 1:length(fromto.RC)) { 709 for (i in 1:length(fromto.RC)) {
815 fromto[[i]] <- as.numeric(colnames(Spectrum_data))[fromto.RC[[i]]] 710 Int[[i]] <- indexInterval(colindex,
816 } 711 from = fromto.RC[[i]][1],
817 } 712 to = fromto.RC[[i]][2], inclusive = TRUE
818 } else { 713 )
819 fromto <- fromto.RC 714 }
820 } 715
821 716 # define Data as the cropped spectrum including the index intervals
822 # TMSPloc in ppm 717 # outside the research zone, the intensities are set to the minimal
823 TMSPloc <- as.numeric(colnames(Spectrum_data))[TMSPpeaks[rowindex_graph]] 718 # intensity of the research zone
824 719
825 # num plot per window 720 if (n > 1) {
826 num.stacked <- 6 721 Data <- apply(Re(Spectrum_data[, unlist(Int)]), 1, function(x) rep(min(x), m))
827 722 Data <- t(Data)
828 # rectanglar bands of color for the search zone 723 Data[, unlist(Int)] <- Re(Spectrum_data[, unlist(Int)])
829 rects <- data.frame(xstart = sapply(fromto, function(x) x[[1]]), 724 } else {
830 xend = sapply(fromto, function(x) x[[2]]), 725 Data <- rep(min(Re(Spectrum_data)), m)
831 Legend = "Peak search zone and location") 726 Data[unlist(Int)] <- Re(Spectrum_data[unlist(Int)])
832 727 }
833 # vlines for TMSP peak 728 }
834 addlines <- data.frame(rowname = rownames(Spectrum_data)[rowindex_graph],TMSPloc) 729
835 730
836 nn <- length(rowindex_graph) 731 # Apply the peak location search method ('thres' or 'max') on spectra
837 i <- 1 732 # -----------------------------------------------------------------------
838 j <- 1 733
839 plots <- vector(mode = "list", length = ceiling(nn/num.stacked)) 734 if (method == "thres") {
840 735 TMSPpeaks <- apply(Data, 1, findTMSPpeak, c = c, direction = direction)
841 while (i <= nn) { 736 } else { # method == "max
842 737 TMSPpeaks <- apply(Re(Data), 1, which.max)
843 last <- min(i + num.stacked - 1, nn) 738 }
844 739
845 melted <- reshape2::melt(Re(Spectrum_data[i:last, ]), 740
846 varnames = c("rowname", "ppm")) 741 # Shift spectra according to the TMSPpeaks found --------------------------------
847 742 # Depends on the shiftHandling
848 plots[[j]] <- ggplot2::ggplot() + ggplot2::theme_bw() + 743
849 ggplot2::geom_line(data = melted, 744 # TMSPpeaks is a column index
850 ggplot2::aes(x = ppm, y = value)) + 745 maxpeak <- max(TMSPpeaks) # max accross spectra
851 ggplot2::geom_rect(data = rects, ggplot2::aes(xmin = xstart, xmax = xend, 746 minpeak <- min(TMSPpeaks) # min accross spectra
852 ymin = -Inf, ymax = Inf, fill = Legend), alpha = 0.4) + 747
853 ggplot2::facet_grid(rowname ~ ., scales = "free_y") + 748
854 ggplot2::theme(legend.position = "none") + 749 if (shiftHandling %in% c("zerofilling", "NAfilling", "cut")) {
855 ggplot2::geom_vline(data = addlines, ggplot2::aes(xintercept = TMSPloc), 750 fill <- NA
856 color = "red", show.legend = TRUE) + 751 if (shiftHandling == "zerofilling") {
857 ggplot2::ggtitle("Peak search zone and location") + 752 fill <- 0
858 ggplot2::theme(legend.position = "top", legend.text = ggplot2::element_text()) 753 }
859 754
860 755 start <- maxpeak - 1
861 756 end <- minpeak - m
862 if ((melted[1, "ppm"] - melted[(dim(melted)[1]), "ppm"]) > 0) { 757
863 plots[[j]] <- plots[[j]] + ggplot2::scale_x_reverse() 758 # ppm values of each interval for the whole spectral range of the spectral matrix
864 } 759 ppmScale <- (start:end) * ppmInterval
865 760
866 i <- last + 1 761 # check if ppm.value is in the ppmScale interval
867 j <- j + 1 762 if (ppm.value < min(ppmScale) | ppm.value > max(ppmScale)) {
868 } 763 warning(
869 764 "ppm.value = ", ppm.value, " is not in the ppm interval [",
870 plots 765 round(min(ppmScale), 2), ",", round(max(ppmScale), 2), "], and is set to its default ppm.value 0"
871 } 766 )
872 767 ppm.value <- 0
873 768 }
874 # Return the results ---------------------------------------------- 769
875 Spectrum_data <- endTreatment("InternalReferencing", begin_info, Spectrum_data_calib) 770 # if ppm.value != 0, ppmScale is adapted
876 771 ppmScale <- ppmScale + ppm.value
877 if (is.null(plots)) { 772
878 return(Spectrum_data) 773 # create the spectral matrix with realigned spectra
879 } else { 774 Spectrum_data_calib <- matrix(fill,
880 return(list(Spectrum_data = Spectrum_data, plots = plots)) 775 nrow = n, ncol = -(end - start) + 1,
881 } 776 dimnames = list(rownames(Spectrum_data), ppmScale)
882 777 )
778
779 # fills in Spectrum_data_calib with shifted spectra
780 for (i in 1:n) {
781 shift <- (1 - TMSPpeaks[i]) + start
782 Spectrum_data_calib[i, (1 + shift):(m + shift)] <- Spectrum_data[i, ]
783 }
784
785 if (shiftHandling == "cut") {
786 Spectrum_data_calib <- as.matrix(stats::na.omit(t(Spectrum_data_calib)))
787 Spectrum_data_calib <- t(Spectrum_data_calib)
788 base::attr(Spectrum_data_calib, "na.action") <- NULL
789 }
790 } else {
791 # circular
792 start <- 1 - maxpeak
793 end <- m - maxpeak
794
795 ppmScale <- (start:end) * ppmInterval
796
797 # check if ppm.value in is the ppmScale interval
798 if (ppm.value < min(ppmScale) | ppm.value > max(ppmScale)) {
799 warning(
800 "ppm.value = ", ppm.value, " is not in the ppm interval [",
801 round(min(ppmScale), 2), ",", round(max(ppmScale), 2), "], and is set to its default ppm.value 0"
802 )
803 ppm.value <- 0
804 }
805
806 # if ppm.value != 0, ppmScale is adapted
807 ppmScale <- ppmScale + ppm.value
808
809 # create the spectral matrix with realigned spectra
810 Spectrum_data_calib <- matrix(
811 nrow = n, ncol = end - start + 1,
812 dimnames = list(rownames(Spectrum_data), ppmScale)
813 )
814
815 # fills in Spectrum_data_calib with shifted spectra
816 for (i in 1:n) {
817 shift <- (maxpeak - TMSPpeaks[i])
818 Spectrum_data_calib[i, (1 + shift):m] <- Spectrum_data[i, 1:(m - shift)]
819 if (shift > 0) {
820 Spectrum_data_calib[i, 1:shift] <- Spectrum_data[i, (m - shift + 1):m]
821 }
822 }
823 }
824
825
826
827
828 # Plot of the spectra (depending on rowindex_graph) ---------------------------------------------------
829
830 ppm <- xstart <- value <- xend <- Legend <- NULL # only for R CMD check
831
832
833 # with the search zone for TMSP and the location of the peaks just found
834 if (!is.null(rowindex_graph)) {
835 if (range == "window") {
836 if (ppm.ir == TRUE) {
837 fromto <- fromto.RC
838 } else {
839 fromto <- list()
840 idcol <- as.numeric(colnames(Spectrum_data))
841 for (i in 1:length(fromto.RC)) {
842 fromto[[i]] <- as.numeric(colnames(Spectrum_data))[fromto.RC[[i]]]
843 }
844 }
845 } else {
846 fromto <- fromto.RC
847 }
848
849 # TMSPloc in ppm
850 TMSPloc <- as.numeric(colnames(Spectrum_data))[TMSPpeaks[rowindex_graph]]
851
852 # num plot per window
853 num.stacked <- 6
854
855 # rectanglar bands of color for the search zone
856 rects <- data.frame(
857 xstart = sapply(fromto, function(x) x[[1]]),
858 xend = sapply(fromto, function(x) x[[2]]),
859 Legend = "Peak search zone and location"
860 )
861
862 # vlines for TMSP peak
863 addlines <- data.frame(rowname = rownames(Spectrum_data)[rowindex_graph], TMSPloc)
864
865 nn <- length(rowindex_graph)
866 i <- 1
867 j <- 1
868 plots <- vector(mode = "list", length = ceiling(nn / num.stacked))
869
870 while (i <= nn) {
871 last <- min(i + num.stacked - 1, nn)
872
873 melted <- reshape2::melt(Re(Spectrum_data[i:last, ]),
874 varnames = c("rowname", "ppm")
875 )
876
877 plots[[j]] <- ggplot2::ggplot() +
878 ggplot2::theme_bw() +
879 ggplot2::geom_line(
880 data = melted,
881 ggplot2::aes(x = ppm, y = value)
882 ) +
883 ggplot2::geom_rect(data = rects, ggplot2::aes(
884 xmin = xstart, xmax = xend,
885 ymin = -Inf, ymax = Inf, fill = Legend
886 ), alpha = 0.4) +
887 ggplot2::facet_grid(rowname ~ ., scales = "free_y") +
888 ggplot2::theme(legend.position = "none") +
889 ggplot2::geom_vline(
890 data = addlines, ggplot2::aes(xintercept = TMSPloc),
891 color = "red", show.legend = TRUE
892 ) +
893 ggplot2::ggtitle("Peak search zone and location") +
894 ggplot2::theme(legend.position = "top", legend.text = ggplot2::element_text())
895
896
897
898 if ((melted[1, "ppm"] - melted[(dim(melted)[1]), "ppm"]) > 0) {
899 plots[[j]] <- plots[[j]] + ggplot2::scale_x_reverse()
900 }
901
902 i <- last + 1
903 j <- j + 1
904 }
905
906 plots
907 }
908
909
910 # Return the results ----------------------------------------------
911 Spectrum_data <- endTreatment("InternalReferencing", begin_info, Spectrum_data_calib)
912
913 if (is.null(plots)) {
914 return(Spectrum_data)
915 } else {
916 return(list(Spectrum_data = Spectrum_data, plots = plots))
917 }
883 } 918 }
884 919
885 ## ==================================================== 920 ## ====================================================
886 # ZeroOrderPhaseCorrection 921 # ZeroOrderPhaseCorrection
887 ## ==================================================== 922 ## ====================================================
888 923
889 ZeroOrderPhaseCorrection <- function(Spectrum_data, type.zopc = c("rms", "manual", "max"), 924 ZeroOrderPhaseCorrection <- function(Spectrum_data, type.zopc = c("rms", "manual", "max"),
890 plot_rms = NULL, returnAngle = FALSE, createWindow = TRUE, 925 plot_rms = NULL, returnAngle = FALSE, createWindow = TRUE,
891 angle = NULL, plot_spectra = FALSE, 926 angle = NULL, plot_spectra = FALSE,
892 ppm.zopc = TRUE, exclude.zopc = list(c(5.1,4.5))) { 927 ppm.zopc = TRUE, exclude.zopc = list(c(5.1, 4.5))) {
893 928 # Data initialisation and checks ----------------------------------------------
894 929
895 # Data initialisation and checks ---------------------------------------------- 930 # Entry arguments definition:
896 931 # plot_rms : graph of rms criterion returnAngle : if TRUE, returns avector of
897 # Entry arguments definition: 932 # optimal angles createWindow : for plot_rms plots angle : If angle is not NULL,
898 # plot_rms : graph of rms criterion returnAngle : if TRUE, returns avector of 933 # spectra are rotated according to the angle vector values
899 # optimal angles createWindow : for plot_rms plots angle : If angle is not NULL, 934 # plot_spectra : if TRUE, plot rotated spectra
900 # spectra are rotated according to the angle vector values 935
901 # plot_spectra : if TRUE, plot rotated spectra 936
902 937
903 938 begin_info <- beginTreatment("ZeroOrderPhaseCorrection", Spectrum_data)
904 939 Spectrum_data <- begin_info[["Signal_data"]]
905 begin_info <- beginTreatment("ZeroOrderPhaseCorrection", Spectrum_data) 940 n <- nrow(Spectrum_data)
906 Spectrum_data <- begin_info[["Signal_data"]] 941 m <- ncol(Spectrum_data)
907 n <- nrow(Spectrum_data) 942
908 m <- ncol(Spectrum_data) 943 rnames <- rownames(Spectrum_data)
909 944
910 rnames <- rownames(Spectrum_data) 945 # Check input arguments
911 946 type.zopc <- match.arg(type.zopc)
912 # Check input arguments 947 checkArg(ppm.zopc, c("bool"))
913 type.zopc <- match.arg(type.zopc) 948 checkArg(unlist(exclude.zopc), c("num"), can.be.null = TRUE)
914 checkArg(ppm.zopc, c("bool")) 949
915 checkArg(unlist(exclude.zopc), c("num"), can.be.null = TRUE) 950
916 951 # type.zopc in c("max", "rms") -----------------------------------------
917 952 if (type.zopc %in% c("max", "rms")) {
918 # type.zopc in c("max", "rms") ----------------------------------------- 953 # angle is found by optimization
919 if (type.zopc %in% c("max", "rms")) { 954
920 # angle is found by optimization 955 # rms function to be optimised
921 956 rms <- function(ang, y, meth = c("max", "rms")) {
922 # rms function to be optimised 957 # if (debug_plot) { graphics::abline(v=ang, col='gray60') }
923 rms <- function(ang, y, meth = c("max", "rms")) { 958 roty <- y * exp(complex(real = 0, imaginary = ang)) # spectrum rotation
924 # if (debug_plot) { graphics::abline(v=ang, col='gray60') } 959 Rey <- Re(roty)
925 roty <- y * exp(complex(real = 0, imaginary = ang)) # spectrum rotation 960
926 Rey <- Re(roty) 961 if (meth == "rms") {
927 962 ReyPos <- Rey[Rey >= 0] # select positive intensities
928 if (meth == "rms") { 963 POSss <- sum((ReyPos)^2, na.rm = TRUE) # SS for positive intensities
929 ReyPos <- Rey[Rey >= 0] # select positive intensities 964 ss <- sum((Rey)^2, na.rm = TRUE) # SS for all intensities
930 POSss <- sum((ReyPos)^2, na.rm = TRUE) # SS for positive intensities 965 return(POSss / ss) # criterion : SS for positive values / SS for all intensities
931 ss <- sum((Rey)^2, na.rm = TRUE) # SS for all intensities 966 } else {
932 return(POSss/ss) # criterion : SS for positive values / SS for all intensities 967 maxi <- max(Rey, na.rm = TRUE)
933 } else { 968 return(maxi)
934 maxi <- max(Rey, na.rm = TRUE) 969 }
935 return(maxi) 970 }
936 } 971
937 } 972
938 973 # Define the interval where to search for (by defining Data)
939 974 if (is.null(exclude.zopc)) {
975 Data <- Spectrum_data
976 } else {
977 # if ppm.zopc == TRUE, then exclude.zopc is in the colnames values, else, in the column
978 # index
979 if (ppm.zopc == TRUE) {
980 colindex <- as.numeric(colnames(Spectrum_data))
981 } else {
982 colindex <- 1:m
983 }
984
985 # Second check for the argument exclude.zopc
986 diff <- diff(unlist(exclude.zopc))[1:length(diff(unlist(exclude.zopc))) %% 2 != 0]
987 for (i in 1:length(diff)) {
988 if (ppm.zopc == TRUE & diff[i] >= 0) {
989 stop(paste("Invalid region removal because from <= to in ppm.zopc"))
990 } else if (ppm.zopc == FALSE & diff[i] <= 0) {
991 stop(paste("Invalid region removal because from >= to in column index"))
992 }
993 }
994
995
996 Int <- vector("list", length(exclude.zopc))
997 for (i in 1:length(exclude.zopc)) {
998 Int[[i]] <- indexInterval(colindex,
999 from = exclude.zopc[[i]][1],
1000 to = exclude.zopc[[i]][2], inclusive = TRUE
1001 )
1002 }
1003
1004 vector <- rep(1, m)
1005 vector[unlist(Int)] <- 0
1006 if (n > 1) {
1007 Data <- sweep(Spectrum_data, MARGIN = 2, FUN = "*", vector) # Cropped_Spectrum
1008 } else {
1009 Data <- Spectrum_data * vector
1010 } # Cropped_Spectrum
1011 }
1012
1013
1014 # angles computation
1015 Angle <- c()
1016 for (k in 1:n)
1017 {
1018 # The function is rms is periodic (period 2pi) and it seems that there is a phase
1019 # x such that rms is unimodal (i.e. decreasing then increasing) on the interval
1020 # [x; x+2pi]. However, if we do the optimization for example on [x-pi; x+pi],
1021 # instead of being decreasing then increasing, it might be increasing then
1022 # decreasing in which case optimize, thinking it is a valley will have to choose
1023 # between the left or the right of this hill and if it chooses wrong, it will end
1024 # up at like x-pi while the minimum is close to x+pi.
1025
1026 # Supposing that rms is unimodal, the classical 1D unimodal optimization will
1027 # work in either [-pi;pi] or [0;2pi] (this is not easy to be convinced by that I
1028 # agree) and we can check which one it is simply by the following trick
1029
1030 f0 <- rms(0, Data[k, ], meth = type.zopc)
1031 fpi <- rms(pi, Data[k, ], meth = type.zopc)
1032 if (f0 < fpi) {
1033 interval <- c(-pi, pi)
1034 } else {
1035 interval <- c(0, 2 * pi)
1036 }
1037
1038 # graphs of rms criteria
1039 debug_plot <- F # rms should not plot anything now, only when called by optimize
1040 if (!is.null(plot_rms) && rnames[k] %in% plot_rms) {
1041 x <- seq(min(interval), max(interval), length.out = 100)
1042 y <- rep(1, 100)
1043 for (K in (1:100)) {
1044 y[K] <- rms(x[K], Data[k, ], meth = type.zopc)
1045 }
1046 if (createWindow == TRUE) {
1047 grDevices::dev.new(noRStudioGD = FALSE)
1048 }
1049 graphics::plot(x, y,
1050 main = paste(
1051 "Criterion maximization \n",
1052 rownames(Data)[k]
1053 ), ylim = c(0, 1.1),
1054 ylab = "positiveness criterion", xlab = "angle "
1055 )
1056 debug_plot <- T
1057 }
1058
1059 # Best angle
1060 best <- stats::optimize(rms,
1061 interval = interval, maximum = TRUE,
1062 y = Data[k, ], meth = type.zopc
1063 )
1064 ang <- best[["maximum"]]
1065
1066
1067 if (debug_plot) {
1068 graphics::abline(v = ang, col = "black")
1069 graphics::text(x = (ang + 0.1 * ang), y = (y[ang] - 0.1 * y[ang]), labels = round(ang, 3))
1070 }
1071
1072 # Spectrum rotation
1073 Spectrum_data[k, ] <- Spectrum_data[k, ] * exp(complex(real = 0, imaginary = ang))
1074 Angle <- c(Angle, ang)
1075 }
1076 } else {
1077 # type.zopc is "manual" -------------------------------------------------------
1078 # if Angle is already specified and no optimisation is needed
1079 Angle <- angle
1080
1081 if (!is.vector(angle)) {
1082 stop("angle is not a vector")
1083 }
1084
1085 if (!is.numeric(angle)) {
1086 stop("angle is not a numeric")
1087 }
1088
1089 if (length(angle) != n) {
1090 stop(paste("angle has length", length(angle), "and there are", n, "spectra to rotate."))
1091 }
1092 for (k in 1:n) {
1093 Spectrum_data[k, ] <- Spectrum_data[k, ] * exp(complex(real = 0, imaginary = -angle[k]))
1094 }
1095 }
1096
1097
1098 # Draw spectra
1099 if (plot_spectra == TRUE) {
1100 nn <- ceiling(n / 4)
1101 i <- 1
1102 for (k in 1:nn) {
1103 if (createWindow == TRUE) {
1104 grDevices::dev.new(noRStudioGD = FALSE)
1105 }
1106 graphics::par(mfrow = c(4, 2))
1107 while (i <= n) {
1108 last <- min(i + 4 - 1, n)
1109 graphics::plot(Re(Spectrum_data[i, ]),
1110 type = "l", ylab = "intensity",
1111 xlab = "Index", main = paste0(rownames(Spectrum_data)[i], " - Real part")
1112 )
1113 graphics::plot(Im(Spectrum_data[i, ]),
1114 type = "l", ylab = "intensity",
1115 xlab = "Index", main = paste0(rownames(Spectrum_data)[i], " - Imaginary part")
1116 )
1117 i <- i + 1
1118 }
1119 i <- last + 1
1120 }
1121 }
1122
1123
1124 # Data finalisation ----------------------------------------------
1125
1126 Spectrum_data <- endTreatment("ZeroOrderPhaseCorrection", begin_info, Spectrum_data)
1127 if (returnAngle) {
1128 return(list(Spectrum_data = Spectrum_data, Angle = Angle))
1129 } else {
1130 return(Spectrum_data)
1131 }
1132 }
1133
1134
1135 ## ====================================================
1136 # Baseline Correction
1137 ## ====================================================
1138 BaselineCorrection <- function(Spectrum_data, ptw.bc = TRUE, maxIter = 42,
1139 lambda.bc = 1e+07, p.bc = 0.05, eps = 1e-08,
1140 ppm.bc = TRUE, exclude.bc = list(c(5.1, 4.5)),
1141 returnBaseline = F) {
1142 # Data initialisation ----------------------------------------------
1143 begin_info <- beginTreatment("BaselineCorrection", Spectrum_data, force.real = T)
1144 Spectrum_data <- begin_info[["Signal_data"]]
1145 p <- p.bc
1146 lambda <- lambda.bc
1147 n <- dim(Spectrum_data)[1]
1148 m <- dim(Spectrum_data)[2]
1149
1150
1151 # Data check
1152 checkArg(ptw.bc, c("bool"))
1153 checkArg(maxIter, c("int", "pos"))
1154 checkArg(lambda, c("num", "pos0"))
1155 checkArg(p.bc, c("num", "pos0"))
1156 checkArg(eps, c("num", "pos0"))
1157 checkArg(returnBaseline, c("bool"))
1158 checkArg(ppm.bc, c("bool"))
1159 checkArg(unlist(exclude.bc), c("num"), can.be.null = TRUE)
1160
940 # Define the interval where to search for (by defining Data) 1161 # Define the interval where to search for (by defining Data)
941 if (is.null(exclude.zopc)) { 1162 if (is.null(exclude.bc)) {
942 Data <- Spectrum_data 1163 exclude_index <- NULL
943 } else { 1164 } else {
944 1165 # if ppm.bc == TRUE, then exclude.bc is in the colnames values, else, in the column
945 # if ppm.zopc == TRUE, then exclude.zopc is in the colnames values, else, in the column 1166 # index
946 # index 1167 if (ppm.bc == TRUE) {
947 if (ppm.zopc == TRUE) { 1168 colindex <- as.numeric(colnames(Spectrum_data))
948 colindex <- as.numeric(colnames(Spectrum_data)) 1169 } else {
949 } else { 1170 colindex <- 1:m
950 colindex <- 1:m 1171 }
951 } 1172
952 1173 Int <- vector("list", length(exclude.bc))
953 # Second check for the argument exclude.zopc 1174 for (i in 1:length(exclude.bc)) {
954 diff <- diff(unlist(exclude.zopc))[1:length(diff(unlist(exclude.zopc)))%%2 !=0] 1175 Int[[i]] <- indexInterval(colindex,
955 for (i in 1:length(diff)) { 1176 from = exclude.bc[[i]][1],
956 if (ppm.zopc == TRUE & diff[i] >= 0) { 1177 to = exclude.bc[[i]][2], inclusive = TRUE
957 stop(paste("Invalid region removal because from <= to in ppm.zopc")) 1178 )
958 } else if (ppm.zopc == FALSE & diff[i] <= 0) {stop(paste("Invalid region removal because from >= to in column index"))} 1179 }
959 } 1180 exclude_index <- unlist(Int)
960 1181 }
961 1182
962 Int <- vector("list", length(exclude.zopc)) 1183 # Baseline Correction implementation definition ----------------------
963 for (i in 1:length(exclude.zopc)) { 1184
964 Int[[i]] <- indexInterval(colindex, from = exclude.zopc[[i]][1], 1185 # 2 Ways: either use the function asysm from the ptw package or by
965 to = exclude.zopc[[i]][2], inclusive = TRUE) 1186 # built-in functions
966 } 1187 if (ptw.bc) {
967 1188 asysm <- ptw::asysm
968 vector <- rep(1, m) 1189 } else {
969 vector[unlist(Int)] <- 0 1190 difsmw <- function(y, lambda, w, d) {
970 if (n > 1) { 1191 # Weighted smoothing with a finite difference penalty cf Eilers, 2003.
971 Data <- sweep(Spectrum_data, MARGIN = 2, FUN = "*", vector) # Cropped_Spectrum 1192 # (A perfect smoother)
972 } else { 1193 # y: signal to be smoothed
973 Data <- Spectrum_data * vector 1194 # lambda: smoothing parameter
974 } # Cropped_Spectrum 1195 # w: weights (use0 zeros for missing values)
975 } 1196 # d: order of differences in penalty (generally 2)
976 1197 m <- length(y)
977 1198 W <- Matrix::Diagonal(x = w)
978 # angles computation 1199 E <- Matrix::Diagonal(m)
979 Angle <- c() 1200 D <- Matrix::diff(E, differences = d)
980 for (k in 1:n) 1201 C <- Matrix::chol(W + lambda * t(D) %*% D)
981 { 1202 x <- Matrix::solve(C, Matrix::solve(t(C), w * y))
982 # The function is rms is periodic (period 2pi) and it seems that there is a phase 1203 return(as.numeric(x))
983 # x such that rms is unimodal (i.e. decreasing then increasing) on the interval 1204 }
984 # [x; x+2pi]. However, if we do the optimization for example on [x-pi; x+pi], 1205 asysm <- function(y, lambda, p, eps, exclude_index) {
985 # instead of being decreasing then increasing, it might be increasing then 1206 # Baseline estimation with asymmetric least squares
986 # decreasing in which case optimize, thinking it is a valley will have to choose 1207 # y: signal
987 # between the left or the right of this hill and if it chooses wrong, it will end 1208 # lambda: smoothing parameter (generally 1e5 to 1e8)
988 # up at like x-pi while the minimum is close to x+pi. 1209 # p: asymmetry parameter (generally 0.001)
989 1210 # d: order of differences in penalty (generally 2)
990 # Supposing that rms is unimodal, the classical 1D unimodal optimization will 1211 # eps: 1e-8 in ptw package
991 # work in either [-pi;pi] or [0;2pi] (this is not easy to be convinced by that I 1212 m <- length(y)
992 # agree) and we can check which one it is simply by the following trick 1213 w <- rep(1, m)
993 1214 i <- 1
994 f0 <- rms(0, Data[k, ],meth = type.zopc) 1215 repeat {
995 fpi <- rms(pi, Data[k, ], meth = type.zopc) 1216 z <- difsmw(y, lambda, w, d = 2)
996 if (f0 < fpi) { 1217 w0 <- w
997 interval <- c(-pi, pi) 1218 p_vect <- rep((1 - p), m) # if y <= z + eps
998 } else { 1219 p_vect[y > z + eps | y < 0] <- p # if y > z + eps | y < 0
999 interval <- c(0, 2 * pi) 1220 if (!is.null(exclude_index)) {
1000 } 1221 p_vect[exclude_index] <- 0 # if exclude area
1001 1222 }
1002 # graphs of rms criteria 1223
1003 debug_plot <- F # rms should not plot anything now, only when called by optimize 1224 w <- p_vect
1004 if (!is.null(plot_rms) && rnames[k] %in% plot_rms) { 1225 # w <- p * (y > z + eps | y < 0) + (1 - p) * (y <= z + eps)
1005 x <- seq(min(interval), max(interval), length.out = 100) 1226
1006 y <- rep(1, 100) 1227 if (sum(abs(w - w0)) == 0) {
1007 for (K in (1:100)) { 1228 break
1008 y[K] <- rms(x[K], Data[k, ], meth = type.zopc) 1229 }
1009 } 1230 i <- i + 1
1010 if (createWindow == TRUE) { 1231 if (i > maxIter) {
1011 grDevices::dev.new(noRStudioGD = FALSE) 1232 warning("cannot find Baseline estimation in asysm")
1012 } 1233 break
1013 graphics::plot(x, y, main = paste("Criterion maximization \n", 1234 }
1014 rownames(Data)[k]), ylim = c(0, 1.1), 1235 }
1015 ylab = "positiveness criterion", xlab = "angle ") 1236 return(z)
1016 debug_plot <- T 1237 }
1017 } 1238 }
1018 1239
1019 # Best angle 1240 # Baseline estimation ----------------------------------------------
1020 best <- stats::optimize(rms, interval = interval, maximum = TRUE, 1241 Baseline <- matrix(NA, nrow = nrow(Spectrum_data), ncol = ncol(Spectrum_data))
1021 y = Data[k,], meth = type.zopc) 1242
1022 ang <- best[["maximum"]] 1243 # for (k in 1:n) {
1023 1244 # Baseline[k, ] <- asysm(y = Spectrum_data[k, ], lambda = lambda, p = p, eps = eps)
1024 1245
1025 if (debug_plot) { 1246 if (ptw.bc) {
1026 graphics::abline(v = ang, col = "black") 1247 Baseline <- apply(Spectrum_data, 1, asysm,
1027 graphics::text(x = (ang+0.1*ang), y = (y[ang]-0.1*y[ang]), labels = round(ang, 3)) 1248 lambda = lambda, p = p,
1028 } 1249 eps = eps
1029 1250 )
1030 # Spectrum rotation 1251 } else {
1031 Spectrum_data[k, ] <- Spectrum_data[k, ] * exp(complex(real = 0, imaginary = ang)) 1252 Baseline <- apply(Spectrum_data, 1, asysm,
1032 Angle <- c(Angle, ang) 1253 lambda = lambda, p = p,
1033 } 1254 eps = eps, exclude_index = exclude_index
1034 1255 )
1035 1256 }
1036 1257
1037 1258
1038 } else { 1259 Spectrum_data <- Spectrum_data - t(Baseline)
1039 # type.zopc is "manual" ------------------------------------------------------- 1260 # }
1040 # if Angle is already specified and no optimisation is needed 1261
1041 Angle <- angle 1262 # Data finalisation ----------------------------------------------
1042 1263 Spectrum_data <- endTreatment("BaselineCorrection", begin_info, Spectrum_data) # FIXME create removeImaginary filter ??
1043 if (!is.vector(angle)) { 1264
1044 stop("angle is not a vector") 1265 if (returnBaseline) {
1045 } 1266 return(list(Spectrum_data = Spectrum_data, Baseline = Baseline))
1046 1267 } else {
1047 if (!is.numeric(angle)) { 1268 return(Spectrum_data)
1048 stop("angle is not a numeric") 1269 }
1049 } 1270 }
1050 1271
1051 if (length(angle) != n) {
1052 stop(paste("angle has length", length(angle), "and there are", n, "spectra to rotate."))
1053 }
1054 for (k in 1:n) {
1055 Spectrum_data[k, ] <- Spectrum_data[k, ] * exp(complex(real = 0, imaginary = - angle[k]))
1056 }
1057 }
1058
1059
1060 # Draw spectra
1061 if (plot_spectra == TRUE) {
1062 nn <- ceiling(n/4)
1063 i <- 1
1064 for (k in 1:nn) {
1065 if (createWindow == TRUE) {
1066 grDevices::dev.new(noRStudioGD = FALSE)
1067 }
1068 graphics::par(mfrow = c(4, 2))
1069 while (i <= n) {
1070 last <- min(i + 4 - 1, n)
1071 graphics::plot(Re(Spectrum_data[i, ]), type = "l", ylab = "intensity",
1072 xlab = "Index", main = paste0(rownames(Spectrum_data)[i], " - Real part"))
1073 graphics::plot(Im(Spectrum_data[i, ]), type = "l", ylab = "intensity",
1074 xlab = "Index", main = paste0(rownames(Spectrum_data)[i], " - Imaginary part"))
1075 i <- i + 1
1076 }
1077 i <- last + 1
1078 }
1079 }
1080
1081
1082 # Data finalisation ----------------------------------------------
1083
1084 Spectrum_data <- endTreatment("ZeroOrderPhaseCorrection", begin_info, Spectrum_data)
1085 if (returnAngle) {
1086 return(list(Spectrum_data = Spectrum_data, Angle = Angle))
1087 } else {
1088 return(Spectrum_data)
1089 }
1090 }
1091 1272
1092 1273
1093 ## ==================================================== 1274 ## ====================================================
1094 # Baseline Correction 1275 # NegativeValuesZeroing
1095 ## ==================================================== 1276 ## ====================================================
1096 BaselineCorrection <- function(Spectrum_data, ptw.bc = TRUE, maxIter = 42,
1097 lambda.bc = 1e+07, p.bc = 0.05, eps = 1e-08,
1098 ppm.bc = TRUE, exclude.bc = list(c(5.1,4.5)),
1099 returnBaseline = F) {
1100
1101 # Data initialisation ----------------------------------------------
1102 begin_info <- beginTreatment("BaselineCorrection", Spectrum_data, force.real = T)
1103 Spectrum_data <- begin_info[["Signal_data"]]
1104 p <- p.bc
1105 lambda <- lambda.bc
1106 n <- dim(Spectrum_data)[1]
1107 m <- dim(Spectrum_data)[2]
1108
1109
1110 # Data check
1111 checkArg(ptw.bc, c("bool"))
1112 checkArg(maxIter, c("int", "pos"))
1113 checkArg(lambda, c("num", "pos0"))
1114 checkArg(p.bc, c("num", "pos0"))
1115 checkArg(eps, c("num", "pos0"))
1116 checkArg(returnBaseline, c("bool"))
1117 checkArg(ppm.bc, c("bool"))
1118 checkArg(unlist(exclude.bc), c("num"), can.be.null = TRUE)
1119
1120 # Define the interval where to search for (by defining Data)
1121 if (is.null(exclude.bc)) {
1122 exclude_index <- NULL
1123 } else {
1124 # if ppm.bc == TRUE, then exclude.bc is in the colnames values, else, in the column
1125 # index
1126 if (ppm.bc == TRUE) {
1127 colindex <- as.numeric(colnames(Spectrum_data))
1128 } else {
1129 colindex <- 1:m
1130 }
1131
1132 Int <- vector("list", length(exclude.bc))
1133 for (i in 1:length(exclude.bc)) {
1134 Int[[i]] <- indexInterval(colindex, from = exclude.bc[[i]][1],
1135 to = exclude.bc[[i]][2], inclusive = TRUE)
1136 }
1137 exclude_index <- unlist(Int)
1138 }
1139
1140 # Baseline Correction implementation definition ----------------------
1141
1142 # 2 Ways: either use the function asysm from the ptw package or by
1143 # built-in functions
1144 if (ptw.bc) {
1145 asysm <- ptw::asysm
1146 } else {
1147 difsmw <- function(y, lambda, w, d) {
1148 # Weighted smoothing with a finite difference penalty cf Eilers, 2003.
1149 # (A perfect smoother)
1150 # y: signal to be smoothed
1151 # lambda: smoothing parameter
1152 # w: weights (use0 zeros for missing values)
1153 # d: order of differences in penalty (generally 2)
1154 m <- length(y)
1155 W <- Matrix::Diagonal(x=w)
1156 E <- Matrix::Diagonal(m)
1157 D <- Matrix::diff(E, differences = d)
1158 C <- Matrix::chol(W + lambda * t(D) %*% D)
1159 x <- Matrix::solve(C, Matrix::solve(t(C), w * y))
1160 return(as.numeric(x))
1161
1162 }
1163 asysm <- function(y, lambda, p, eps, exclude_index) {
1164 # Baseline estimation with asymmetric least squares
1165 # y: signal
1166 # lambda: smoothing parameter (generally 1e5 to 1e8)
1167 # p: asymmetry parameter (generally 0.001)
1168 # d: order of differences in penalty (generally 2)
1169 # eps: 1e-8 in ptw package
1170 m <- length(y)
1171 w <- rep(1, m)
1172 i <- 1
1173 repeat {
1174 z <- difsmw(y, lambda, w, d = 2)
1175 w0 <- w
1176 p_vect <- rep((1-p), m) # if y <= z + eps
1177 p_vect[y > z + eps | y < 0] <- p # if y > z + eps | y < 0
1178 if(!is.null(exclude_index)){
1179 p_vect[exclude_index] <- 0 # if exclude area
1180 }
1181
1182 w <- p_vect
1183 # w <- p * (y > z + eps | y < 0) + (1 - p) * (y <= z + eps)
1184
1185 if (sum(abs(w - w0)) == 0) {
1186 break
1187 }
1188 i <- i + 1
1189 if (i > maxIter) {
1190 warning("cannot find Baseline estimation in asysm")
1191 break
1192 }
1193 }
1194 return(z)
1195 }
1196 }
1197
1198 # Baseline estimation ----------------------------------------------
1199 Baseline <- matrix(NA, nrow = nrow(Spectrum_data), ncol = ncol(Spectrum_data))
1200
1201 # for (k in 1:n) {
1202 # Baseline[k, ] <- asysm(y = Spectrum_data[k, ], lambda = lambda, p = p, eps = eps)
1203
1204 if (ptw.bc ){
1205 Baseline <- apply(Spectrum_data,1, asysm, lambda = lambda, p = p,
1206 eps = eps)
1207 }else {
1208 Baseline <- apply(Spectrum_data,1, asysm, lambda = lambda, p = p,
1209 eps = eps, exclude_index = exclude_index)
1210 }
1211
1212
1213 Spectrum_data <- Spectrum_data - t(Baseline)
1214 # }
1215
1216 # Data finalisation ----------------------------------------------
1217 Spectrum_data <- endTreatment("BaselineCorrection", begin_info, Spectrum_data) # FIXME create removeImaginary filter ??
1218
1219 if (returnBaseline) {
1220 return(list(Spectrum_data = Spectrum_data, Baseline = Baseline))
1221 } else {
1222 return(Spectrum_data)
1223 }
1224 }
1225
1226
1227
1228 ## ====================================================
1229 # NegativeValuesZeroing
1230 ## ====================================================
1231 1277
1232 NegativeValuesZeroing <- function(Spectrum_data) { 1278 NegativeValuesZeroing <- function(Spectrum_data) {
1233 # Data initialisation and checks ---------------------------------------------- 1279 # Data initialisation and checks ----------------------------------------------
1234 begin_info <- beginTreatment("NegativeValuesZeroing", Spectrum_data, force.real = T) 1280 begin_info <- beginTreatment("NegativeValuesZeroing", Spectrum_data, force.real = T)
1235 Spectrum_data <- begin_info[["Signal_data"]] 1281 Spectrum_data <- begin_info[["Signal_data"]]
1236 1282
1237 # NegativeValuesZeroing ---------------------------------------------- 1283 # NegativeValuesZeroing ----------------------------------------------
1238 Spectrum_data[Spectrum_data < 0] <- 0 1284 Spectrum_data[Spectrum_data < 0] <- 0
1239 1285
1240 # Data finalisation ---------------------------------------------- 1286 # Data finalisation ----------------------------------------------
1241 return(endTreatment("NegativeValuesZeroing", begin_info, Spectrum_data)) 1287 return(endTreatment("NegativeValuesZeroing", begin_info, Spectrum_data))
1242 } 1288 }
1243
1244