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
我有一个非常大的数据框 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