首页 > 解决方案 > 识别相同值之间的序列

问题描述

我有一个大矩阵:

id    v1   v2   v3   v4   v5   v6   v7   v8
1001   37   15   30   37    4   11   35   37
2111   44   31   44   30   24   39   44   18
3121   43   49   39   34   44   43   26   24
4532   45   31   26   33   12   47   37   15
5234   23   27   34   23   30   34   23    4
6345   9    46   39   34    8   43   26   24

对于每一行(id),我想确定列 v1 到 v8 中的数字间隔。区间在这里定义为以相同数字开头和结尾的数字序列。

例如,在第一行中,有两个以 37 开头和结尾的序列:从第 1 列到第 4 列(37、15、30、37 和从第4列到第 8 列(37、4、11、3537 ).

焦点值应该只出现在开始和结束位置。例如,在第一行中,从 V1 的 37 到 V8 的 37 的序列包括在内,因为 37 也出现在 V4 中。

对于每个间隔,我想要开始和结束列的索引、焦点开始和结束值,以及它们之间的数字序列。

期望的输出:

1001 [v1] to [v4] 37 to 37: 15,30
1001 [v4] to [v8] 37 to 37: 4, 11, 35
2111 [v1] to [v3] 44 to 44: 31 
2111 [v3] to [v7] 44 to 44: 30, 24, 39

有什么建议么?算法?

我设法为向量而不是矩阵的索引编写代码,

a <- which(x == 37)
from <- a[!(a-1) %in% a]
to <- a[!(a+1) %in% a]
rbind(from, to)

标签: rmatrixsequence

解决方案


非常暴力的方法。获取给定行的唯一元素,检查它们是否存在不止一次但不是并排存在,然后lapply通过每个元素,获取x它们之间的行的元素。

apply(m, 1, function(x) {
  u <- unique(x)
  u <- u[sapply(u, function(u) any(diff(which(x == u)) > 1))]
  lapply(setNames(u, u), function(u){ 
      ind <- which(x == u)
      lapply(seq(length(ind) - 1), 
             function(i) x[seq(ind[i] + 1, ind[i + 1] - 1)])
  })
})

输出:

# [[1]]
# [[1]]$`37`
# [[1]]$`37`[[1]]
# [1] 15 30
# 
# [[1]]$`37`[[2]]
# [1]  4 11 35
# 
# 
# 
# [[2]]
# [[2]]$`44`
# [[2]]$`44`[[1]]
# [1] 31
# 
# [[2]]$`44`[[2]]
# [1] 30 24 39
# 
# 
# 
# [[3]]
# [[3]]$`43`
# [[3]]$`43`[[1]]
# [1] 49 39 34 44
# 
# 
# 
# [[4]]
# named list()
# 
# [[5]]
# [[5]]$`23`
# [[5]]$`23`[[1]]
# [1] 27 34
# 
# [[5]]$`23`[[2]]
# [1] 30 34
# 
# 
# [[5]]$`34`
# [[5]]$`34`[[1]]
# [1] 23 30
# 
# 
# 
# [[6]]
# named list()

编辑:Henrik 的回答启发了我做一个基于加入的版本

library(data.table)
library(magrittr)

d <- melt(as.data.table(m), "id", variable.name = 'ci')[, ci := rowid(id)]

setorder(d, id) 
options(datatable.nomatch = 0)

d[d, on = .(id, value, ci > ci)
  , .(id, value, i.ci, x.ci)
  , mult = 'first'] %>% 
  .[d, on = .(id, i.ci < ci, x.ci > ci)
    , .(id, value, from_ci = x.i.ci, to_ci = x.x.ci, i.value)] %>% 
  .[, .(val = .(i.value))
    , by = setdiff(names(.), 'i.value')]


#      id value from_ci to_ci         val
# 1: 1001    37       1     4       15,30
# 2: 1001    37       4     8     4,11,35
# 3: 2111    44       1     3          31
# 4: 2111    44       3     7    30,24,39
# 5: 3121    43       1     6 49,39,34,44
# 6: 5234    23       1     4       27,34
# 7: 5234    34       3     6       23,30
# 8: 5234    23       4     7       30,34

推荐阅读