当循环超出向量时过滤 NA 值(定义循环边界)

Filtering NA values when loop goes outside vector (defining loop boundaries)

我正在 运行 设置两个嵌套的 for 循环来检查向量的每一行,如果下面的 10 行中的任何一行比下面的 3 行大 3 个点。如果为真,则在新创建的二进制向量上记录 1。 (我知道这听起来很复杂,但是这种比较允许测试时间序列中的条件以用于交易目的)

例如,对于第一行,要检查是否:

(顺便说一句,我需要循环,想法是 运行 这在数千行上,而不仅仅是 20 行)

以下代码运行良好,但有一个不需要的特性,即当其中一个循环超出向量时会产生 NA 值。

df <- data.frame(  LastPrice = c( 1221, 1220, 1230, 1217, 1216,  1218 , 1216, 1216, 1217, 1220, 1219, 1218, 1220, 1216, 1217, 1218, 1218, 1207, 1206, 1205))

df$StrongMoveBinary[j] <- 0
for(j in 1:20) {
  tmp <- 0
  for (i in 1:10) { 
    tmp <- tmp + ifelse (df$LastPrice[j+i] - df$LastPrice[j+i+3] > 3, 1, 0)}
  df$StrongMoveBinary[j] <- tmp>0}

//Note: purpose of tmp variable is to record every occasion that LastPrice > LastPrice 3 rows below, rather than just the last instance

该代码创建 StrongMoveBinary = 1 1 0 0 1 1 1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA。那是 13 个 NA。但是有足够的数据只有 4 个 NA。其他 9 个 NA 是我编码不当的结果。为了解决这个问题,我修改了代码,以在给定 "j" 的值的情况下限制 "i" 的值,从而停止 "i" 循环以在向量外循环。

df$StrongMoveBinary[j] <- 0
for(j in 1:20) {
  x <- 0
  if (j <= 10) {x=10}
  if (j > 10) {x=20-j}
  tmp <- 0
  for (i in 1:x) { 
    tmp <- tmp + ifelse (df$LastPrice[j+i] - df$LastPrice[j+i+3] > 3, 1, 0)}
  df$StrongMoveBinary[j] <- tmp>0}

很遗憾,它不起作用。 StrongMoveBinary 仍然有 13 个 NA。任何想法将不胜感激!谢谢。

我认为混淆的最大问题是缺少命名变量。你有几个参数(看下面 3 行,如果下面有 10 行,数据框中的行数,有多少要检查的差异),但你只是到处使用数字,这很难保持直线。你不应该写 20,你应该写 nrow(df) - 这样,相同的代码就可以在你的 20 行示例和你的数千行实际数据上工作。如果任何参数发生变化,您只能在一个地方进行更改。

window = 10     # up to this far below the current row
rows_below = 3  # check against this far down
min_diff = 3    # for a difference at least this big

现在我们将使用这些来显式计算循环的边界。 pmin 是一个非常方便的函数,用于确保我们不会超出数据范围。 (当然,应该非常仔细地检查这些定义的准确性——这就是我在写关于第 17 个条目是否应该 NA 的挑剔评论时所做的。)

base_rows = 1:(nrow(df) - rows_below - 1)  # can't check more than this

# for a given base row, this is the maximum row to start checking against
candidate_max = pmin(base_rows + window, nrow(df) - rows_below)

# pre-allocate a vector of results
StrongMoveBinary = integer(length = length(base_rows))

所有设置完成后,我们就可以开始测试了:

for (i in seq_along(base_rows)) {
    StrongMoveBinary[i] = as.numeric(
        any(
            df$LastPrice[(i + 1):candidate_max[i]] - 
                df$LastPrice[((i + 1):candidate_max[i]) + rows_below] > min_diff
        )
    )
}

让我们看看我们得到了什么:

StrongMoveBinary
# [1] 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1

我完全放弃了 NA。如果您更愿意拥有它们,请预先分配 StrongMoveBinary = rep(NA, nrow(df)) 而不是我在顶部所做的方式。


最后,也许我们想把它变成一个可以应用于任何向量的函数。设置参数非常简单。唯一的编辑是让它在向量(length())上工作,而不是数据框的特定行(nrow())。

strong_indicate = function(x, window = 10, rows_below = 3, min_diff = 3) {
    base_rows = 1:(length(x) - rows_below - 1)  # can't check more than this

    # for a given base row, this is the maximum row to start checking against
    candidate_max = pmin(base_rows + window, length(x) - rows_below)

    # pre-allocate a vector of results
    StrongMoveBinary = integer(length = length(base_rows))

    for (i in seq_along(base_rows)) {
        StrongMoveBinary[i] = as.numeric(
            any(
                x[(i + 1):candidate_max[i]] - 
                    x[((i + 1):candidate_max[i]) + rows_below] > min_diff
            )
        )
    }
    return(StrongMoveBinary)
}

我们可以在数据列上调用它:

strong_indicate(x = df$LastPrice)
# [1] 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1

我们可以探索其他值的作用:

strong_indicate(x = df$LastPrice, min_diff = 12)
# [1] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1

strong_indicate(x = df$LastPrice, window = 5)
# [1] 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1