1

I'm using R to analyze some spectra and I'm trying to get the local maxima, namely their position and their value.

For example, with a vector:

spectrum <- c(1,1,2,3,5,3,3,2,1,1,5,6,9,5,1,1)

I would like the following result:

pos.peaks = c(5,13)
val.peaks = c(5,9)

I've already used the solution provided here: Finding local maxima and minima for the position of the peaks but how do I extract the corresponding value afterwards? Knowing that I don't have just one vector, I have several columns within several dataframes within a list, and I want to apply the function to every single column of all dataframes in the list. For example, for all the positions I did this:

example <- lapply(mylist, function (x) lapply(x, function(y) which(diff(sign(diff(y)))==-2)+1))

I didn't manage to make it work with slice or filter, because I don't need the same rows within the same dataframe...

Furthermore, I would like to know how to reduce the amount of local maxima I get because my data is very noisy.

I'd appreciate any help you can give me.

Thanks!

Nath

nathkpa
  • 121
  • 8

2 Answers2

1
peakPosition <- function(x, inclBorders=TRUE) {
  if(inclBorders) {y <- c(min(x), x, min(x))
  } else {y <- c(x[1], x)}
  y <- data.frame(x=sign(diff(y)), i=1:(length(y)-1))
  y <- y[y$x!=0,]
  idx <- diff(y$x)<0
  (y$i[c(idx,F)] + y$i[c(F,idx)] - 1)/2
}

(pos.peaks  <- peakPosition(spectrum))
#[1]  5 13

(val.peaks  <- spectrum[pos.peaks])
#[1] 5 9

And for the loop to get the values something like:

example <- lapply(mylist, function(x) {x[peakPosition(x)]})

and for the positions:

lapply(mylist, peakPosition)

In the comment you say your data is very noisy and you get to many local maxima, so you may try first to smooth your data like following:

d <- predict(loess(spectrum ~ seq_along(spectrum)))
pos.peaksS  <- peakPosition(d)
(i <- pos.peaks[apply(abs(outer(pos.peaks, pos.peaksS, "-")), 1, FUN=which.min)])
#[1]  5 13
spectrum[i]
#[1] 5 9

or you make some aggregation to the index like:

set.seed(42)
x <- rnorm(1e3)

y <- peakPosition(x)
(pos.peaks <- sort(aggregate(y, list(k=kmeans(y, 7)$cluster), FUN = function(i) i[which.max(x[i])])[,2]))
#[1] 118 287 459 525 613 820 988

(val.peaks  <- x[pos.peaks])
#[1] 2.701891 2.459594 2.965865 3.229069 2.223534 3.211199 3.495304
GKi
  • 37,245
  • 2
  • 26
  • 48
  • Thank you for going all the way to the looping, I'm going to experiment with both solutions to see what fits best to my data (it's very noisy so I get to many local maxima) – nathkpa Jul 18 '19 at 06:42
  • @nathkpa I have updated the Answer, but maybe you ask a new question with the topic of reducing the amount of local maxima. – GKi Jul 18 '19 at 08:47
  • Thanks again, I've adapted the title and the text, is the term "restrain" appropriate for the title? – nathkpa Jul 18 '19 at 09:28
  • @nathkpa I don't know if "restrain" is appropriate - no native speaker. – GKi Jul 18 '19 at 09:30
1

If the vector has at least length 3:

find_peaks <- function(x, max = TRUE) {
  if (max == FALSE) x <- x * (-1)
  res <- rep(FALSE, length(x))
  if (x[1] > x[2]) res[1] <- TRUE
  if (x[length(x)-1] < x[length(x)]) res[length(res)] <- TRUE
  for (i in (2:(length(x)-1))) {
    if ((x[i-1] < x[i]) & (x[i+1] < x[i])) res[i] <- TRUE
  }
  res
}
spectrum[find_peaks(spectrum)]
# [1] 5 9
which(find_peaks(spectrum))
# [1]  5 13
r.user.05apr
  • 5,356
  • 3
  • 22
  • 39