我有一个数字向量,想要提取连续的递减值。此外,每个序列中的第一个值应该是>= 40
,最后一个值应该是<= 20
。
例如:
Mydata = c(1, 5, 0, 10, 40, 30, 25, 20, 7, 34, 23, 55, 70, 42, 38, 22, 44, 33, 11, 17, 25)
结果的序列是:
c(40, 30, 25, 20, 7)
和c(44, 33, 11)
。我有一个数字向量,想要提取连续的递减值。此外,每个序列中的第一个值应该是>= 40
,最后一个值应该是<= 20
。
例如:
Mydata = c(1, 5, 0, 10, 40, 30, 25, 20, 7, 34, 23, 55, 70, 42, 38, 22, 44, 33, 11, 17, 25)
c(40, 30, 25, 20, 7)
和c(44, 33, 11)
。一种非惯用的过程化方法:
Mydata = c(1, 5, 0, 10, 40, 30, 25, 20, 7, 34, 23, 55, 70, 42, 38, 22, 44, 33, 11, 17, 25)
results = list()
# loop each element of the data vector to check if it can be the start of a result
for (x in 1:length(Mydata)) {
if (Mydata[x] >= 40) {
# start subresult list
subresult = c(Mydata[x])
i = 0
# add elements while decreasing
while (Mydata[x+i+1] < Mydata[x+i]) {
subresult = append(subresult, Mydata[x+i+1])
i = i + 1
}
# store in main result list if last element of subresult <= 20
if (subresult[length(subresult)] <= 20){
results[[length(results)+1]] = subresult
}
}
}
结果:
> results
[[1]]
[1] 40 30 25 20 7
[[2]]
[1] 44 33 11
使用“标准”方法创建基于值之间差异的分组变量(cumsum(...diff(...))
; 创建连续序列和拆分向量的分组变量)。使用tapply
按组检查条件。删除空列表元素。
L = tapply(x, cumsum(c(1L, diff(x) > 0)), \(v) if(v[1] >= 40 & tail(v, 1) <= 20) v)
L[lengths(L) != 0]
$`4`
[1] 40 30 25 20 7
$`8`
[1] 44 33 11
或者一次性地从tapply
中过滤结果:
Filter(Negate(is.null), tapply(x, cumsum(c(1L, diff(x) > 0)), \(v) if(v[1] >= 40 & tail(v, 1) <= 20) v))
使用data.table
的相同逻辑:
library(data.table)
data.table(x)[, if(x[1] >= 40 & x[.N] <= 20) x, by = .(g = cumsum(c(1L, diff(x) > 0)))]
g V1
<int> <num>
1: 4 40
2: 4 30
3: 4 25
4: 4 20
5: 4 7
6: 8 44
7: 8 33
8: 8 11
library(dplyr)
data.frame(x = Mydata) |>
filter(lag(x) > x | lead(x) < x) |>
mutate(id = cumsum(c(0, diff(x)) > 0)) |>
group_by(id) |>
filter(first(x) >= 40 & last(x) <= 20) |>
with(split(x, id)) |>
unname()
# [[1]]
# [1] 40 30 25 20 7
#
# [[2]]
# [1] 44 33 11
filter(lag(x) > x | lead(x) < x)
的目的吗? - Yang Yangx
可以是递减序列的一部分,如果 (a) 它前面的值大于它 (lag(x) > x
) 或者 (b) 它后面的值小于它 (lead(x) < x
)。如果既不满足 (a) 也不满足 (b),那么 x
就不是递减序列的一部分。 - Gregor Thomaslibrary(dplyr)
sapply(
as_tibble(Mydata) %>%
mutate(grp = c(F, diff(value) < 0),
con = consecutive_id(grp),
con = if_else(!grp & lead(con, default=F) != con, con + 1, con)) %>%
filter(any(grp) & first(value) >= 40 & last(value) <= 20, .by = con) %>%
group_split(con), "[", 1)
$value
[1] 40 30 25 20 7
$value
[1] 44 33 11
尝试这个序列:
step1 <- Mydata[cumsum(Mydata >= 40) > 0]
step2 <- step1[cumsum(step1 != cummin(step1)) < 1]
step2
# [1] 40 30 25 20 7
由你来决定是否 step2[length(step2)]
(也就是tail(step2,1)
)小于或等于20;如果是,那么就没问题,否则就没有到达那里的路径(我想是这样的)。
步骤:
第一步,从40开始:
Mydata[cumsum(Mydata >= 40) > 0]
# [1] 40 30 25 20 7 34 23 55 70 42 38 22 44 33 11 17 25
step1 <- Mydata[cumsum(Mydata >= 40) > 0]
第二步,我们可以沿着向量使用cummin
(累积最小值)找到运行最小值:
cummin(step1)
# [1] 40 30 25 20 7 7 7 7 7 7 7 7 7 7 7 7 7
并且通过这个,查找这是真实值的累积出现次数。
cumsum(step1 != cummin(step1)) < 1
# [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
step1[cumsum(step1 != cummin(step1)) < 1]
# [1] 40 30 25 20 7
我们需要使用cumsum(.) < 1
步骤,因为如果以下值之一实际匹配,我们可能会得到意外的匹配,例如
step1[11] <- 7
step1 == cummin(step1)
# [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
step1[step1 == cummin(step1)]
# [1] 40 30 25 20 7 7
这显然不在原始数据中。