Max M Max M - 1 month ago 6
R Question

Higher order function find min of list

I am looping over an estimation and saving all the estimation objects and then pick the one with the lowest deviance. For this, I wanted to use the Filter/Map/Position functions, but I could not find a solution, because it always returns the first object of the list, instead of the second. I probably misunderstand something about how the Position function works, but would like to know what I missed.

MWE:

ls<-list(3,2,4)
Position(min,Map(function(x) {x^2}, ls))


I ended up using unlist and which.min

Answer

You ended up doing the correct procedure. Why? First, we'll pull up a salient extract from the help page:

Position(f, x, right = FALSE, nomatch = NA_integer_)

Find and Position are patterned after Common Lisp's find-if and position-if, respectively. If there is an element for which the predicate function gives true, then the first or last such element or its position is returned depending on whether right is false (default) or true, respectively. If there is no such element, the value specified by nomatch is returned. The current implementation is not optimized for performance.

So, Position() is going to apply f() to all elements of x and when the result of f() is TRUE (directly or via coercion) Position() will return the index of that element (if nothing ends up being TRUE then it returns the value assigned to nomatch).

Here's the actual Position() function source:

Position <- function (f, x, right=FALSE, nomatch=NA_integer_) {

  ind <- seq_along(x)
  if (right) ind <- rev(ind)

  for (i in ind) {
    if (f(x[[i]])) return(i)
  }

  nomatch

}

Following the source you can prbly see that min() is getting called on the first element of the list and it returns the value that the min() of the vector at list position 1 and that value is non-zero so it thinks it did a good job and returns the list index it was at.

If you had been doing:

Position(min, Map(function(x) {x-3}, dat))

then you would have seen the result be:

## [1] 2

and possibly have thought it was working, but it's only returning that since the first element of the list of 3 and 3-3 == 0 and 0 is coerced to FALSE.

NOTE: ls is also the name of a base function which is fine since R knows what to do based on usage context but I don't like potentially causing weird errors down the road in function calls by crushing very common core namespace elements so I used dat instead of ls.

The idea behind Position() is more for something like:

eqls4 <- function(x) x==4

Position(eqls4, Map(function(x) {x^2}, dat))

which does return:

## [1] 2

So, what you ended up doing was 100% correct.

Note also that the purrr package provides alternative functional idioms that (IMO) tend to be more readable + have ways of ensuring proper types are maintained and it exports %>% so piping is also readily available:

library(purrr)

map(dat, ~.^2) %>% 
  flatten_dbl() %>% 
  which.min()