从向量中提取递增和递减序列
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
并将其与前面的 i-1
进行比较
- 如果
i
大于或等于i-1
,则序列符合递增;如果 i
小于或等于 i-1
,则序列符合递减条件。
- 通过提取第
i
个元素 为这样的部分存储 increase/decrease
- 一旦下一个值大于(对于递减序列)或小于(对于递增序列)前一个值,每个序列就会终止
- 如果从
i-1
到 i
没有变化(即 i-1
和 i
相等),序列将继续,就像当 a NA
发生
由于 v
是一阶差分级数,提取的元素 i
(第 3 个要点)已经代表 increase/decrease。现在,我不想限制序列的长度,因此一个序列可能已经由两个元素给出了。
我设想将 v
的第 i
个元素存储在一个新向量中(例如 inc.v
和 dec.v
),然后找到最大值和平均值 increase/decrease 的序列,以及这些序列的最大和平均长度。这些元素应该存储在 inc.v
或 dec.v
中,相对于它们在 v
中的原始位置,以便我可以追溯它们。 inc.v
和dec.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
}
}
if
和 else 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()
语句应该保护 TRUE
或 FALSE
参数?!
很高兴听到你的想法!
您真的应该尝试矢量化方法,这可能是一种更清晰的查找递增或递减序列运行的方法:
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
然后您可以轻松找到最好的或做任何您想做的事。
我有一个包含 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
并将其与前面的i-1
进行比较
- 如果
i
大于或等于i-1
,则序列符合递增;如果i
小于或等于i-1
,则序列符合递减条件。 - 通过提取第
i
个元素 为这样的部分存储 increase/decrease
- 一旦下一个值大于(对于递减序列)或小于(对于递增序列)前一个值,每个序列就会终止
- 如果从
i-1
到i
没有变化(即i-1
和i
相等),序列将继续,就像当 aNA
发生
由于 v
是一阶差分级数,提取的元素 i
(第 3 个要点)已经代表 increase/decrease。现在,我不想限制序列的长度,因此一个序列可能已经由两个元素给出了。
我设想将 v
的第 i
个元素存储在一个新向量中(例如 inc.v
和 dec.v
),然后找到最大值和平均值 increase/decrease 的序列,以及这些序列的最大和平均长度。这些元素应该存储在 inc.v
或 dec.v
中,相对于它们在 v
中的原始位置,以便我可以追溯它们。 inc.v
和dec.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
}
}
if
和 else 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()
语句应该保护 TRUE
或 FALSE
参数?!
很高兴听到你的想法!
您真的应该尝试矢量化方法,这可能是一种更清晰的查找递增或递减序列运行的方法:
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
然后您可以轻松找到最好的或做任何您想做的事。