识别相同值之间的序列

时间:2018-11-28 19:40:45

标签: r matrix sequence

我有一个大矩阵:

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、35, 37 )。

焦点值应仅出现在开始和结束位置。例如,在第一行中, 不包括从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)

2 个答案:

答案 0 :(得分:4)

非常强力的方法。获取给定行的唯一元素,检查它们是否存在多次但不是并排,然后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

答案 1 :(得分:1)

这是data.table的替代方案。

将矩阵转换为data.table,将melt转换为长格式。创建列索引“ ci”以跟踪原始列(rowid(id))。按“ id”排序。

对于每个'id'和'value'(by = .(id, value)),检查行数是否大于一(if(.N > 1)),即是否至少有一个序列。如果是这样,请抓住序列的行索引(.I)及其列索引(在原始数据中)。对于每个序列,获取开始索引和结束索引之间的对应值。将list包装两次(.(.(),以创建一个列表列。

library(data.table)
d <- melt(as.data.table(m), id.vars = "id")
d[ , `:=`(
  ci = rowid(id),
  variable = NULL)]  
setorder(d, id)

d2 <- d[ , if(.N > 1){
  .(from = .I[-.N], to = .I[-1],
    from_ci = ci[-.N], to_ci = ci[ -1])
}, by = .(id, value)]

d2[ , val := .(.(d$value[seq(from + 1, to - 1)])), by = 1:nrow(d2)]
d2[ , `:=`(from = NULL, to = NULL)]

#      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    23       4     7       30,34
# 8: 5234    34       3     6       23,30