идентифицировать последовательно перекрывающиеся сегменты в R - PullRequest
0 голосов
/ 30 августа 2018

Мне нужно объединить перекрывающиеся сегменты в один сегмент в диапазоне всех соединенных сегментов .

Обратите внимание, что простые конвертеры не могут обнаружить соединения между не перекрывающимися, но соединенными сегментами, см. Пример для пояснения. Если на моем участке будет дождь, я ищу участки сухой земли.

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

Я думал о неэквивалентном скользящем соединении, но не смог реализовать это

library(data.table)
(x <- data.table(start = c(41,43,43,47,47,48,51,52,54,55,57,59), 
                  end = c(42,44,45,53,48,50,52,55,57,56,58,60)))

#     start end
#  1:    41  42
#  2:    43  44
#  3:    43  45
#  4:    47  53
#  5:    47  48
#  6:    48  50
#  7:    51  52
#  8:    52  55
#  9:    54  57
# 10:    55  56
# 11:    57  58
# 12:    59  60

setorder(x, start)[, i := .I] # i is just a helper for plotting segments
plot(NA, xlim = range(x[,.(start,end)]), ylim = rev(range(x$i)))
do.call(segments, list(x$start, x$i, x$end, x$i))

x$grp <- c(1,3,3,2,2,2,2,2,2,2,2,4) # the grouping I am looking for
do.call(segments, list(x$start, x$i, x$end, x$i, col = x$grp))
(y <- x[, .(start = min(start), end = max(end)), k=grp])

#    grp start end
# 1:   1    41  42
# 2:   2    47  58
# 3:   3    43  45
# 4:   4    59  60

do.call(segments, list(y$start, 12.2, y$end, 12.2, col = 1:4, lwd = 3))

EDIT:

Это замечательно, спасибо, cummax & cumsum справляются с работой, Ответ Уве немного лучше, чем комментарий Дэвидса.

  • end[.N] может дать неправильные результаты, попробуйте данные примера x ниже. max(end) правильно во всех случаях и быстрее.

    x <- data.table(start = c(11866, 12696, 13813, 14011, 14041), end = c(13140, 14045, 14051, 14039, 14045))

  • min(start) и start[1L] дают то же самое (так как x упорядочено по пуску), последнее быстрее.
  • GRP на лету значительно быстрее, к сожалению, мне нужно назначить GRP.
  • cumsum(cummax(shift(end, fill = 0)) < start) значительно быстрее, чем cumsum(c(0, start[-1L] > cummax(head(end, -1L)))).
  • Я не тестировал пакет GenomicRanges решение.

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

ОП запросил объединить перекрывающиеся сегменты в один сегмент, охватывающий все соединенные сегменты.

Вот еще одно решение, которое использует cummax() и cumsum() для идентификации групп перекрывающихся или смежных сегментов:

x[order(start, end), grp := cumsum(cummax(shift(end, fill = 0)) < start)][
  , .(start = min(start), end = max(end)), by = grp]
   grp start end
1:   1    41  42
2:   2    43  45
3:   3    47  58
4:   4    59  60

Отказ от ответственности: я видел этот умный подход где-то еще на SO, но я не могу точно вспомнить, где.

Редактировать

Как отметил Дэвид Аренбург , нет необходимости создавать переменную grp отдельно. Это можно сделать на лету в параметре by =:

x[order(start, end), .(start = min(start), end = max(end)), 
  by = .(grp = cumsum(cummax(shift(end, fill = 0)) < start))]

Визуализация

Сюжет OP можно изменить, чтобы показать также агрегированные сегменты (быстрые и грязные):

plot(NA, xlim = range(x[,.(start,end)]), ylim = rev(range(x$i)))
do.call(segments, list(x$start, x$i, x$end, x$i))
x[order(start, end), .(start = min(start), end = max(end)), 
  by = .(grp = cumsum(cummax(shift(end, fill = 0)) < start))][
    , segments(start, grp + 0.5, end, grp + 0.5, "red", , 4)]

enter image description here

0 голосов
/ 30 августа 2018

Вы можете попробовать GenomicRanges подход. В выходных данных каждая строка является группой.

library(GenomicRanges)
x_gr <- with(x, GRanges(1, IRanges(start, end)))
as.data.table(reduce(x_gr, min.gapwidth=0))[,2:3]
   start end
1:    41  42
2:    43  45
3:    47  58
4:    59  60

И визуальный осмотр можно сделать с помощью Gviz. Здесь нужно знать, что пакет был создан для биологов и генетической информации. Образец позади - основы ДНК. Следовательно, нужно вычесть 1 конец сегмента, чтобы получить правильный график.

library(Gviz)
ga <- Gviz::GenomeAxisTrack()
xgr <- with(x, GRanges(1, IRanges(start, end = end - 1)))
xgr_red <- reduce(xgr, min.gapwidth=1)
ga <- GenomeAxisTrack()
GT <- lapply(xgr, GeneRegionTrack)
GT_red <- lapply(xgr_red, GeneRegionTrack, fill = "lightblue")
plotTracks(c(ga, GT, GT_red),from = min(x$start), to = max(x$start)+2)

enter image description here

...