Группировать целочисленный вектор в последовательные прогоны - PullRequest
7 голосов
/ 06 декабря 2011

У меня есть два вектора целых чисел. Я хотел бы определить интервалы последовательных целочисленных последовательностей, представленных во втором векторе, обусловленные первым вектором (этот вектор можно рассматривать как фактор, по которому второй вектор можно классифицировать на несколько групп).

Здесь я представляю манекен для моей проблемы.

Данные в одной группе (определяемой первым вектором) второго вектора, монотонно увеличиваются целыми числами.

my.data <- data.frame(
    V1=c(rep(1, 10), rep(2, 9), rep(3,11)), 
    V2=c(seq(2,5), seq(7,11), 13, seq(4, 9), seq(11,13), seq(1, 6), seq(101, 105))
)

Что я хочу:

  • выводит начало и конец интервала
  • здесь, группа в первом столбце, начальное целое число во втором, конечное целое число в третьем.

Ожидаемые результаты:

1, 2, 5 \n
1, 7, 11 \n
1, 13, 13 \n
2, 4, 9 \n
2, 11, 13 \n
3, 1, 6 \n
3, 101, 105 \n

Ответы [ 5 ]

10 голосов
/ 06 декабря 2011

Вот краткий ответ, используя агрегат ....

runs <- cumsum( c(0, diff(my.data$V2) > 1) )
aggregate(V2 ~ runs + V1, my.data, range)[,-1]


  V1 V2.1 V2.2
1  1    2    5
2  1    7   11
3  1   13   13
4  2    4    9
5  2   11   13
6  3    1    6
7  3  101  105
8 голосов
/ 06 декабря 2011

Некоторое время назад я написал вариант rle(), который я назвал seqle(), потому что он позволяет искать целочисленные последовательности, а не повторения. Затем вы можете сделать:

Rgames: seqle(my.data[my.data$V1==1,2]) #repeat for my.data$V1 equal to 2 and 3
$lengths 
[1] 4 5 1 

$values 
[1]  2  7 13 

(например). Потребовалось бы немного возиться, чтобы получить эти результаты в нужной табличной форме, но я подумал, что упомяну это. Кстати, вот код для seqle. Если вы установите incr=0, вы получите базовый результат.

function(x,incr=1){ 

    if(!is.numeric(x)) x <- as.numeric(x) 
    n <- length(x)  
    y <- x[-1L] != x[-n] + incr 
    i <- c(which(y|is.na(y)),n) 
    list( lengths = diff(c(0L,i)),  values = x[head(c(0L,i)+1L,-1L)]) 
} 

РЕДАКТИРОВАТЬ: есть отличное обновление, предоставленное flodel, на Как проверить, содержит ли вектор n последовательных чисел . Он указал, что эта версия имеет обычные проблемы с плавающей точкой при работе с двойными числами, а также предоставил исправление.

6 голосов
/ 06 декабря 2011

вот пример:

library(plyr)

ddply(my.data, .(V1), 
 function(x) data.frame(do.call("rbind", tapply(x$V2, cumsum(c(T, diff(x$V2)!=1)), 
   function(y) c(min(y), max(y))))))

может быть, слишком сложно, но важен cumsum(c(T, diff(x$V2)!=1)).

> ddply(my.data, .(V1), 
+  function(x) data.frame(do.call("rbind", tapply(x$V2, cumsum(c(T, diff(x$V2)!=1)), 
+    function(y) c(min(y), max(y))))))
  V1  X1  X2
1  1   2   5
2  1   7  11
3  1  13  13
4  2   4   9
5  2  11  13
6  3   1   6
7  3 101 105
3 голосов
/ 06 декабря 2011

Вот решение, использующее ddply из пакета plyr.Основная идея состоит в том, чтобы видеть, когда diff(x) не равно 1, чтобы найти точки переключения.

ddply(
  my.data,
  .(V1),
  summarise,
  lower =
  {
    cut_points <- which(diff(V2) != 1)
    V2[c(1, cut_points + 1)]
  },
  upper =
  {
    cut_points <- which(diff(V2) != 1)
    V2[c(cut_points, length(V2))]
  }
)
2 голосов
/ 06 декабря 2011
my.data$run <- ave(my.data$V2, my.data$V1, FUN=function(x) c(1, diff(x)))
strstp <- by(my.data, list(my.data$V1), 
                 FUN=function(x) list(
                           starts=c( head(x$V2,1), x$V2[x$run != 1]), 
                           stops=c(x$V2[which(x$run != 1)-1], tail(x$V2, 1))))
> strstp
: 1
$starts
[1]  2  7 13

$stops
[1]  5 11 13

------------------------------------------------------------- 
: 2
$starts
[1]  4 11

$stops
[1]  9 13

------------------------------------------------------------- 
: 3
$starts
[1]   1 101

$stops
[1]   6 105
...