从向量中提取递增和递减序列

Extract increasing and decreasing sequences from vector

我有一个包含 2718 个观察值的数据框,其中一列是感兴趣的。这是用 diff() 创建的第一个差异系列。为了方便起见,让我们创建一个类似于数据的假向量,并假装 v 是一阶差分序列。引入 NA 使其与原始数据相似。

# Create fake first difference series vector v
v <- runif(2718, -0.05, 0.05)
v <- append(NA, diff(v))

# Insert NAs at the beginning and end
v[c(1:8, 2712:2718)] <- NA

# Insert some NAs at random places in v
ind <- which(v %in% sample(v, 7))
v[ind] <- NA

我对 v 显示增加和减少行为的序列感兴趣。具体来说,我想分别提取连续增加和减少的 v 序列。在递增序列中,v 的每个元素都不能小于其前一个元素,在递减序列中,v 的每个元素都不能大于其前一个元素。在绘制 v 时试着想象一下:只要曲线不下降(即上升或保持水平),它就是一个递增的序列,而只要曲线不增加(即下降或保持水平),它就是递减序列。

澄清一下,程序可以这样解释:

由于 v 是一阶差分级数,提取的元素 i(第 3 个要点)已经代表 increase/decrease。现在,我不想限制序列的长度,因此一个序列可能已经由两个元素给出了。

我设想将 v 的第 i 个元素存储在一个新向量中(例如 inc.vdec.v),然后找到最大值和平均值 increase/decrease 的序列,以及这些序列的最大和平均长度。这些元素应该存储在 inc.vdec.v 中,相对于它们在 v 中的原始位置,以便我可以追溯它们。 inc.vdec.v中的每一个序列用NA个元素分隔应该很容易区分。

我尝试用 for 循环和条件语句编写此代码,但没有走得太远:

inc.v <- NULL
dec.v <- NULL
for (i in 2:length(v)) {
  if(!v[i] < v[i-1] | is.na(v[i])) {
    inc.v[i] <- v[i]
  } else if (!v[i] > v[i-1] | is.na(v[i])) {
    dec.v[i] <- v[i]
  } else {
    next
  }
}

ifelse if 语句代表第五个要点。我知道当 i 等于 i-1 时,它既可以作为递增序列也可以作为递减序列,并且应该将其添加到之前存储的任何序列中。我只是不知道如何实现它。我认为序列会很短,因为数据很嘈杂,decrease/no 没有增长的时期不会持续很长时间。因此,尝试使用例如此操作可能是个好主意。 50/100 点移动均值:

# A symmetric 50 points moving average for v
f50 <- rep(1/51,51)
v_smooth <- filter(v, f50, sides = 2)

当 运行 循环截至目前,第一个条件的评估导致 NA,给我错误:

Error in if (!v[i] < v[i - 1] | is.na(v[i])) { : 
  missing value where TRUE/FALSE needed

我不太明白这里发生了什么,因为 is.na() 语句应该保护 TRUEFALSE 参数?!

很高兴听到你的想法!

您真的应该尝试矢量化方法,这可能是一种更清晰的查找递增或递减序列运行的方法:

library(data.table)
data <- as.data.table(v)
data[, vl := shift(v, 1)]
data[, runs := rleid(vl > v)]

使用 data.table 库

这是尝试回答您的问题(请注意,我稍微更改了您的示例)

# Create fake first difference series vector v
v <- runif(2718, -0.05, 0.05)
v <- append(NA, diff(v))

# Insert NAs at the beginning and end
v[c(1:8, 2712:2718)] <- NA

# Insert some NAs at random places in v
v[sample(1:length(v), 7)] <- NA

# a couple of equal values
v[10:15] <- 1


# create an empty vector of character
out <- character(length(v)-1)
tmp <- diff(v)
# known increase
out[tmp>0] <- "I"
# known decrease
out[tmp<0] <- "D"
# no change
out[tmp == 0] <- "E"
# known NA
out[is.na(tmp)] <- NA
# let change E for the right value (I or D) if no way to know, I by default
for (i in 1:length(out)) {
  if (!is.na(out[i]) & out[i] == "E") {
    if (i==1) {
      out[i] <- "I"
    } else {
      if (is.na(out[i-1])) {
        out[i] <- "I"
      } else out[i] <- out[i-1]
    }
  }
}

# Retrieve values 
dec.v <- inc.v <- rep(NA_real_, length(v))
idi <- which(out == "I")+1
inc.v[idi] <- v[idi]
idd <- which(out == "I")+1
dec.v[idd] <- v[idd]

关于循环中的错误,您必须先更改逻辑测试中元素的顺序, is.na(),这样就不会触发测试而 v[i] 实际上是 NA.

希望对您有所帮助 :)

你应该向量化而不是循环,并在你的差异向量上使用直接条件来创建包含你的 inc 和 dec 的新列。当您想要平滑时,它的工作原理相同。这是一个例子:

library(data.table)
plouf <- setDT(list( v = v, diff = c(NA,diff(v))))
plouf[diff > 0,inc := v]
plouf[diff < 0, dec := v]

f50 <- rep(1/51,51)
plouf[,v_smooth := filter(v, f50, sides = 2)]
plouf[,diff_smooth :=c(NA,diff(v_smooth))]

plouf[diff_smooth > 0,inc_smooth := v_smooth]
plouf[diff_smooth < 0, dec_smooth := v_smooth]

要提取减少的值,您需要创建一个分组变量,该变量在 diff 的每次变化时增加,因此我们可以使用 by[=13= 对每个增加或减少的序列执行任何操作]

plouf[,grouptmp := abs(c(NA,diff(ifelse(diff>0,1,0))))]
plouf[is.na(grouptmp),grouptmp:= 0]
plouf[,group := cumsum(grouptmp)]

plouf[,decvalue := dec[.N] - dec[1], by = group]
plouf[,incvalue := inc[.N]-inc[1], by = group]

                  v          diff           inc           dec group     decvalue grouptmp
   1:            NA            NA            NA            NA     0           NA        0
   2:            NA            NA            NA            NA     0           NA        0
   3:            NA            NA            NA            NA     0           NA        0
   4:            NA            NA            NA            NA     0           NA        0
   5:            NA            NA            NA            NA     0           NA        0
   6:            NA            NA            NA            NA     0           NA        0
   7:            NA            NA            NA            NA     0           NA        0
   8:            NA            NA            NA            NA     0           NA        0
   9: -0.0344851657            NA            NA            NA     0           NA        0
  10:  0.0788633499  0.1133485156  0.0788633499            NA     0           NA        0
  11: -0.0415118591 -0.1203752090            NA -0.0415118591     1  0.000000000        1
  12:  0.0557818390  0.0972936981  0.0557818390            NA     2           NA        1
  13: -0.0314433977 -0.0872252367            NA -0.0314433977     3  0.000000000        1
  14:  0.0098391432  0.0412825409  0.0098391432            NA     4           NA        1
  15: -0.0147885296 -0.0246276728            NA -0.0147885296     5  0.000000000        1
  16: -0.0009157661  0.0138727635 -0.0009157661            NA     6           NA        1
  17:  0.0303060166  0.0312217827  0.0303060166            NA     6           NA        0
  18: -0.0384165912 -0.0687226078            NA -0.0384165912     7 -0.005185349        1
  19: -0.0436019399 -0.0051853487            NA -0.0436019399     7 -0.005185349        0
  20:  0.0821260908  0.1257280307  0.0821260908            NA     8           NA        1
  21: -0.0172987636 -0.0994248545            NA -0.0172987636     9 -0.003255037        1
  22: -0.0205538005 -0.0032550369            NA -0.0205538005     9 -0.003255037        0
  23: -0.0114417208  0.0091120797 -0.0114417208            NA    10           NA        1
  24:  0.0524503477  0.0638920686  0.0524503477            NA    10           NA        0
  25: -0.0105871856 -0.0630375333            NA -0.0105871856    11 -0.047042624        1
  26: -0.0576298093 -0.0470426237            NA -0.0576298093    11 -0.047042624        0
  27:  0.0031608195  0.0607906288  0.0031608195            NA    12           NA        1
  28: -0.0009828784 -0.0041436979            NA -0.0009828784    13  0.000000000        1
  29:  0.0167153471  0.0176982255  0.0167153471            NA    14           NA        1
  30:  0.0088964230 -0.0078189241            NA  0.0088964230    15 -0.033234568        1
  31:  0.0065035882 -0.0023928348            NA  0.0065035882    15 -0.033234568        0
  32: -0.0243381450 -0.0308417332            NA -0.0243381450    15 -0.033234568        0

然后您可以轻松找到最好的或做任何您想做的事。