【问题标题】:Identify sequences between identical values识别相同值之间的序列
【发布时间】:2018-11-28 19:40:45
【问题描述】:

我有一个大矩阵:

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)

【问题讨论】:

  • 试试apply(mat, 1, function(x) tapply(x, cumsum(duplicated(x)|duplicated(x, fromLast = TRUE)), FUN = tail, -1))
  • @akrun;谢谢 有没有办法将大列表导出到 csv 文件中?

标签: r matrix sequence


【解决方案1】:

非常暴力的方法。获取给定行的唯一元素,检查它们是否多次出现但不是并排出现,然后通过每个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

【讨论】:

    【解决方案2】:

    这是一个data.table 替代方案。

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

    对于每个 'id' 和 'value' (by = .(id, value)),检查行数是否大于一 (if(.N &gt; 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
    

    【讨论】:

    • 非常感谢您的帮助;代码正在运行,谢谢;我再次失败的是将结果导出到 csv 或表格中。如果我 export d2 我只收到第一行。我错过了什么?请问你能帮帮我吗?谢谢
    • 成功了吗?我通常更喜欢以整洁、长格式保存我的数据,而不是使用列表列。所以你可以将最后一步替换为d2[ , .(id = id, value = value, from_ci = from_ci, to_ci = to_ci, val = d$value[seq(from + 1, to - 1)]), by = 1:nrow(d2)]
    • @Henrik- 是的;谢谢,我现在正在尝试将结果保存在文件中。是的,我尝试导出列表,但没有成功。我会试试你的新建议
    • 另一种可能性是将 'val' 列中的值连接到一个字符串。也许最终可以在报告中打印,但否则无用,即如果您想进一步处理这些值。无论如何:d2[ , val := .(toString(d$value[seq(from + 1, to - 1)])), by = 1:nrow(d2)]。同样,如果您要进一步处理您的数据,至少data.table 更喜欢长而整齐的数据,而不是列出列。
    猜你喜欢
    • 2020-08-13
    • 2021-12-07
    • 2015-02-17
    • 1970-01-01
    • 2017-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多