R:如果值在 n 个元素之后重复,则更改数据框中的行

R: Change rows in dataframe if value repeats after n elements

我有一个非常大的数据框 Data,我需要检查 Energy 是否在 n 步内重复。可以看到n步之后Energy又归零了,就是我要改的。我在示例中将 n 设置为 10。我需要这样做,因为这是一个错误。

下面是我想出的代码,但是完成我的孔数据需要很长时间。

这是我的数据的摘录,您可以对其进行测试:

Date <- as.POSIXct(c("2017-06-03 01:00:00", "2017-06-03 01:15:00", "2017-06-03 01:30:00","2017-06-03 01:45:00","2017-06-03 02:00:00", "2017-06-03 02:15:00", "2017-06-03 02:30:00","2017-06-03 02:45:00","2017-06-03 03:00:00", "2017-06-03 03:15:00", "2017-06-03 03:30:00","2017-06-03 03:45:00","2017-06-03 04:00:00", "2017-06-03 04:15:00", "2017-06-03 04:30:00","2017-06-03 04:45:00","2017-06-03 05:00:00", "2017-06-03 05:15:00", "2017-06-03 05:30:00","2017-06-03 05:45:00","2017-06-03 06:00:00", "2017-06-03 06:15:00", "2017-06-03 06:30:00","2017-06-03 06:45:00","2017-06-03 07:00:00", "2017-06-03 07:15:00", "2017-06-03 07:30:00","2017-06-03 07:45:00","2017-06-03 08:00:00","2017-06-03 08:15:00"))
Energy <- c(0,0,0,0,150,149,149,146,147,146,142,5,0,0,0,0,5,14,37,55,54,94,82,127,197,NA,256,195,219,220)
Sun <-c(0,0,0,0,0,0,0,0,1,5,11,23,34,34,31,34,41,75,107,111,104,141,107,199,197,180,241,190,153,150)
Data <- data.frame(Date, Energy, Sun) 

列表中还有 NA 值,我也需要考虑它们。我为这个例子设置了一个值 NA。

n <- 10
for (m in c(1:length(Data[[1]]))) {

    if (Data$Energy[m] == 0 && !is.na(Data$Energy[m])) {

      for (l in c(1:n)) {
        if (m+l > length(Data[[1]])) {
          break()
        }
        if (Data$Energy[m] == Data$Energy[m + l] && !is.na(Data$Energy[m + l])) {
          for (j in c(1:(l-1))) {
            Data$Energy[m + j] <- 0

          }
        }
      }
   }
}

我确信有更简单的方法来解决这个问题,但我不知道如何解决,因为我是 R 的新手。我的意思是我使用 if 和 for so much,它不能 运行 快.代码 运行 的速度变快非常重要,因为我在 Dataframe 中有超过 2 000 000 个元素。

我得到了这个结果(我想要,但需要很长时间):

Data
                  Date Energy Sun
1  2017-06-03 01:00:00      0   0
2  2017-06-03 01:15:00      0   0
3  2017-06-03 01:30:00      0   0
4  2017-06-03 01:45:00      0   0
5  2017-06-03 02:00:00      0   0
6  2017-06-03 02:15:00      0   0
7  2017-06-03 02:30:00      0   0
8  2017-06-03 02:45:00      0   0
9  2017-06-03 03:00:00      0   1
10 2017-06-03 03:15:00      0   5
11 2017-06-03 03:30:00      0  11
12 2017-06-03 03:45:00      0  23
13 2017-06-03 04:00:00      0  34
14 2017-06-03 04:15:00      0  34
15 2017-06-03 04:30:00      0  31
16 2017-06-03 04:45:00      0  34
17 2017-06-03 05:00:00      5  41
18 2017-06-03 05:15:00     14  75
19 2017-06-03 05:30:00     37 107
20 2017-06-03 05:45:00     55 111
21 2017-06-03 06:00:00     54 104
22 2017-06-03 06:15:00     94 141
23 2017-06-03 06:30:00     82 107
24 2017-06-03 06:45:00    127 199
25 2017-06-03 07:00:00    197 197
26 2017-06-03 07:15:00     NA 180
27 2017-06-03 07:30:00    256 241
28 2017-06-03 07:45:00    195 190
29 2017-06-03 08:00:00    219 153
30 2017-06-03 08:15:00    220 150

提前感谢您的宝贵时间和帮助。

下面的代码比问题的解决方案更快。
它不是嵌套的 for 循环,而是仅使用 sapply 循环一次,并使用 dplyr::lead 确定重复次数。然后快速rowSums得到列向量Energy需要改变的元素

n <- 10

eq <- sapply(seq.int(n), function(l){
  z <- Data[["Energy"]] == dplyr::lead(Data[["Energy"]], n = l, default = 0)
  z | Data[["Energy"]] == 0
})

eq[is.na(eq)] <- FALSE
inx <- rowSums(eq) != 0
inx <- which(inx)
if(length(inx) > 0) {
  Data[["Energy"]][min(inx):max(inx)] <- 0
}
Data

在 运行 这段代码之后,不再需要创建的两个向量。

rm(eq, inx)    # tidy up

我不完全确定我是否理解正确,因为您对问题的描述与您的代码所做的不完全匹配,但您似乎希望将 window 的值设置为如果它以零结尾,则为零。如果是这种情况,您可以通过索引相当快速、轻松地完成此操作。

# Window size
n <- 10

# Find zeroes
zeros <- which(Data$Energy == 0)

# Find distance between zeroes
dist.zero <- diff(zeros)

# Generate index sequences of windows to change
idx <- unlist(lapply(which(dist.zero > 1 & dist.zero <= n), function(x) zeros[x]:zeros[x+1]))

# Replace values
Data$Energy[idx] <- 0