Найдите начальную и конечную позиции / индексы прогонов / последовательные значения

Проблема: для данного атомарного вектора найти в векторе начальный и конечный индексы прогонов.

Пример вектора с пробегами:

x = rev(rep(6:10, 1:5))
# [1] 10 10 10 10 10  9  9  9  9  8  8  8  7  7  6

Вывод из rle():

rle(x)
# Run Length Encoding
#  lengths: int [1:5] 5 4 3 2 1
#  values : int [1:5] 10 9 8 7 6

Желаемый результат:

#   start end
# 1     1   5
# 2     6   9
# 3    10  12
# 4    13  14
# 5    15  15

Базовый класс rle, похоже, не предоставляет эту функциональность, но класс _ 6_ и функция _ 7_ делать. Однако, учитывая, насколько незначительна функциональность, придерживаться базового R кажется более разумным, чем установка и загрузка дополнительных пакетов.

Существуют примеры фрагментов кода (здесь, здесь и в SO), которые решают несколько иную проблему поиска начального и конечного индексов для прогонов, удовлетворяющих некоторому условию. Мне нужно было что-то более общее, которое могло бы выполняться в одну строку и не предполагало присвоения временных переменных или значений.

Отвечая на свой вопрос, потому что я был разочарован отсутствием результатов поиска. Надеюсь, это кому-нибудь поможет!


person Clara    schedule 09.05.2017    source источник


Ответы (2)


Основная логика:

# Example vector and rle object
x = rev(rep(6:10, 1:5))
rle_x = rle(x)

# Compute endpoints of run
end = cumsum(rle_x$lengths)
start = c(1, lag(end)[-1] + 1)

# Display results
data.frame(start, end)
#   start end
# 1     1   5
# 2     6   9
# 3    10  12
# 4    13  14
# 5    15  15

Tidyverse / dplyr способ (ориентированный на кадры данных):

library(dplyr)

rle(x) %>%
  unclass() %>%
  as.data.frame() %>%
  mutate(end = cumsum(lengths),
         start = c(1, dplyr::lag(end)[-1] + 1)) %>%
  magrittr::extract(c(1,2,4,3)) # To re-order start before end for display

Поскольку векторы start и end имеют ту же длину, что и компонент values объекта rle, решение связанной проблемы идентификации конечных точек для прогонов, удовлетворяющих некоторому условию, является простым: filter или подмножество векторов start и end, используя условие для значений прогона.

person Clara    schedule 09.05.2017
comment
Привет, @Clara! У меня проблемы с запуском вашего кода. Вы имеете в виду dplyr::lag в своем первом решении? Во втором решении означает select вместо extract (из tidyr?)? Ваше здоровье - person Henrik; 08.05.2019
comment
Если вы хотите сохранить первое решение в base, вы можете использовать head(end, -1) вместо lag. - person Henrik; 08.05.2019
comment
@Henrik Хороший вопрос! Я имел в виду stats::lag (я также недавно столкнулся с проблемой коллизии пространства имен между dplyr и stats на lag, так что поймите, почему вы спросите!). Для extract я имею в виду magrittr::extract, который является функцией прямого канала для оператора [. Спасибо за то, что вскрыли эту двусмысленность, в то время я не понимал, что делаю! - person Clara; 23.05.2019
comment
Спасибо за ответ @Clara! С stats::lag я получаю другой результат, чем то, что вы показываете. (start <- c(1, stats::lag(end)[-1] + 1)); [1] 1 10 13 15 16. dplyr::lag дает результат, который вы показываете: (start <- c(1, dplyr::lag(end)[-1] + 1)); [1] 1 6 10 13 15. Ваше здоровье - person Henrik; 24.05.2019
comment
Также без lag: start <- end - rle_x$lengths + 1 - person qwr; 26.06.2019

data.table возможность, где .I и .N используются для выбора соответствующих индексов для каждой группы, определенной rleid запусками.

library(data.table)
data.table(x)[ , .(start = .I[1], end = .I[.N]), by = rleid(x)][, rleid := NULL][]
#    start end
# 1:     1   5
# 2:     6   9
# 3:    10  12
# 4:    13  14
# 5:    15  15
person Henrik    schedule 09.05.2017