查找向量中最后一次出现的唯一元素的索引

时间:2015-01-06 15:16:45

标签: r

我有一个无序的向量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))来查找唯一元素但是如何找到它们最后一次出现的索引?有什么想法吗?

提前致谢。

10 个答案:

答案 0 :(得分:14)

即使未订购数据,另一种方法仍然有效:

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

答案 1 :(得分:9)

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

答案 2 :(得分:7)

如果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

更新

对于新的向量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

使用@Marat talipov的帖子中的示例(z),

 f1(z)
 #[1] 4 5 3

注意:我按照唯一元素首次出现在z中的顺序得到结果。即1,然后是32。如果需要根据值再次进行排序,可以使用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)

答案 3 :(得分:6)

也可以尝试

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

或类似地

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

答案 4 :(得分:5)

您可以尝试使用&#34; data.table&#34;中的.N,如下所示:

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所推荐的,我们可以跳过创建&#34; y&#34;直接做:

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)

答案 5 :(得分:5)

我很遗憾地说,但是接受的答案,以及应该对无序向量起作用的其他几个答案,提供了错误的解决方案

[EDIT2]

对于应该被视为“正确”或“错误”答案的内容,这个答案已成为一个争议问题。在这里,我将所需的输出解释为解决方案应该是沿着独特元素的递增顺序排序的未命名向量。事实证明,其他解释可能存在(见下面的评论),虽然它们对我来说看起来不太明显,但它们肯定有权存在,至少在OP增加更多例子来澄清情况之前

从这个角度来看,可能更好的说法是“重现OP样本的答案可能导致其他输入数据集在输出向量中元素排序方面的结果不一致”< / strong>即可。这种不一致的部分原因在于原始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])]  
}

[编辑] 我被告知原始f.data.table结果 - 这是一个包含两列(xV1)的data.table结构 - 包含以问题作者所期望的格式构建答案所需的所有信息。事实上,f.data.table中的错误是由我决定使用列V1作为函数输出引入的。我通过修改后的代码(请参阅下面的评论)更新了f.data.table,该代码以预期的格式提供了正确的解决方案,并将旧版本保存为注释。此外,我从答案的最后删除了对f.data.table解决方案的讨论,因为不再需要它了。

答案 6 :(得分:4)

这是另一种方法:

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来获取索引。

更新: 如评论中所述,我的原始答案which(!duplicated(z,fromLast=T))返回的向量与输入向量中元素的递增顺序不对应。为了解决这个问题,我将第一个命令的结果保存为向量i并根据需要重新排序。

答案 7 :(得分:4)

只是为了好玩,

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)

答案 8 :(得分:3)

只是为了好玩 - 不是矢量化 - 而是完成工作:

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

答案 9 :(得分:1)

使用grouping功能:

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