Finding All Positions for Multiple Elements in a Vector
Asked Answered
K

7

25

Suppose I have the following vector:

x <- c(8, 6, 9, 9, 7, 3, 2, 5, 5, 1, 6, 8, 5, 2, 9, 3, 5, 10, 8, 2)

How can I find which elements are either 8 or 9?

Kelvinkelwen answered 23/7, 2009 at 3:10 Comment(1)
If you meant "detect all duplicated elements". R has a useful fn duplicated, and you can get all duplicates with duplicated(x) | duplicated(x, fromLast=T)Westward
K
42

This is one way to do it. First I get the indices at which x is either 8 or 9. Then we can verify that at those indices, x is indeed 8 and 9.

> inds <- which(x %in% c(8,9))
> inds
[1]  1  3  4 12 15 19
> x[inds]
[1] 8 9 9 8 9 8
Kelvinkelwen answered 23/7, 2009 at 3:13 Comment(3)
But suppose I'm looking for the specific indexes of two values without their orders sorted. How would I get a result of "26, 1" instead of "1, 26" if I'm looking for the indexes of Z and A in the alphabet? which( letters %in% c( 'z', 'a' ) )Riot
@Riot use any kind of sort, like bubblesortVow
This should be the upvoted answer...Temper
S
11

You could try the | operator for short conditions

which(x == 8 | x == 9)
Solitude answered 23/7, 2009 at 13:10 Comment(0)
H
2

In this specific case you could also use grep:

# option 1
grep('[89]',x)
# option 2
grep('8|9',x)

which both give:

[1]  1  3  4 12 15 19

When you also want to detect number with more than one digit, the second option is preferred:

> grep('10|8',x)
[1]  1 12 18 19

However, I did put emphasis on this specific case at the start of my answer for a reason. As @DavidArenburg mentioned, this could lead to unintended results. Using for example grep('1|8',x) will detect both 1 and 10:

> grep('1|8',x)
[1]  1 10 12 18 19

In order to avoid that side-effect, you will have to wrap the numbers to be detected in word-bounderies:

> grep('\\b1\\b|8',x)
[1]  1 10 12 19

Now, the 10 isn't detected.

Hurrah answered 17/8, 2018 at 8:8 Comment(0)
C
2

Here is a generalized solution to find the locations of all target values (only works for vectors and 1-dimensional arrays).

locate <- function(x, targets) {
    results <- lapply(targets, function(target) which(x == target))
    names(results) <- targets
    results
}

This function returns a list because each target may have any number of matches, including zero. The list is sorted (and named) in the original order of the targets.

Here is an example in use:

sequence <- c(1:10, 1:10)

locate(sequence, c(2,9))
$`2`
[1]  2 12

$`9`
[1]  9 19
Capreolate answered 19/2, 2020 at 21:45 Comment(2)
nice function it would be better if it returned a vectorGulch
locate <- function(x, targets) { results <- lapply(targets, function(target) which(x == target)) unlist(results) }Gulch
T
1

Alternatively, if you do not need to use the indices but just the elements you can do

> x <- sample(1:10,20,replace=TRUE)
> x
 [1]  6  4  7  2  9  3  3  5  4  7  2  1  4  9  1  6 10  4  3 10
> x[8<=x & x<=9]
[1] 9 9
Tootsy answered 23/7, 2009 at 7:34 Comment(0)
P
1

If you want to find the answer using loops, then the following script will do the job:

> req_nos<- c(8,9)
> pos<-list()
> for (i in 1:length(req_nos)){
  pos[[i]]<-which(x==req_nos[i])}

The output will look like this:

>pos
[[1]]
[1] 1 12 19
[[2]] 
[1] 3  4 15

Here, pos[[1]] contains positions of 8 and pos[[2]] contains positions of 9. If you are using the %in% method and change the input order of elements, i.e, c(9,8) instead of c(8,9), the output will be the same for both of them. This method alleviates such problem.

Polybius answered 18/4, 2022 at 15:0 Comment(0)
T
-1

grepl maybe a useful function. Note that grepl appears in versions of R 2.9.0 and later. What's handy about grepl is that it returns a logical vector of the same length as x.

grepl(8, x)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE

grepl(9, x)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE

To arrive at your answer, you could do the following

grepl(8,x) | grepl(9,x)
Trundle answered 26/7, 2009 at 18:26 Comment(2)
I like grepl as well, great for filtering dataframes on text strings, etc. Thanks for the OR example - I thought it would be that simple, but I kept trying || which is the wrong syntax.Tawny
This is a very dangerous solution. grepl(9, c(9, 99, 654649)) will return TRUE for all of these. One should be very careful with exact matches and regex.Plasm

© 2022 - 2024 — McMap. All rights reserved.