С этими примерами данных:
df <- data.frame(
Range1 = c(1, 2, 3, 5, 10, 12, 16, 20, 21, 28, 33),
Range2 = c(2, 3, 5, 10, 12, 16, 20, 21, 28, 33, 40),
Breakpoint = c("", "", "", "Y", "", "Y", "", "", "Y", "", ""))
Решение с вырезанными концевыми битами:
Сначала обрежьте свисающие биты:
df2 = df[1:max(which(df$Breakpoint=="Y")),]
Затемопределить длину каждой группы:
> rgroup=rle(rev(cumsum(rev(df2$Break=="Y"))))$lengths
Получить, где Y:
> Ypos = which(df2$Breakpoint=="Y")
Построить индексный вектор, который представляет собой позиции Y минус обратная последовательность от 1 до длиныкусокПодмножество:
> df2[rep(Ypos, rgroup) - unlist(lapply(rgroup,function(x){1:x})) +1,]
Range1 Range2 Breakpoint
4 5 10 Y
3 3 5
2 2 3
1 1 2
6 12 16 Y
5 10 12
9 21 28 Y
8 20 21
7 16 20
При необходимости снова включить висячие биты.
[править - добавлена новая версия выше.Приведенный ниже код для исторических целей]
Моя старая версия была такой и имела дело с висячими битами:
> group=rev(cumsum(rev(df$Break=="Y")))
> rbind(do.call(rbind,lapply(split(df[group>0,],-group[group>0]),function(x){x[nrow(x):1,,drop=FALSE]}))[,c("Range1","Range2")],df[max(which(df$Break=="Y")),1:2,drop=FALSE],df[group==0,1:2])
и получаю:
Range1 Range2
-3.4 5 10
-3.3 3 5
-3.2 2 3
-3.1 1 2
-2.6 12 16
-2.5 10 12
-1.9 21 28
-1.8 20 21
-1.7 16 20
9 21 28
10 28 33
11 33 40
Если вы неt, как имена строк, затем отбросьте их.Используются только базовые функции R.
Я не уверен, работает ли это, если после последнего перерыва нет завершающего вопроса, но вы не указали проблему хорошо, если это может произойти.
БонусАннотированная версия:
> group=rev(cumsum(rev(df$Break=="Y")))
Это создает вектор, который начинается с 0 для последней строки и увеличивается каждый раз, когда он находит Y. Обратный, чтобы получить переменную группировки для кусков вплоть до каждого Y.
Этот бит не сработает, если использовать режущую пасту из-за комментариев, которые я собираюсь сделать:
> rbind(
# we need to bind three things. The reversed chunks, the last break point and
# the trailing stuff:
do.call(
# the trailing stuff is the rbind of the reversed chunks:
rbind,
# split the data into a list of chunks
lapply(
split(df[group>0,],-group[group>0]),
# reverse them
function(x){x[nrow(x):1,,drop=FALSE]}
# and only take the columns we need:
))[,c("Range1","Range2")],
# this is the last Y
df[max(which(df$Break=="Y")),1:2,drop=FALSE],
# this is the trailing rows, get them in order they appear:
df[group==0,1:2])
Такое аннотирование позволило мне увидеть некоторые оптимизации, которые можно было бы сделать, но на этом пока все.