## ----setup, include=FALSE----------------------------------------------------- if (Sys.getenv("IN_PKGDOWN") == "" && ! interactive()) { knitr::opts_chunk$set(echo = FALSE, dev = "png", fig.retina = 1, dev.args = list(pointsize = 10)) } else if (knitr::opts_knit$get("rmarkdown.pandoc.to") %in% c("docx", "docx+styles")) { knitr::opts_chunk$set(echo = FALSE, dev = "png", dpi = 300, dev.args = list(pointsize = 10)) } else { knitr::opts_chunk$set(echo = FALSE, dev = "png", dpi = 300, dev.args = list(pointsize = 10)) } # figure plotting functions figure_letter <- function(letter) { old_usr <- par("usr") if (par("xlog")) old_usr[1:2] <- 10^old_usr[1:2] if (par("ylog")) old_usr[3:4] <- 10^old_usr[3:4] old_par <- par(mar=c(0.2,0.2,0.2,0.2)) plot.window(old_usr[1:2], old_usr[3:4]) mtext(letter, 3, -1, adj=0, font=2) par(old_par) plot.window(old_usr[1:2], old_usr[3:4]) } ## ----results = if (Sys.getenv("IN_PKGDOWN")=="" && !interactive()) "asis" else "hide", echo = FALSE---- cat("**Note:** A high-resolution version of this document is [available online](https://smith-group.github.io/fitnmr/articles/timeseries1d.html).") ## ----fid-directory, echo=TRUE------------------------------------------------- # directory with each subdirectory containing an NMRPipe formatted FID file (*.fid) and Bruker acqus file fid_dir <- system.file("extdata", "noesy1d", package = "fitnmr") #fid_dir <- "path/to/your/directory" ## ----read_fids---------------------------------------------------------------- # find all the fid files within the fid directory fid_files <- list.files(fid_dir, "*.fid", full.names = TRUE, recursive = TRUE) # order fid files fid_files <- fid_files[do.call(order, read.table(text=fid_files, sep="/"))] # read all the FIDs with FitNMR if they haven't already been read if (!"fid_list" %in% ls()) { fid_list <- suppressWarnings(lapply(fid_files, fitnmr::read_nmrpipe, complex_data=TRUE)) fid_list <- fid_list fid_mat <- sapply(fid_list, `[[`, "int") } # extract times from acqus files acqus_files <- sub("[^/]+.fid", "acqus", fid_files) acqus_lines <- lapply(acqus_files, readLines) date_lines <- sapply(acqus_lines, function(x) grep("^\\$\\$ [0-9]", x, value=TRUE)) date_text <- sub("^[^ ]+ ([^ ]+ [^ ]+ [^ ]+) .*$", "\\1", date_lines) seconds <- as.numeric(as.POSIXct(date_text)) seconds <- seconds - min(seconds) hours <- seconds/60/60 ## ----initial_ft, cache=TRUE--------------------------------------------------- # perform initial Fourier transform if (!"ft_list" %in% ls()) { ft_list <- lapply(seq_along(fid_list), function(i) { fid <- fid_list[[i]] fid_ft <- fitnmr::nmrpipe_ft(fid) }) ft_mat <- sapply(ft_list, `[[`, "int") } ## ----phase_opt, cache=TRUE---------------------------------------------------- # phase optimization functions nmrpipe_p1_frac <- function(fheader) { as.vector(sapply(1, function(j) { frac <- seq(0, 1, length.out=fheader["FTSIZE",j]+1) frac <- frac[-length(frac)] if (all(fheader[c("X1","XN"),j] != 0)) { frac <- frac[seq(fheader["X1",j], fheader["XN",j])] } frac })) } phase_fn <- function(int, p0=0, p1=0, p1_frac=0) { p0 <- p0*pi/180 # convert degrees to rad p1 <- p1*pi/180 # convert degrees to rad pvec <- exp(1i*(p0+p1*p1_frac)) Re(int*pvec) } rss_fn <- function(x, spec, spec_ref, p1_frac) { spec_diff <- phase_fn(spec, x[1], x[2], p1_frac)-spec_ref sum(Re(spec_diff)^2, na.rm=TRUE) } spec_xlim <- c(10,0) spec_ppm <- as.vector(ft_list[[1]]$ppm[[1]]) phase_initial <- c(156, 0) phase_p0_range <- c(-180, 180) phase_p1_range <- c(-10, 10) #*.0001 phase_ref_int <- ft_list[[1]]$int phase_ref_int[] <- 0 phase_ref_int[spec_ppm < spec_xlim[1] & spec_ppm > spec_xlim[2]] <- NA phase_p1_frac <- nmrpipe_p1_frac(ft_list[[1]]$fheader) if (! "phase_vals" %in% ls()) { phase_vals <- sapply(seq_along(ft_list), function(i) { optim(phase_initial, rss_fn, method="L-BFGS-B", lower=c(phase_p0_range[1], phase_p1_range[1]), upper=c(phase_p0_range[2], phase_p1_range[2]), spec=ft_list[[i]]$int, spec_ref=phase_ref_int, p1_frac=phase_p1_frac)$par }) } if (!"ftps_list" %in% ls()) { ftps_list <- lapply(seq_along(ft_list), function(i) { ft <- ft_list[[i]] ftps <- fitnmr::nmrpipe_ps(ft, p0=phase_vals[1,i], p1=phase_vals[2,i]) }) ftps_mat <- sapply(ftps_list, `[[`, "int") } ## ----phases, fig.cap="**Automated optimization of zero- and first-order phases.** The red to purple color scheme is used in all figures to indicate the data acquisition time."---- all_cols <- rainbow(ncol(phase_vals), end=0.80) old_par <- par(mar=c(1.5, 3.1, 0.5, 0.5), oma=c(1.6, 0, 0, 0), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(hours, phase_vals[1,], col=all_cols, pch=16, cex=0.75, xlab=NA, ylab="P0 (degrees)") points(hours, phase_vals[1,], type="l") plot(hours, phase_vals[2,], col=all_cols, pch=16, cex=0.75, xlab=NA, ylab="P1 (degrees)") points(hours, phase_vals[2,], type="l") #plot(hours, colSums(Re(ftps_mat)), col=all_cols, pch=16, cex=0.5, xlab=NA, ylab="Total Intensity") #points(hours, colSums(Re(ftps_mat)), type="l") title(xlab="Time (hours)", outer=TRUE, mgp=c(0.5, 0, 0)) ## ----solvent-filter, cache=TRUE----------------------------------------------- solvent_filter <- -dnorm(spec_ppm, fid_list[[1]]$fheader["CAR",], 0.5) solvent_filter <- solvent_filter-min(solvent_filter) solvent_filter <- solvent_filter/max(solvent_filter) if (!"ftpssf_list" %in% ls()) { ftpssf_list <- lapply(seq_along(ftps_list), function(i) { ftpssf <- ftps_list[[i]] ftpssf$int[] <- ftpssf$int[]*solvent_filter ftpssf }) ftpssf_mat <- sapply(ftpssf_list, `[[`, "int") } ## ----spec-phased, cache=TRUE, fig.height=6, fig.cap="**Spectra before (A) and after (B) solvent removal with Gaussian filter (black line).**"---- # positions of x-axis tick marks axis_integer <- seq(floor(min(spec_ppm)), ceiling(max(spec_ppm))) axis_tenths <- setdiff(seq(floor(min(spec_ppm)*10),ceiling(max(spec_ppm)*10))/10, axis_integer) spec_lwd <- 0.1 par(mar=c(3.1,3.1,0.5+0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(1, 1, type="n", xlim=spec_xlim, ylim=range(Re(ftpssf_mat)), xlab="1H (ppm)", ylab="Intensity", xaxt="n") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray") for (i in seq_len(ncol(ftpssf_mat))) { points(spec_ppm, Re(ftps_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } sf_plot_idx <- solvent_filter < 0.9 points(spec_ppm[sf_plot_idx], (solvent_filter[sf_plot_idx]-1)*abs(par("usr")[3]), type="l", lwd=0.5) figure_letter("A") plot(1, 1, type="n", xlim=spec_xlim, ylim=range(Re(ftpssf_mat)), xlab="1H (ppm)", ylab="Intensity", xaxt="n") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray") for (i in seq_len(ncol(ftpssf_mat))) { points(spec_ppm, Re(ftpssf_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } sf_plot_idx <- solvent_filter < 0.9 points(spec_ppm[sf_plot_idx], (solvent_filter[sf_plot_idx]-1)*abs(par("usr")[3]), type="l", lwd=0.5) figure_letter("B") ## ----inverse-fft, cache=FALSE------------------------------------------------- if (!"ftpssffti_list" %in% ls()) { ftpssffti_list <- lapply(seq_along(ftpssf_list), function(i) { ftpssf <- ftpssf_list[[i]] ftpssf$int[] <- ftpssf$int[]*solvent_filter ftpssffti <- fitnmr::nmrpipe_fti(ftpssf) ftpssffti }) ftpssffti_mat <- sapply(ftpssffti_list, `[[`, "int") } fid_seconds <- as.numeric(rownames(ftpssffti_mat)) ## ----solvent-filtered-fid, cache=TRUE, fig.height=6, fig.cap="**Time domain data before (A) and after (B) solvent filtering in the frequency domain.**"---- par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(1, 1, type="n", xlim=range(fid_seconds), ylim=max(abs(Re(sapply(fid_list, "[[", "int"))))*c(-1,1), xlab="Time (seconds)", ylab="Intensity") abline(h=0, v=0, col="gray") for (i in seq_along(fid_list)) { points(fid_seconds, Re(fid_list[[i]]$int), type="l", lwd=spec_lwd, col=all_cols[i]) } figure_letter("A") plot(1, 1, type="n", xlim=range(fid_seconds), ylim=max(abs(Re(ftpssffti_mat)))*c(-1,1), xlab="Time (seconds)", ylab="Intensity") abline(h=0, v=0, col="gray") for (i in seq_len(ncol(ftpssffti_mat))) { points(fid_seconds, Re(ftpssffti_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } figure_letter("B") ## ----fid-1-ratio-loess-calc, cache=TRUE--------------------------------------- fid_1_ratio_mat <- ftpssffti_mat/ftpssffti_mat[,1] fid_pre_idx <- seq_len(fid_list[[1]]$header["FDDMXVAL"]) loess_span <- 0.05 # get field inhomogeneity from first scan if (!"fid_1_ratio_loess_mat" %in% ls()) { fid_1_ratio_loess_mat <- sapply(seq_len(ncol(ftpssffti_mat)), function(i) { data_re <- data.frame(x=seq_len(nrow(fid_1_ratio_mat)), y=Re(fid_1_ratio_mat[,i])) # don't fit first part of FID data_re <- data_re[-fid_pre_idx,] loess_fit_re <- loess(y~x, data_re, span=loess_span) data_im <- data.frame(x=seq_len(nrow(fid_1_ratio_mat)), y=Im(fid_1_ratio_mat[,i])) # don't fit first part of FID data_im <- data_im[-fid_pre_idx,] loess_fit_im <- loess(y~x, data_im, span=loess_span) loess_fit_re$fitted + loess_fit_im$fitted*1i }) } fid_seconds_loess <- fid_seconds[-fid_pre_idx] ## ----fid-1-ratio-loess, cache=TRUE, fig.height=6, fig.cap="**Smoothed ratios of every FID to the first FID.** Real ratios indicate line broadening and imaginary ratios indicate frequency shifts relative to the first spectrum."---- par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(1, 1, type="n", xlim=range(fid_seconds), ylim=range(Re(fid_1_ratio_loess_mat)), xlab="Time (seconds)", ylab="Real Ratio") abline(h=c(0, 1), col="gray") for (i in seq_len(ncol(ftpssffti_mat))) { points(fid_seconds_loess, Re(fid_1_ratio_loess_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } plot(1, 1, type="n", xlim=range(fid_seconds), ylim=range(Im(fid_1_ratio_loess_mat)), xlab="Time (seconds)", ylab="Imaginary Ratio") abline(h=0, col="gray") for (i in seq_len(ncol(ftpssffti_mat))) { points(fid_seconds_loess, Im(fid_1_ratio_loess_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } ## ----fid-1-ratio-ft-calc, cache=TRUE------------------------------------------ if (!"fid_1_ratio_loess_ft" %in% ls()) { fid_1_ratio_loess_ft <- lapply(seq_len(ncol(fid_1_ratio_loess_mat)), function(i) { fid <- fid_list[[1]] fid$int[] <- 0 fid$int[-fid_pre_idx] <- fid_1_ratio_loess_mat[,i] fid$int[-fid_pre_idx][1] <- fid$int[-fid_pre_idx][1]*0.5 #fid$int[fid_pre_idx] <- fid_1_ratio_loess_mat[1,i] fid <- fitnmr::nmrpipe_sp(fid, off=0.5, pow=2, cval=0.5) fid_zf <- fitnmr::nmrpipe_zf(fid, 2) fid_zfft <- fitnmr::nmrpipe_ft(fid_zf) fid_zfft }) fid_1_ratio_loess_ft_mat <- sapply(fid_1_ratio_loess_ft, `[[`, "int") } if (!"fid_1_ratio_ft" %in% ls()) { fid_1_ratio_ft <- lapply(seq_len(ncol(fid_1_ratio_mat)), function(i) { fid <- fid_list[[1]] fid$int[] <- fid_1_ratio_mat[,i] fid <- fitnmr::nmrpipe_sp(fid, off=0.5, pow=2, cval=0.5) fid_zf <- fitnmr::nmrpipe_zf(fid, 2) fid_zfft <- fitnmr::nmrpipe_ft(fid_zf) fid_zfft }) fid_1_ratio_ft_mat <- sapply(fid_1_ratio_ft, `[[`, "int") } ## ----fid-1-ratio-ft, cache=TRUE, fig.height=6, fig.cap="**Smoothed (A) and original (B) FID ratios (individual/first) transformed into the frequency domain.** The expected result for no deviation from the first spectrum is indicated by the black line. It is just the Fourier transform of the cosine-squared window function."---- xlim <- c(1,-1)*.02 freq_ppm <- spec_ppm-fid_list[[1]]$fheader["CAR",] freq_ppm_2 <- as.numeric(rownames(fid_1_ratio_ft_mat))-fid_list[[1]]$fheader["CAR",] par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(1, 1, type="n", xlim=xlim, ylim=range(Re(fid_1_ratio_loess_ft_mat)), xlab="Frequency (ppm)", ylab="Intensity") abline(h=0, v=0, col="gray") for (i in seq_len(ncol(fid_1_ratio_loess_ft_mat))) { points(freq_ppm_2, Re(fid_1_ratio_loess_ft_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } points(freq_ppm_2, Re(fid_1_ratio_loess_ft_mat[,1]), type="l", lwd=spec_lwd*6)#, col=all_cols[i]) figure_letter("A") plot(1, 1, type="n", xlim=xlim, ylim=range(Re(fid_1_ratio_ft_mat)), xlab="Frequency (ppm)", ylab="Intensity") abline(h=0, v=0, col="gray") for (i in seq_len(ncol(fid_1_ratio_ft_mat))) { points(freq_ppm_2, Re(fid_1_ratio_ft_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } points(freq_ppm_2, Re(fid_1_ratio_ft_mat[,1]), type="l", lwd=spec_lwd*6)#, col=all_cols[i]) figure_letter("B") ## ----fid-mean-ratio-loess-calc, cache=TRUE------------------------------------ fid_mean_ratio_mat <- rowMeans(ftpssffti_mat)/ftpssffti_mat if (!"fid_mean_ratio_loess_mat" %in% ls()) { fid_mean_ratio_loess_mat <- sapply(seq_len(ncol(ftpssffti_mat)), function(i) { data_re <- data.frame(x=seq_len(nrow(fid_mean_ratio_mat)), y=Re(fid_mean_ratio_mat[,i])) # don't fit first part of FID data_re <- data_re[-fid_pre_idx,] loess_fit_re <- loess(y~x, data_re, span=loess_span) data_im <- data.frame(x=seq_len(nrow(fid_mean_ratio_mat)), y=Im(fid_mean_ratio_mat[,i])) # don't fit first part of FID data_im <- data_im[-fid_pre_idx,] loess_fit_im <- loess(y~x, data_im, span=loess_span) loess_fit_re$fitted + loess_fit_im$fitted*1i }) } ## ----fid-mean-ratio-loess, cache=TRUE, fig.height=6, fig.cap="**Smoothed ratio of mean FID intensities to each experimental FID.**"---- par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(1, 1, type="n", xlim=range(fid_seconds), ylim=range(Re(fid_1_ratio_loess_mat)), xlab="Time (seconds)", ylab="Real Ratio") abline(h=c(0, 1), col="gray") for (i in seq_len(ncol(ftpssffti_mat))) { points(fid_seconds_loess, Re(fid_mean_ratio_loess_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } plot(1, 1, type="n", xlim=range(fid_seconds), ylim=range(Im(fid_1_ratio_loess_mat)), xlab="Time (seconds)", ylab="Imaginary Ratio") abline(h=0, col="gray") for (i in seq_len(ncol(ftpssffti_mat))) { points(fid_seconds_loess, Im(fid_mean_ratio_loess_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } ## ----fid-mean-ratio-ft-calc, cache=TRUE--------------------------------------- if (!"fid_mean_ratio_loess_ft" %in% ls()) { fid_mean_ratio_loess_ft <- lapply(seq_len(ncol(fid_mean_ratio_loess_mat)), function(i) { fid <- fid_list[[1]] fid$int[] <- 0 fid$int[-fid_pre_idx] <- fid_mean_ratio_loess_mat[,i] fid$int[-fid_pre_idx][1] <- fid$int[-fid_pre_idx][1]*0.5 #fid$int[fid_pre_idx] <- fid_mean_ratio_loess_mat[1,i] fid <- fitnmr::nmrpipe_sp(fid, off=0.5, pow=2, cval=0.5) fid_zf <- fitnmr::nmrpipe_zf(fid, 1) fid_zfft <- fitnmr::nmrpipe_ft(fid_zf) fid_zfft }) fid_mean_ratio_loess_ft_mat <- sapply(fid_mean_ratio_loess_ft, `[[`, "int") } if (!"fid_mean_ratio_ft" %in% ls()) { fid_mean_ratio_ft <- lapply(seq_len(ncol(fid_mean_ratio_mat)), function(i) { fid <- fid_list[[1]] fid$int[] <- fid_mean_ratio_mat[,i] fid <- fitnmr::nmrpipe_sp(fid, off=0.5, pow=2, cval=0.5) fid_zf <- fitnmr::nmrpipe_zf(fid, 1) fid_zfft <- fitnmr::nmrpipe_ft(fid_zf) fid_zfft }) fid_mean_ratio_ft_mat <- sapply(fid_mean_ratio_ft, `[[`, "int") } ## ----fid-mean-ratio-ft, cache=TRUE, fig.height=6, fig.cap="**Smoothed (A) and original (B) FID ratios (mean/individual) transformed into the frequency domain.** High frequency components of ratios have been suppressed by multiplying a Gaussian function with standard deviation of 0.01 ppm (black line)."---- xlim <- c(1,-1)*.02 freq_ppm_1 <- as.numeric(rownames(fid_mean_ratio_ft_mat))-fid_list[[1]]$fheader["CAR",] freq_filter <- dnorm(freq_ppm_1, sd=0.01) freq_filter <- freq_filter/max(freq_filter) fid_mean_ratio_loess_filter_mat <- Re(fid_mean_ratio_loess_ft_mat) fid_mean_ratio_loess_filter_mat <- fid_mean_ratio_loess_filter_mat*freq_filter fid_mean_ratio_loess_filter_mat <- t(t(fid_mean_ratio_loess_filter_mat)/colSums(fid_mean_ratio_loess_filter_mat)) fid_mean_ratio_filter_mat <- Re(fid_mean_ratio_ft_mat) fid_mean_ratio_filter_mat <- fid_mean_ratio_filter_mat*freq_filter fid_mean_ratio_filter_mat <- t(t(fid_mean_ratio_filter_mat)/colSums(fid_mean_ratio_filter_mat)) par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) ylim <- range(Re(fid_mean_ratio_loess_filter_mat)) plot(1, 1, type="n", xlim=xlim, ylim=ylim, xlab="Frequency (ppm)", ylab="Intensity") abline(h=0, v=0, col="gray") for (i in seq_len(ncol(fid_mean_ratio_loess_filter_mat))) { points(freq_ppm_1, Re(fid_mean_ratio_loess_filter_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } #points(freq_ppm_1, Re(fid_mean_ratio_loess_filter_mat[,1]), type="l", lwd=spec_lwd*6) points(freq_ppm_1, 0.75*freq_filter*(-par("usr")[3])+par("usr")[3], type="l") figure_letter("A") ylim <- range(Re(range(Re(fid_mean_ratio_filter_mat)))) plot(1, 1, type="n", xlim=xlim, ylim=ylim, xlab="Frequency (ppm)", ylab="Intensity") abline(h=0, v=0, col="gray") for (i in seq_len(ncol(fid_mean_ratio_filter_mat))) { points(freq_ppm_1, Re(fid_mean_ratio_filter_mat[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } #points(freq_ppm_1, Re(fid_mean_ratio_filter_mat[,1]), type="l", lwd=spec_lwd*6) points(freq_ppm_1, 0.75*freq_filter*(-par("usr")[3])+par("usr")[3], type="l") figure_letter("B") ## ----unfiltered-filtered-calc, cache=TRUE------------------------------------- if (!"filtered_list" %in% ls()) { filtered_list <- lapply(seq_along(fid_list), function(i) { sig_fid <- ftpssffti_list[[i]] sig_fid <- fitnmr::nmrpipe_zf(sig_fid) sig_ft <- fitnmr::nmrpipe_ft(sig_fid) sig_ft$int[] <- Re(sig_ft$int) filt_ft <- sig_ft filt_ft$int[] <- fid_mean_ratio_loess_filter_mat[,i] #filt_ft$int[] <- fid_mean_ratio_filter_mat[,i] sig_fid <- fitnmr::nmrpipe_fti(sig_ft) filt_fid <- fitnmr::nmrpipe_fti(filt_ft) out_fid <- sig_fid out_fid$int[] <- out_fid$int*filt_fid$int out_ft <- fitnmr::nmrpipe_ft(out_fid) }) filtered_mat <- sapply(filtered_list, `[[`, "int") } if (!"unfiltered_list" %in% ls()) { unfiltered_list <- lapply(seq_along(fid_list), function(i) { sig_fid <- ftpssffti_list[[i]] sig_fid <- fitnmr::nmrpipe_sp(sig_fid, off=0.5, pow=2, cval=0.5) sig_fid <- fitnmr::nmrpipe_zf(sig_fid) sig_ft <- fitnmr::nmrpipe_ft(sig_fid) sig_ft$int[] <- Re(sig_ft$int) #filt_ft <- sig_ft #filt_ft$int[] <- fid_mean_ratio_loess_filter_mat[,i] #filt_ft$int[] <- fid_mean_ratio_filter_mat[,i] sig_fid <- fitnmr::nmrpipe_fti(sig_ft) #filt_fid <- fitnmr::nmrpipe_fti(filt_ft) out_fid <- sig_fid #out_fid$int[] <- out_fid$int*filt_fid$int out_ft <- fitnmr::nmrpipe_ft(out_fid) }) unfiltered_mat <- sapply(unfiltered_list, `[[`, "int") } filter_mean_freq <- colSums(fid_mean_ratio_loess_filter_mat*freq_ppm_1) ## ----filter-data, cache=TRUE, fig.cap="**Data from the calculated FID filter. A)** Frequency shift induced by the filter calculated from the weighted mean frequency in Figure \\@ref(fig:fid-mean-ratio-ft)A. **B)** Variation in the total intensity of the spectrum after applying the filter."---- par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(hours, filter_mean_freq, col=all_cols, pch=16, cex=0.75, xlab="Time (hours)", ylab="Filter Frequency (ppm) ") abline(h=0, col="gray") points(hours, filter_mean_freq, type="l") figure_letter("A") plot(hours, colSums(Re(filtered_mat)), col=all_cols, pch=16, cex=0.75, xlab="Time (hours)", ylab="Total Intensity") points(hours, colSums(Re(filtered_mat)), type="l") figure_letter("B") ## ----unfiltered-filtered, cache=TRUE, fig.height=6, fig.cap="**Spectra before (A) and after (B) filtering with smoothed FID ratios (mean/individual).** Both sets of spectra have had total intensity normalization applied."---- filtered_mat_norm <- t(t(filtered_mat)*Re(mean(filtered_mat)/colMeans(filtered_mat))) unfiltered_mat_norm <- t(t(unfiltered_mat)*Re(mean(unfiltered_mat)/colMeans(unfiltered_mat))) spec_ppm_1 <- as.vector(filtered_list[[1]]$ppm[[1]]) solvent_filter_1 <- -dnorm(spec_ppm_1, fid_list[[1]]$fheader["CAR",], 0.5) solvent_filter_1 <- solvent_filter_1-min(solvent_filter_1) solvent_filter_1 <- solvent_filter_1/max(solvent_filter_1) spec_xlim <- c(3.25,2.85) spec_ylim <- range(Re(unfiltered_mat_norm)[spec_ppm_1 < spec_xlim[1] & spec_ppm_1 > spec_xlim[2],]) par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2, 1), cex=1) plot(1, 1, type="n", xlim=spec_xlim, ylim=spec_ylim, xlab="1H (ppm)", ylab="Intensity")#, xaxt="n") #axis(1, axis_integer, TRUE) #axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray") for (i in seq_len(ncol(unfiltered_mat_norm))) { points(spec_ppm_1, Re(unfiltered_mat_norm[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } sf_plot_idx <- solvent_filter_1 < 0.9 points(spec_ppm_1[sf_plot_idx], (solvent_filter_1[sf_plot_idx]-1)*abs(par("usr")[3]), type="l", lwd=0.5) figure_letter("A") spec_ylim <- range(Re(filtered_mat_norm)[spec_ppm_1 < spec_xlim[1] & spec_ppm_1 > spec_xlim[2],]) plot(1, 1, type="n", xlim=spec_xlim, ylim=spec_ylim, xlab="1H (ppm)", ylab="Intensity")#, xaxt="n") #axis(1, axis_integer, TRUE) #axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray") for (i in seq_len(ncol(filtered_mat_norm))) { points(spec_ppm_1, Re(filtered_mat_norm[,i]), type="l", lwd=spec_lwd, col=all_cols[i]) } sf_plot_idx <- solvent_filter_1 < 0.9 points(spec_ppm_1[sf_plot_idx], (solvent_filter_1[sf_plot_idx]-1)*abs(par("usr")[3]), type="l", lwd=0.5) figure_letter("B") ## ----exclude-regions-matrix, echo=TRUE---------------------------------------- # consecutive pairs of numbers give starting and stopping ppm values to exclude exclude_ppm <- matrix(c(12.4, 10, 0, -3.7), nrow=2) exclude_ppm ## ----exclude-regions, fig.cap="**Spectral regions excluded from fitting.** Excluded regions are shown in gray between the boundaries shown in red. The y-axis limits are determined by either the whole spectrum **(A)** or just the non-excluded regions **(B)**. In this case both limits are the same."---- ppm <- spec_ppm_1 int <- Re(filtered_mat_norm[,1]) exclude_idx <- logical(length(ppm)) for (i in seq_len(ncol(exclude_ppm))) { exclude_idx[ppm < max(exclude_ppm[,i]) & ppm > min(exclude_ppm[,i])] <- TRUE } int_excluded <- int int_excluded[!exclude_idx] <- NA int[exclude_idx] <- NA # line width for all spectra spec_lwd <- 0.33 # x-axis limits xlim <- rev(range(ppm, exclude_ppm)) par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(2,1), cex=1) plot(ppm, int, type="l", lwd=spec_lwd, xlim=xlim, ylim=range(int, int_excluded, na.rm=TRUE), xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab=NA) axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) points(ppm, int_excluded, type="l", lwd=spec_lwd, col="gray", xlab=expression(phantom()^1*H ~ (ppm)), ylab=NA) abline(v=exclude_ppm, col="red", lwd=0.25) figure_letter("A") plot(ppm, int, type="l", lwd=spec_lwd, xlim=xlim, xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab=NA) axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) points(ppm, int_excluded, type="l", lwd=spec_lwd, col="gray", xlab=expression(phantom()^1*H ~ (ppm)), ylab=NA) abline(v=exclude_ppm, col="red", lwd=0.25) figure_letter("B") ## ----two-state-fitting, fig.height=8------------------------------------------ #### Spectrum model fitting utility functions #### # convert a list of parameters to a vector (pop: a vector of state A populations, int: a list of two vectors with spectrum intensities) pack_int_param <- function(param_list) { int_mat <- sapply(param_list$int, function(x) x[!is.na(x)]) c(param_list$pop, int_mat) } # convert a vector of parameters to a list (pop: a vector of state A populations, int: a list of two vectors with spectrum intensities) unpack_int_param <- function(param_vec, template) { template$pop <- param_vec[seq_along(template$pop)] vec_offset <- length(template$pop) not_na_idx <- !is.na(template$int[[1]]) template$int[[1]][not_na_idx] <- param_vec[vec_offset+seq_len(sum(not_na_idx))] vec_offset <- vec_offset+sum(not_na_idx) not_na_idx <- !is.na(template$int[[2]]) template$int[[2]][not_na_idx] <- param_vec[vec_offset+seq_len(sum(not_na_idx))] template } # calculate the sum of squared differences between model and data lsq_fn <- function(par, template, spec_int_mat) { param_list <- unpack_int_param(par, template) i1 <- param_list$int[[1]] i2 <- param_list$int[[2]] p <- param_list$pop sim_int_mat <- i1 %o% p + i2 %o% (1-p) resid_mat <- spec_int_mat - sim_int_mat sum(resid_mat^2) } # calculate gradient of the sum of squared differences between model and data lsq_gr <- function(par, template, spec_int_mat) { param_list <- unpack_int_param(par, template) i1 <- param_list$int[[1]] i2 <- param_list$int[[2]] p <- param_list$pop sim_int_mat <- i1 %o% p + i2 %o% (1-p) resid_mat <- spec_int_mat - sim_int_mat deriv_list <- param_list # D[(d - (p*i1 + (1 - p)*i2))^2, {{p, i1, i2}}] deriv_list$pop <- colSums(2*(i2-i1)*resid_mat) deriv_list$int[[1]] <- colSums(-2*p*t(resid_mat)) deriv_list$int[[2]] <- colSums(-2*(1-p)*t(resid_mat)) pack_int_param(deriv_list) } #### Make Spectrum Fitting Input #### # create matrix of non-excluded spectrum intensities spec_int_mat <- Re(filtered_mat_norm[!exclude_idx,]) # normalize spectrum intensities so that the maximum intensity is 1 spec_int_mat <- spec_int_mat/max(abs(spec_int_mat)) fit_spec <- function(spec_int_mat, seconds) { # starting parameters in list (unpacked) format par_start_list <- list( pop = seq(1, 0, length.out=ncol(spec_int_mat)), int = list(spec_int_mat[,1], spec_int_mat[,ncol(spec_int_mat)]) ) # starting parameters in vector format par_start <- pack_int_param(par_start_list) # lower limits (A populations greater than or equal to 0) lower_list <- par_start_list lower_list$pop[] <- 0 lower_list$int[[1]][] <- -Inf lower_list$int[[2]][] <- -Inf lower <- pack_int_param(lower_list) # upper limits (A populations less than or equal to 0) upper_list <- par_start_list upper_list$pop[] <- 1 upper_list$int[[1]][] <- Inf upper_list$int[[2]][] <- Inf upper <- pack_int_param(upper_list) # optimize basis spectra and populations simultaneously optim_out <- optim(par_start, lsq_fn, lsq_gr, template=par_start_list, spec_int_mat=spec_int_mat, method="L-BFGS-B", lower=lower, upper=upper) # get list of optimized parameters par_optim_list <- unpack_int_param(optim_out$par, par_start_list) #### Analyze residuals #### sim_int_mat <- par_optim_list$int[[1]] %o% par_optim_list$pop + par_optim_list$int[[1]] %o% (1-par_optim_list$pop) resid_mat <- spec_int_mat-sim_int_mat #### Fit exponential decay #### # data for exponential decay fitting rate_data <- data.frame(seconds=seconds, pop=par_optim_list$pop) # formula for exponential decay exp_form <- pop ~ pop1*exp(-r1*seconds) + popf rate_starts <- exp(seq(log(1/rate_data$seconds[2]), log(1/max(rate_data$seconds)), length.out=3)) # fit the exponential decay rate_fit <- try(minpack.lm::nlsLM(exp_form, rate_data, c(popf=max(rate_data$pop), pop1=diff(range(rate_data$pop)), r1=rate_starts[2]), algorithm="port", lower=c(popf=-Inf, pop1=0, r1=0)), silent=TRUE) if (class(rate_fit) != "try-error") { # formula for multiexponential decay exp_form <- pop ~ pop1*exp(-r1*seconds) + pop2*exp(-r2*seconds) + popf rate_starts <- exp(seq(log(1/rate_data$seconds[2]), log(1/max(rate_data$seconds)), length.out=4)) rate_starts[2:3] <- rate_fit$m$getPars()["r1"]*c(2, 0.5) # fit the multiexponential decay fit_start <- c(popf=max(rate_data$pop), pop1=diff(range(rate_data$pop))/2, pop2=diff(range(rate_data$pop))/2, r1=rate_starts[2], r2=rate_starts[3]) fit_lower <- c(popf=-Inf, pop1=0, pop2=0, r1=unname(rate_fit$m$getPars()["r1"]), r2=0) fit_upper <- c(popf=Inf, pop1=Inf, pop2=Inf, r1=1/mean(diff(seconds)), r2=unname(rate_fit$m$getPars()["r1"])) rate_fit_multiexp <- try(minpack.lm::nlsLM(exp_form, rate_data, fit_start, algorithm="port", lower=fit_lower, upper=fit_upper), silent=TRUE) } else { rate_fit_multiexp <- rate_fit } list(par_optim_list=par_optim_list, rate_data=rate_data, rate_fit=rate_fit, rate_fit_multiexp=rate_fit_multiexp) } fit_spec_out <- fit_spec(spec_int_mat, seconds) par_optim_list <- fit_spec_out$par_optim_list rate_data <- fit_spec_out$rate_data rate_fit <- fit_spec_out$rate_fit #print(summary(rate_fit)) #### Plot results from fitting #### # x-axis limits xlim <- rev(range(ppm[!is.na(int)])) # positions of x-axis tick marks axis_integer <- seq(floor(min(xlim)), ceiling(max(xlim))) axis_tenths <- setdiff(seq(floor(min(xlim)*10),ceiling(max(xlim)*10))/10, axis_integer) # compute spectrum using the initial population fit by the exponential decay spec0 <- par_optim_list$int[[1]] if (class(rate_fit) != "try-error") { spec0 <- par_optim_list$int[[1]] * sum(rate_fit$m$getPars()[c("popf","pop1")]) + par_optim_list$int[[2]] * (1-sum(rate_fit$m$getPars()[c("popf","pop1")])) } # compute spectrum using the final population fit by the exponential decay specf <- par_optim_list$int[[2]] if (class(rate_fit) != "try-error") { specf <- par_optim_list$int[[1]] * rate_fit$m$getPars()["popf"] + par_optim_list$int[[2]] * (1-rate_fit$m$getPars()["popf"]) } # create a vector to map from the excluded intensities full_idx <- rep(NA_integer_, length(int)) full_idx[!exclude_idx] <- seq_along(spec0) if (FALSE) { par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(4,1), cex=1) plot(ppm, spec_int_mat[full_idx,1], type="l", lwd=spec_lwd, xlim=xlim, ylim=range(spec_int_mat), col="blue", xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Intensity") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) points(ppm, spec_int_mat[full_idx,ncol(spec_int_mat)], type="l", lwd=spec_lwd, col="purple") abline(h=0, col="gray", lwd=0.1) legend("topright", legend=c("First Spectrum", "Last Spectrum"), col=c("blue", "purple"), lwd=spec_lwd, bty="n") plot(ppm, spec0[full_idx], type="l", lwd=spec_lwd, xlim=xlim, ylim=range(spec_int_mat), col="blue", xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Intensity") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) points(ppm, specf[full_idx], type="l", lwd=spec_lwd, col="red") abline(h=0, col="gray", lwd=0.1) legend("topright", legend=c("Modeled Initial Spectrum", "Modeled Final Spectrum"), col=c("blue", "red"), lwd=spec_lwd, bty="n") plot(ppm, specf[full_idx]-spec0[full_idx], type="l", lwd=spec_lwd, xlim=xlim, ylim=c(-0.1, 0.1)*5, xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Delta Intensity") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray", lwd=0.1) if (FALSE) { plot(ppm, apply(resid_mat, 1, median)[full_idx], type="l", lwd=spec_lwd, xlim=xlim, col="black", xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Median Residual") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray") } ylim <- range(rate_data$pop) if (class(rate_fit) != "try-error") { ylim <- range(rate_data$pop, rate_fit$m$getPars()[1:2]) } plot(rate_data, pch=16, ylim=ylim, xlab="Seconds", ylab="Fit Population A") if (class(rate_fit) != "try-error") { points(rate_data$seconds, rate_fit$m$predict(), type="l", col="purple") abline(h=sum(summary(rate_fit)$coef[c("popf","pop1"),"Estimate"]), col="blue") # didn't do any propagation of uncertainty here... abline(h=sum(summary(rate_fit)$coef[c("popf","pop1"),"Estimate"])+c(-1,1)*summary(rate_fit)$coef["pop1","Std. Error"], col="blue", lty="dashed") abline(h=summary(rate_fit)$coef["popf","Estimate"], col="red") abline(h=summary(rate_fit)$coef["popf","Estimate"]+c(-1,1)*summary(rate_fit)$coef["popf","Std. Error"], col="red", lty="dashed") } if (class(rate_fit) != "try-error") { legend("topright", legend=parse(text=sprintf("\"k = %0.2e\" ~ s^{-1}", rate_fit$m$getPars()[3])), col=c("purple"), lwd=spec_lwd, bty="n", title=" ") } } ## ----update-populations, fig.height=8, fig.cap="**NMR spectra time series fit to a two-state model and then exponential decay.** **A)** First and last spectra in the time series. **B)** Modeled initial and final spectra. **C)** Differences in intensity between the modeled initial and final spectra. **D)** Expoenential fit to the population A component of the two-state fit."---- pop_rss <- function(pop_A, spec, spec_A, spec_B) { #print(pop_A) sum(((pop_A*spec_A + (1-pop_A)*spec_B) - spec)^2) } pop_A_updated <- apply(spec_int_mat, 2, function(spec) { optimize(pop_rss, c(-1, 2), spec, spec0, specf)$minimum }) exp_form <- pop ~ pop1*exp(-r1*seconds) + popf exp_form_simp <- pop ~ pop1*exp(-r1*seconds) + popf rate_data_updated <- rate_data rate_data_updated$pop <- pop_A_updated rate_fit_updated <- minpack.lm::nlsLM(exp_form_simp, rate_data_updated, c(popf=max(rate_data_updated$pop), pop1=diff(range(rate_data_updated$pop)), r1=1/max(rate_data$seconds)), algorithm="port", lower=c(popf=-Inf, pop1=0, r1=0)) #rate_fit_updated <- nls(exp_form, rate_data_updated, c(popf=max(rate_data_updated$pop), pop1=diff(range(rate_data_updated$pop)), r1=(1/max(rate_data$seconds))))#, algorithm="port", lower=c(popf=-Inf, pop1=0, r1=0)) # unname(rate_fit$m$getPars()["r1"]) #stop() par(mar=c(3.1,3.1,0.5,0.5), mgp=c(1.9, 0.6, 0), mfrow=c(4,1), cex=1) plot(ppm, spec_int_mat[full_idx,1], type="l", lwd=spec_lwd, xlim=xlim, ylim=range(spec_int_mat), col="firebrick", xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Intensity", yaxt="n") axis(2, seq(0, 1, by=0.5), tick=FALSE) axis(2, seq(0, 1, by=0.25), label=FALSE) axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) points(ppm, spec_int_mat[full_idx,ncol(spec_int_mat)], type="l", lwd=spec_lwd, col="darkslateblue") abline(h=0, col="gray", lwd=0.1) legend("topleft", legend=c("First Spectrum", "Last Spectrum"), col=c("firebrick", "darkslateblue"), lwd=spec_lwd, bty="n") figure_letter("A") plot(ppm, spec0[full_idx], type="l", lwd=spec_lwd, xlim=xlim, ylim=range(spec_int_mat), col="red", xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Intensity", yaxt="n") axis(2, seq(0, 1, by=0.5), tick=FALSE) axis(2, seq(0, 1, by=0.25), label=FALSE) axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) points(ppm, specf[full_idx], type="l", lwd=spec_lwd, col="mediumslateblue") abline(h=0, col="gray", lwd=0.1) legend("topleft", legend=c("Modeled Initial Spectrum (Population A)", "Modeled Final Spectrum (Population B)"), col=c("red", "mediumslateblue"), lwd=spec_lwd, bty="n") figure_letter("B") plot(1, 1, type="n", xlim=xlim, ylim=max(abs(specf[full_idx]-spec0[full_idx]), na.rm=TRUE)*c(-1,1), xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Delta Intensity", yaxt="n") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) #axis(2, seq(-1, 1, by=0.5), tick=FALSE) axis(2, seq(-1, 1, by=0.25), label=TRUE) abline(h=0, col="gray", lwd=0.1) points(ppm, specf[full_idx]-spec0[full_idx], type="l", lwd=spec_lwd) figure_letter("C") if (FALSE) { plot(ppm, apply(resid_mat, 1, median)[full_idx], type="l", lwd=spec_lwd, xlim=c(10,0), col="black", xaxt="n", xlab=expression(phantom()^1*H ~ (ppm)), ylab="Median Residual") axis(1, axis_integer, TRUE) axis(1, axis_tenths, FALSE, lwd.ticks=0.5, tcl=-0.25) abline(h=0, col="gray") } ylim <- c(0, 1) ylim <- range(summary(rate_fit_updated)$coef[c("popf","pop1"),"Estimate"]) #if (class(rate_fit) != "try-error") { # ylim <- range(rate_data$pop, rate_fit$m$getPars()[1:2]) #} plot(rate_data_updated, pch=16, ylim=ylim, xlab="Seconds", ylab="Fit Population A", yaxt="n") axis(2, seq(0, 1, by=0.5), tick=FALSE) axis(2, seq(0, 1, by=0.25), label=FALSE) #points(rate_data$seconds, exp(-rate_data$seconds*rate_fit$m$getPars()["r1"]), type="l", col="green") if (class(rate_fit) != "try-error") { points(rate_data$seconds, rate_fit_updated$m$predict(), type="l", col="purple", lwd=2) abline(h=sum(summary(rate_fit_updated)$coef[c("popf","pop1"),"Estimate"]), col="red") # didn't do any propagation of uncertainty here... abline(h=sum(summary(rate_fit_updated)$coef[c("popf","pop1"),"Estimate"])+c(-1,1)*summary(rate_fit_updated)$coef["pop1","Std. Error"], col="red", lty="dashed") abline(h=summary(rate_fit_updated)$coef["popf","Estimate"], col="mediumslateblue") abline(h=summary(rate_fit_updated)$coef["popf","Estimate"]+c(-1,1)*summary(rate_fit_updated)$coef["popf","Std. Error"], col="mediumslateblue", lty="dashed") } if (class(rate_fit) != "try-error") { legend("topright", legend=parse(text=sprintf("\"k = \"*%s ~ s^{-1}", sub("e(-?)0?", " %*% 10^\\1", sprintf("%.2e", rate_fit$m$getPars()[3])))), col=c("purple"), lwd=2, bty="n", title=" ") } figure_letter("D") ## ----include = FALSE---------------------------------------------------------- par(old_par)