Find the indices of last occurrence of the unique elements in a vector

You could try rle if the vector is already ordered. Extract the lengths ($lengths) and then cumsum. As I mentioned earlier, this will not work if it is not ordered (again it depends on what you really wanted). Basically rle works by checking the number of consecutive elements that are similar on a stretch. It will give the lengths and corresponding values in a list.

cumsum(rle(v1)$lengths)
#[1] 28 37 42 46 50

Another option is to group the sequence by the vector and get the max value for each group. I would imagine this to be slow.

unname(cumsum(tapply(seq_along(v1),v1, FUN=which.max)))    
#[1] 28 37 42 46 50

Or just check whether the previous value is the same as the current value and then insert TRUE as the last element, and get the index of TRUE with which

 which(c(v1[-1]!=v1[-length(v1)],TRUE))
 #[1] 28 37 42 46 50

Or use match

 c(match(unique(v1),v1)-1, length(v1))[-1]
#[1] 28 37 42 46 50

Or use findInterval

 findInterval(unique(v1), v1)
 #[1] 28 37 42 46 50

Update

For the new vector v2

max.col(t(sapply(unique(v2), `==`, v2)),'last')
#[1] 41 46 45 44 50 27

Or a function using findInterval after ordering the unordered vector

   f1 <- function(v){
      v1 <- setNames(v, seq_along(v))
      ind <- order(v1)
      as.numeric(names(v1[ind][findInterval(unique(v1), v1[ind])]))
    }     

 f1(v2)
 #[1] 41 46 45 44 50 27

Using the example (z) from @Marat talipov's post,

 f1(z)
 #[1] 4 5 3

NOTE: I get the result in the order in which the unique elements first appeared in z. i.e. 1, followed by 3, 2. If it needs to be ordered again based on the values, it can be done using order (as mentioned by @Marat Talipov). However, it is not clear what the OP really wanted in such situations.

data

v1 <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 
 3, 4, 4, 4, 4, 5, 5, 5, 5)

v2 <-  c(1, 2, 1, 2, 1, 1, 1, 3, 1, 2, 2, 3, 3, 3, 1, 1, 1, 4, 1, 1, 
 1, 4, 1, 5, 5, 6, 6, 2, 3, 3, 4, 4, 2, 2, 2, 2, 2, 3, 3, 3, 1, 
 4, 4, 4, 3, 2, 5, 5, 5, 5)

 z <- c(1, 3, 2, 1, 3)

Another approach that works even if the data are not ordered:

length(v1)-match(unique(v1),rev(v1))+1

tapply(seq_along(v), v, max)
#  1  2  3  4  5  6 
# 41 46 45 44 50 27 

Also could try

which(c(diff(tmp), TRUE) == 1)
# [1] 28 37 42 46 50

Or similarly

which(!!c(diff(tmp), TRUE))

Tags:

R