在R

时间:2019-04-04 12:15:23

标签: r sequence

我想找到(最小)长度为n的序列中的所有子序列。假设我有这个顺序

sequence <- c(1,2,3,2,5,3,2,6,7,9)

我想找到最小长度为3的递增子序列。输出应该是一个数据帧,其中每个发现的子序列的起始和结束位置。

df =data.frame(c(1,7),c(3,10))
colnames(df) <- c("start", "end")

有人可以提示如何解决我的问题吗?

谢谢!

2 个答案:

答案 0 :(得分:2)

仅使用基数R的一种方法

n <- 3

do.call(rbind, sapply(split(1:length(sequence), cumsum(c(0, diff(sequence)) < 1)), 
        function(x) if (length(x) >= n) c(start = x[1], end = x[length(x)])))

#  start end
#1    1    3
#4    7   10

split基于连续递增子序列的索引sequence,如果每个组的length大于等于n,则返回索引的开始和结束索引该小组。


要了解,让我们分解并逐步了解它

使用diff,我们可以找到连续元素之间的差异

diff(sequence)
#[1]  0  1  1 -1  3 -2 -1  4  1  2

我们检查其中哪些子序列不增加

diff(sequence) < 1
#[1] FALSE FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE

对它们进行累计求和以创建组

cumsum(c(0, diff(sequence)) < 1)
#[1] 1 1 1 2 2 3 4 4 4 4

基于这些组,我们split的索引1:length(sequence)

split(1:length(sequence), cumsum(c(0, diff(sequence)) < 1))
#$`1`
#[1] 1 2 3

#$`2`
#[1] 4 5

#$`3`
#[1] 6

#$`4`
#[1]  7  8  9 10

使用sapply遍历此列表,并返回列表if的开始和结束索引,列表的length>= n(在这种情况下为3)

sapply(split(1:length(sequence), cumsum(c(0, diff(sequence)) < 1)), 
       function(x) if (length(x) >= n) c(start = x[1], end = x[length(x)]))

#$`1`
#start   end 
#    1     3 

#$`2`
# NULL

#$`3`
#NULL

#$`4`
#start   end 
#    7    10 

最后,使用rbinddo.call一起使用。 NULL个元素将被自动忽略。

do.call(rbind, sapply(split(1:length(sequence), cumsum(c(0, diff(sequence)) < 1)), 
       function(x) if (length(x) >= n) c(start = x[1], end = x[length(x)])))

#  start end
#1     1   3
#4     7  10

答案 1 :(得分:2)

这是使用基数R的另一种解决方案。我试图对其进行很好的注释,但是可能仍然很难遵循。似乎您想要的是方向性/学习性,而不是一个直接的答案,因此,如果有任何不清楚的地方(或对您的实际应用不起作用),请绝对跟进问题。

另外,对于您的数据,我在末尾添加了12,以确保其返回正确的位置,以进行大于n的重复增加(本例中为3):

# Data (I added 11 on the end)
sequence <- c(1,2,3,2,5,3,2,6,7,9, 12)

# Create indices for whether or not the numbers in the sequence increased
indices <- c(1, diff(sequence) >= 1)
indices
[1] 1 1 1 0 1 0 0 1 1 1 1

现在我们有了索引,我们需要获取重复> = 3

的开始和结束位置
# Finding increasing sequences of n length using rle
n <- 3
n <- n - 1

# Examples 
rle(indices)$lengths
[1] 3 1 1 2 4

rle(indices)$values
[1] 1 0 1 0 1

# Finding repeated TRUE (1) in our indices vector
reps <- rle(indices)$lengths >= n & rle(indices)$values == 1
reps
[1]  TRUE FALSE FALSE FALSE  TRUE

# Creating a vector of positions for the end of a sequence
# Because our indices are true false, we can use cumsum along
# with rle to create the positions of the end of the sequences
rle_positions <- cumsum(rle(indices)$lengths)
rle_positions
[1]  3  4  5  7 11

# Creating start sequence vector and subsetting start / end using reps
start <- c(1, head(rle_positions, -1))[reps]

end <- rle_positions[reps]

data.frame(start, end)
  start end
1     1   3
2     7  11

或者,简而言之:

n <- 3
n <- n-1
indices <- c(1, diff(sequence) >= 1)
reps <- rle(indices)$lengths >= n & rle(indices)$values == 1
rle_positions <- cumsum(rle(indices)$lengths)
data.frame(start = c(1, head(rle_positions, -1))[reps], 
           end = rle_positions[reps])
  start end
1     1   3
2     7  11

编辑:@Ronak的更新使我意识到,第一步应该使用带有匿名功能的diff而不是sapply。更新了答案b / c,它在向量的末尾没有增加(例如,sequence <- c(1,2,3,2,5,3,2,6,7,9,12, 11, 11, 20, 100),还需要在n <- 3下再增加一行。这现在应该可以正常工作了。