我想找到(最小)长度为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")
有人可以提示如何解决我的问题吗?
谢谢!
答案 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
最后,使用rbind
将do.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
下再增加一行。这现在应该可以正常工作了。>