R - Filter a vector using a function

72,957

Solution 1

You'll need to Vectorize the function to call it on a vector:

isGoodNumber = Vectorize(isGoodNumber)
v[isGoodNumber(v)]

Solution 2

There is a function named "Filter" that will do exactly what you want:

Filter( isGoodNumber, v)
#[1] 5 5 5 5

There would be the option of making a function that was vectorized, either by by the use of the Vectorize function (already illustrated) or writing it with ifelse (also mentioned) and there would be the option of a function that was "Filter"-like

 isGoodNumber3 <- function(X) 
   { X[ ifelse(X==5, TRUE,FALSE)]
   }

 isGoodNumber3(v)
#[1] 5 5 5 5

Solution 3

I think here is the easiest method

> v<-c(1,2,3,4,5,5,5,5)
> v[v==5]
[1] 5 5 5 5

Solution 4

Use mapply():

> v <- c(1,2,3,4,5,5,5,5)
> newV <- mapply(function(X) { if (X==5) return(TRUE) else return(FALSE) }, v)
> newV
[1] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
> v[newV == TRUE]
[1] 5 5 5 5

Solution 5

You should get into the habit of writing vectorised functions where possible. For example, if you're writing a function f(x), make sure it works when x is a vector, not just a single number. Don't rely on Vectorize to do this for you, because it will be slow for very large vectors.

A useful technique is to replace if ... then ... else with ifelse. For example:

isGoodNumber <- function(X) 
{
  ifelse(X==5, TRUE, FALSE)
}
v<-c(1,2,3,4,5,5,5,5)
v [ isGoodNumber(v) == TRUE ]

In this particular case you can of course streamline things:

isGoodNumber <- function(X) return(X==5)

or even just

v[v==5]

but the ifelse technique will be useful more generally.

Share:
72,957
MadSeb
Author by

MadSeb

Updated on July 09, 2022

Comments

  • MadSeb
    MadSeb almost 2 years

    I have a function similar to this one:

    isGoodNumber <- function(X) 
    {
    if (X==5) return(TRUE) else return(FALSE)
    }
    
    I have a vector:
    v<-c(1,2,3,4,5,5,5,5)
    

    I want to obtain a new vector that contains the elements of v where isGoodNumber(v) == TRUE

    How do I do this ?

    Tried v [ isGoodNumber(v) == TRUE ] but it doesn't work :-)

    Thanks !!

    • Dason
      Dason over 12 years
      Are you able to rewrite the function? Or is the function defined and you aren't able to change it?
    • Chase
      Chase over 12 years
      I'm guessing your real application is more complicated than the example, but v[v==5] will do the trick for your example.
    • MadSeb
      MadSeb over 12 years
      Hi @Dason, yes I am able to rewrite the function :-)
    • MadSeb
      MadSeb over 12 years
      @Chase : yes, the real application is more complicated than the example :-) but thanks anyway !
    • Dason
      Dason over 12 years
      I was just asking because ifelse is a vectorized version of if. The fact that you're using if in this example is what causes problems. There are ways to get around functions that can't easily be vectorized but in some situations the best solution is to just write your function in a vectorized way initially.
    • IRTFM
      IRTFM almost 3 years
      @Dason. Just for the record (and I suspect you know this now) ifelse in R is NOT a vectorized version of if The if function is designed to execute code. It WILL execute anything in either its second or third arguments. The executed code may be rather complex and assignments are retained in the calling environment. The ifelse function will return only a vector of values chosen from between two sets of possible values. Including assignments in the alternatives will NOT be successful. Thinking of them as similar will lead you to erroneous conclusions.
  • krlmlr
    krlmlr about 10 years
    This is the only answer that doesn't require repetition of v, which is a good thing if v is a computed expression.
  • Thomas Luechtefeld
    Thomas Luechtefeld over 2 years
    easier with pipes v %>% .[sapply(.,isGoodNumber)]. the . references the v object and sapply applies isGoodNumber to each element in v.
  • IRTFM
    IRTFM over 2 years
    @ThomasLuechtefeld. We must have different definitions of easy. The piped method looks more complicated than ‘v[ sapply(v, isGoodNumber)]’ and they both look less elegant than the Filter method.
  • Thomas Luechtefeld
    Thomas Luechtefeld over 2 years
    you are correct, my mistake.