假设我有这个琐碎的数据表。
library(data.table)
dt <- data.table(
day = 1:10,
a = c(0, 1, 10, 2, 2.5, 2.3, 2.7, 2.9, 5, 8)
)
我想根据a
上的某些条件对其进行过滤。在这种情况下,a
变化的时刻大于3。这是微不足道的:
dt[abs(a - shift(a)) >= 3]
但是,我不想长时间丢失信息。因此,如果没有遇到上述情况,我需要确保没有超过3天的“过滤掉”的延伸。
在上述情况下,基于a
的条件满足:
dt[, abs(a - shift(a)) >= 3]
# [1] NA FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE
-----------------------------
请注意,结尾处会有很长的FALSE
。我想到的最好的是
dt[, abs(a - shift(a)) >= 3 | .I %% 3 == 0]
# [1] NA FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE TRUE
---- ----
(即确保每三行都被接受),但是它不会放置最好或最少的其他行。
最佳结果将是一个过滤器,该过滤器将FALSE
的那一行与中间的一个TRUE
(或必要的数目)分隔开。
# [1] NA FALSE TRUE TRUE FALSE FALSE TRUE FALSE FALSE TRUE
----
答案 0 :(得分:4)
也许有人可以在data.table
中复制它,但这是您可能正在寻找的逻辑。为了清楚起见,我分离了test
,run_length
和result
,但是如果需要,可以将逻辑组合或包装在函数中。
这会将所有行保留在
test
是TRUE
OR
N
和TRUE
的每个条纹中的第FALSE
行。
这样,所有TRUE
都被第一个条件保留,第二个条件捕获了每个条纹的第N
个元素,因此也捕获了一些FALSE
。 -
library(dplyr)
N <- 3
dt %>%
mutate(
test = abs(a - lag(a)) >= N, # flag change(a) >= N
run_length = sequence(rle(test)$lengths), # seq along streaks of TRUE and FALSE
result = test | run_length %% N == 0
) # %>%
# filter(result) # uncomment this to get final dt
day a test run_length result
1 1 0.0 NA 1 NA
2 2 1.0 FALSE 1 FALSE
3 3 10.0 TRUE 1 TRUE
4 4 2.0 TRUE 2 TRUE
5 5 2.5 FALSE 1 FALSE
6 6 2.3 FALSE 2 FALSE
7 7 2.7 FALSE 3 TRUE
8 8 2.9 FALSE 4 FALSE
9 9 5.0 FALSE 5 FALSE
10 10 8.0 TRUE 1 TRUE
在data.table
中(我想)-
dt[, (test <- abs(a - shift(a)) >= N) | sequence(rle(test)$lengths) %% N == 0]
[1] NA FALSE TRUE TRUE FALSE FALSE TRUE FALSE FALSE TRUE
答案 1 :(得分:2)
如果您是真的意思
没有大于3个 行
的“过滤出”拉伸
然后尝试一下。您的abs(a - shift(a))
将第一个条件保留为NA
,这与cumsum
步骤很混乱,因此我们可以将其中一个替换为
c(FALSE, abs(diff(a)) >= 3)
.I > 1 & abs(a - shift(a)) >= 3
abs(a - shift(a, fill = a[1])) >= 3
这确保第一行不会被忽略。对于本演示,我将使用第三个示例,因为它与您对shift
的用法保持一致,而您更喜欢进行维护。
挑战就在于:给定一个向量,确定元素,使选定元素之间的间隔永远不会超过某个定义的值(在这种情况下为3)。定义no_further
说“从上一个“ true”起没有更多的than
步骤了”。 (也许我需要处理这个措辞。)
no_further(4:10, than = 3) # expect: '7'
# [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE
v <- c(4, 6, 8, 9, 10)
### 4 to 8 is too far, need '6' to be included
### 6 to 8 is good
### 6 to 9 is good, but since 6 to 10 is too far, need '9' to be included
no_further(v, than = 3) # expect: '6', '9'
# [1] FALSE TRUE FALSE TRUE FALSE
no_further <- function(x, than) {
i <- 1
out <- logical(length(x))
while (i < length(x)) {
d <- x - x[i]
if (!is.na(toobig <- which(d > than)[1])) {
out[ toobig-1 ] <- TRUE
i <- toobig-1
} else break
}
out
}
然后,我们可以在每组数据(定义为从其他条件开始的一组数据)中使用此功能:
library(magrittr) # solely for demo with %>% pipes, not needed for the function
dt %>%
.[, keep := abs(a - shift(a, fill = a[1])) >= 3 ] %>%
.[, grp1 := cumsum(keep) ] %>%
.[, keep2 := keep | no_further(day, than = 3), by = "grp1" ]
# day a keep grp1 keep2
# 1: 1 0.0 FALSE 0 FALSE
# 2: 2 1.0 FALSE 0 FALSE
# 3: 3 10.0 TRUE 1 TRUE
# 4: 4 2.0 TRUE 2 TRUE
# 5: 5 2.5 FALSE 2 FALSE
# 6: 6 2.3 FALSE 2 FALSE
# 7: 7 2.7 FALSE 2 TRUE
# 8: 8 2.9 FALSE 2 FALSE
# 9: 9 5.0 FALSE 2 FALSE
# 10: 10 8.0 TRUE 3 TRUE
我使用magrittr
只是为了使内容逐行可读,对此没有严格的要求。
答案 2 :(得分:0)
好的,所以这可能是最不优雅的解决方案,但以您的示例为例:
temp <- dt[,abs(a - shift(a)) >=3]
for(i in 3:length(temp)) {
if(!(temp[i]|temp[i-1]|temp[i-2])) {
temp[[i]] <- T
}
}
答案 3 :(得分:0)
这是我的data.table
方法
(编辑:在阅读了其他答案之后,它遵循@shree的data.table方法的逻辑)。
#create a column which is TRUE when a changes >= 3
dt[, change_3 := (abs(a - shift(a)) >= 3)]
#create groups based on value the change_3 column
dt[, no_change_gr := rleidv( dt$change_3 ) ]
#create rownumbers within each group of no_change_gr
dt[, no_change_rowid := rowid( no_change_id )]
#mark rownumbers where %%3 == 0 with TRUE
dt[no_change_rowid %% 3 == 0, false_3 := TRUE]
#filter out rows where either change_3 or false_3 is TRUE
dt[ change_3 == TRUE | false_3 == TRUE, .(day,a)][]
# day a
# 1: 3 10.0
# 2: 4 2.0
# 3: 7 2.7
# 4: 10 8.0
我没有合并行,因此您可以查看每个步骤的结果。 如果输出符合预期,则可以将操作合并为较短的代码(行数更少)