R语言 提取具有特定开始值和结束值的递减序列

vpfxa7rd  于 2023-04-27  发布在  其他
关注(0)|答案(5)|浏览(122)

我有一个数字向量,我想提取递减值的运行。此外,每个序列中的第一个值应该是>= 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)

wgeznvg7

wgeznvg71#

非惯用的过程方法:

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
vs3odd8k

vs3odd8k2#

使用“标准”方式创建基于值之间差异的分组变量(cumsum(...diff(...));Create grouping variable for consecutive sequences and split vector)。使用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
xuo3flqw

xuo3flqw3#

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
hsvhsicv

hsvhsicv4#

使用dplyr s consecutive_id得到分组,使用group_split分离分组,使用周围的sapply提取分组作为向量

library(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
inn6fuwd

inn6fuwd5#

试试这个序列:

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将由您决定;如果是,你很好,如果不是,那么就没有一条通往那里的道路(我认为)。
演练:
1.第一步,从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]

1.第2步,我们可以沿着向量使用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

这显然不在原始数据中。

相关问题