Подтвердить что ты не робот

Найти индексы последнего вхождения уникальных элементов в вектор

У меня есть неупорядоченный вектор v, подобный показанному ниже, и хотел бы найти индексы последнего вхождения всех уникальных элементов в списке.

v <- scan(text="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")
v
# [1] 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 
# [41] 1 4 4 4 3 2 5 5 5 5

Ожидаемый результат (в порядке 1, 2, 3, 4, 5):

41 46 45 44 50

Я знаю, что я могу использовать unique(unlist(v)) для поиска уникальных элементов, но затем как найти индексы их последнего появления? Любая идея?

Спасибо заранее.

4b9b3361

Ответ 1

Другой подход, который работает, даже если данные не упорядочены:

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

Ответ 2

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

Ответ 3

Вы можете попробовать rle, если vector уже упорядочен. Извлеките длины ($lengths), а затем cumsum. Как я упоминал ранее, это не сработает, если оно не упорядочено (опять же, это зависит от того, что вы действительно хотели). В основном rle работает, проверяя количество последовательных элементов которые похожи на растяжку. В списке будет lengths и соответствующем values.

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

Другим вариантом является группировка последовательности по вектору и получение значения max для каждого group. Я предполагаю, что это будет медленным.

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

Или просто проверьте, совпадает ли предыдущее значение с текущим значением, а затем вставьте TRUE в качестве последнего элемента и получите индекс TRUE с помощью which

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

Или используйте match

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

Или используйте findInterval

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

Update

Для нового вектора v2

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

Или функция, использующая findInterval после ordering неупорядоченный вектор

   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

Используя пример (z) из сообщения @Marat talipov,

 f1(z)
 #[1] 4 5 3

ПРИМЕЧАНИЕ. Я получаю результат в том порядке, в котором уникальные элементы впервые появились в z. т.е. 1, а затем 3, 2. Если его нужно заказывать снова на основе значений, это можно сделать, используя order (как упоминалось @Marat Talipov). Тем не менее, неясно, что OP действительно хотел в таких ситуациях.

данные

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)

Ответ 4

Также можно попробовать

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

Или аналогично

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

Ответ 5

Вы можете попробовать использовать .N из "data.table", например:

library(data.table)
data.table(x, y = seq_along(x))[, y[.N], by = x]
#    x V1
# 1: 1 41
# 2: 2 46
# 3: 3 45
# 4: 4 44
# 5: 5 50
# 6: 6 27

Здесь мы в основном создаем двух столбцов data.table, где первый столбец - ваш вектор, а второй - индексная позиция вашего вектора. .N указывает, сколько строк в каждой группе (захвачено с помощью by =), поэтому мы можем просто подмножать значения из y непосредственно с помощью этой информации.


Еще лучше, как рекомендовано @Arun, мы можем пропустить создание "y" и непосредственно сделать:

data.table(x)[, .I[.N], by=x]

Пример данных:

x <- 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)

Ответ 6

Я сожалею, что, но принятый ответ, а также несколько других ответов, которые должны работать на неупорядоченных векторах, предоставляют неправильное решение.

[EDIT2]

Этот ответ стал предметом споров о том, что следует рассматривать как "правильный" или "неправильный" ответ. Здесь я интерпретировал желаемый результат, так как решение должно быть безымянным вектором, упорядоченным по возрастающему порядку уникальных элементов. Оказалось, что могут существовать другие интерпретации (см. Комментарии ниже), и хотя они не кажутся мне очень очевидными, они определенно имеют право на существование, по крайней мере до тех пор, пока OP не добавит больше примеров, чтобы прояснить ситуацию.

В этом свете было бы лучше сказать, что "ответы, которые воспроизводят образец OP, могут привести к несогласованным результатам в других наборах входных данных относительно упорядочения элементов в выходном векторе" . Непоследовательность частично возникла из-за того, что исходный вопрос ОП был изменен пару раз, и ответы, которые были в полном соответствии с текущим состоянием вопроса, могут не сработать для окончательного состояния вопроса. Мой ответ должен знать читателей об этой ситуации и предложить простое решение для получения решения для конечного состояния вопроса ОП.

Наконец, я понимаю, что мой ответ оказался огромным излишеством, но, учитывая уровень замешательства на этом посту, я думаю, что лучше прояснить ситуацию для будущих заинтересованных читателей.

/[EDIT2]

Я случайно обнаружил эту проблему, когда я начал совместное использование разных решений для проведения сравнительного исследования. Некоторые упомянутые здесь решения не работают, потому что исходный вопрос подразумевает, что входной вектор сортируется в возрастающем порядке, что оказалось не так, поэтому я не обсуждаю их здесь. Решения, которые дают правильные ответы на набор данных примера автора, собирались вместе и завертывались в соответствующие функции:

f.duplicated <- function(z) {
  i <- which(!duplicated(z,fromLast=T))
  i[order(z[i])]  
}

f.match.unique.rev <- function(v1) {
  length(v1)-match(unique(v1),rev(v1))+1
}

f.max.col.sapply.unique <- function(v2){
  max.col(t(sapply(unique(v2), `==`, v2)),'last')
}

f.data.table <- function(x) {
  # data.table(x, y = seq_along(x))[, y[.N], by = x]$V1
  setkey(data.table(x, y = seq_along(x)), x)[, y[.N], by = x]$V1
}

f.tapply.seq_along.max <- function(v) {
  tapply(seq_along(v), v, max)
}

f.sapply.split.seq_along.max <- function(v) {
  sapply(split(seq_along(v), v), max)
}

Затем я написал небольшую функцию для сравнения результатов:

compare.results <- function(z) {
  d <- rbind(
    f.duplicated(z),
    f.match.unique.rev(z),
    f.max.col.sapply.unique(z),
    f.data.table(z),
    f.tapply.seq_along.max(z),
    f.sapply.split.seq_along.max(z)
    )
  rownames(d) <- c(
    'f.duplicated',
    'f.match.unique.rev',
    'f.max.col.sapply.unique',
    'f.data.table',
    'f.tapply.seq_along.max',
    'f.sapply.split.seq_along.max'
  )
  d
}

и убедитесь, что выбранное решение работает с примерными данными:

z <- 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)

compare.results(z)
#                               1  2  3  4  5  6
# f.duplicated                 41 46 45 44 50 27
# f.match.unique.rev           41 46 45 44 50 27
# f.max.col.sapply.unique      41 46 45 44 50 27
# f.data.table                 41 46 45 44 50 27
# f.tapply.seq_along.max       41 46 45 44 50 27
# f.sapply.split.seq_along.max 41 46 45 44 50 27

[ПРОБЛЕМА], когда я использовал другой входной вектор 1 3 2 1 3, для которого правильный ответ 4 3 5, я обнаружил, что некоторые решения дают неправильный результат:

z <- c(1,3,2,1,3)
compare.results(z)
#                              1 2 3
# f.duplicated                 4 3 5
# f.match.unique.rev           4 5 3  # ***
# f.max.col.sapply.unique      4 5 3  # ***
# f.data.table                 4 3 5
# f.tapply.seq_along.max       4 3 5
# f.sapply.split.seq_along.max 4 3 5

[FIX] Я понял, что проблема с решениями f.match.unique.rev (принятый ответ) и f.max.col.sapply.unique заключается в том, что уникальные элементы имеют возрастающий порядок в наборе данных, что в примере автора, но не в моем примере. Вот фиксированные решения:

f.max.col.sapply.unique <- function(v2){
  i <- max.col(t(sapply(unique(v2), `==`, v2)),'last')
  i[order(v2[i])]  
}


f.match.unique.rev <- function(v1) {
  i <- length(v1)-match(unique(v1),rev(v1))+1
  i[order(v1[i])]  
}

[EDIT] Мне сообщили, что исходный результат f.data.table, который был структурой data.table с двумя столбцами (x и V1), содержит всю необходимую информацию чтобы построить ответ в формате, который ожидал автор вопроса. Фактически, ошибка в f.data.table была введена моим решением использовать столбец V1 как выход функции. Я обновил f.data.table с помощью модифицированного кода (см. Комментарий ниже), который содержит правильное решение в ожидаемом формате и сохранил старую версию в качестве комментария. Кроме того, я удалил обсуждение решения f.data.table с конца моего ответа, поскольку он больше не нужен.

Ответ 7

Вот еще один подход:

z <- 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)

i <- which(!duplicated(z,fromLast=T))
i[order(z[i])]

duplicated возвращает логический вектор, указывающий дубликаты, рассмотренные с обратной стороны. Идея состоит в том, чтобы взять обратный этот вектор для получения логического вектора уникальных элементов и использовать which для получения индексов.

UPDATE: Как отмечено в комментарии, мой оригинальный ответ which(!duplicated(z,fromLast=T)) возвратил вектор, который не соответствовал возрастающему порядку элементов во входном векторе. Чтобы исправить эту проблему, я сохранил результат из первой команды как vector i и переупорядочил ее по мере необходимости.

Ответ 8

Просто для удовольствия,

library(dplyr)  
#you can use new feature `add_rownames()`   
data.frame(x, row=1:length(x)) %>% group_by(x) %>%  summarise(max(row))
#  x max(row)
#1 1       41
#2 2       46
#3 3       45
#4 4       44
#5 5       50
#6 6       27

для

x <- 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)

Ответ 9

Просто для удовольствия - не векторизованный - но выполняет задание:

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

Ответ 10

С функцией grouping:

g <- grouping(v)
g[attr(g, "ends")]
# [1] 41 46 45 44 50 27