【问题标题】:Extract increasing and decreasing sequences from vector从向量中提取递增和递减序列
【发布时间】:2018-02-23 16:47:11
【问题描述】:

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

# 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,并将其与前一个i-1进行比较
  • 如果i 大于或等于i-1,则序列符合递增条件;如果i 小于或等于i-1,则序列符合递减条件。
  • 通过提取ith 元素存储此类部分的增加/减少
  • 一旦下一个值大于(对于递减序列)或小于(对于递增序列)前一个值,每个序列就会终止
  • 如果从到i-1i 没有变化(即i-1i 相等),则序列继续,就像NA 发生时一样

由于v 是第一个差分系列,提取的元素i(第三个要点)已经代表了增加/减少。目前,我不想限制序列的长度,因此一个序列可能已经由两个元素给出。

我想将vith 元素存储在一个新向量中(例如inc.vdec.v),然后找到序列的最大和平均增加/减少,以及最大和平均长度这些序列。元素应该存储在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 时,它既可以作为递增序列也可以作为递减序列,并且应该将其添加到之前存储的任何序列中。我只是不知道如何实现它。我认为序列会很短,因为数据很嘈杂,而且不减少/不增加的时期不会持续很长时间。因此,最好也尝试此操作,例如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 参数?!

很高兴听到你的想法!

【问题讨论】:

  • 我真的不知道我是否理解这个问题。 dec.v &lt;- v[which(diff(v) &lt; 0)] 有什么问题? diff(v) &gt; 0 也一样?请注意,由于您有 NA 值,因此您将需要 which

标签: r sequence na difference


【解决方案1】:

您应该向量化而不是循环,并在差异向量上使用直接条件来创建包含您的 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 对每个递增或递减序列执行任何操作

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

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

【讨论】:

  • 这看起来很棒!使用na.contigiuous,我现在可以在inc (inc_smooth) 和dec (dec_smooth) 中寻找最长的增加/减少拉伸,而没有足够的NA。但是,这忽略了它们的实际值。你能想出一种方法来提取所有伸展的最大增加/减少,而不管伸展有多长?基本上,对于所有延伸(不被 NA 中断的数字),您分别从第一个值中减去最后一个值,然后您必须为此操作找到 max(对于递增系列) min(对于递减系列)。有什么想法吗?
  • @ArneBrandschwede 我在答案中添加了几行来解决您的问题。变量 decvalue 是最后一个递减值和第一个递减值的差值,增加也是一样
【解决方案2】:

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

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

使用 data.table 库

【讨论】:

  • 从未使用过这些函数,但我猜它们是这样工作的:shiftv 系列向下推一行并将其存储为vl 列。现在,每个 v 观察值与其在 vl 列中的前一个值配对。 Rleid 获取我每次更改的 ID,vl &gt; v 条件评估为 TRUE/FALSE。有没有一种聪明的方法来改变runs 列,以便只使用两个ID 来增加/减少系列?可能通过创建两个单独的列,一个用于增加运行,一个用于减少运行?这也可能有助于解决原始帖子中的第五个要点
  • 您可以只使用条件vl&gt;v 将两个系列分开。它们应该按顺序递增,因此如果您想分隔 ID,可以执行 data[vl&gt;v, increasingRuns := runs / 2]data[vl&lt;v, decreasingRuns := (runs + 1)/2] 之类的操作
【解决方案3】:

这里试图回答你的问题(注意我稍微改变了你的例子)

# 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] 实际上是@ 时不会触发任何测试987654324@.

希望对你有所帮助:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    相关资源
    最近更新 更多