R:按重叠容差标准删除间隔

时间:2016-02-23 07:55:15

标签: r

我正在寻找创建一些掩码的解决方案,我可以根据某些标准删除一些数据(例如data.frame中的行),例如:

a <- c(0,0,0,3,5,6,3,0,0,0,4,5,8,5,0,0,0,0,0)
mask <- a == 0
mask
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE TRUE TRUE

在我的实际问题中,这种切割过于刺耳,我希望有一些更顺畅的过渡。想法:我想在非零之前包含一些零,并且在非零之后添加一些零。简单的方法:如果我有这个向量,我想将每个与FALSE相邻的TRUE切换为FALSE,这会为数据添加一个重叠的容差区域。而不是

a[!mask]
[1] 3 5 6 3 4 5 8 5

我宁愿有类似

的东西
a[!mask]
[1] 0 3 5 6 3 0 0 4 5 8 5 0 

或(增加公差窗口的大小)

a[!mask]
[1] 0 0 3 5 6 3 0 0 0 4 5 8 5 0 0

在最后一种情况下,中间出现三个零,因为左边和右边的公差开始重叠。我的问题:有没有人有一个好方法,如何编写一个函数来创建这样一个具有重叠容差的掩码?

[编辑] 有一次我意识到我最初的问题中的错误(感谢@tospig)在我的初始帖子中,我完全使中间部分的零数错误!对困惑感到抱歉。因此,为了澄清:在公差窗口为1的情况下,中间确实应该有两个零:一组来自右边的有效数据,一组来自左边的有效数据。抱歉混乱!

所以,尽管来自@tospig的非常酷的方法(我必须牢记这一点)@agenis的解决方案完美地解决了我的问题!

2 个答案:

答案 0 :(得分:2)

我认为我会使用3阶经典移动平均线来简单地扩展&#34;非零&#34;一个向左,一个向右。就这么简单。你只需要弄清楚你对矢量的第一个和最后一个点做什么就变成了NA(在我的例子中我把它们变成零)。

你有你想要的结果(对于一个更大的面具你采取5而不是3):

a <- c(0,0,0,3,5,6,3,0,0,0,4,5,8,5,0,0,0,0,0)
library(forecast)
a.ma <- ma(a, 3)
a.ma[is.na(a.ma)] <- 0
mask <- a.ma == 0
a[!mask]
#### [1] 0 3 5 6 3 0 0 4 5 8 5 0

然后,您可以轻松地将这段代码转换为函数。

[编辑] 此方法无法保证零总数的保留(请参阅其他注释以澄清OP初始问题)

答案 1 :(得分:1)

这是一个允许您指定容差的解决方案。目前它没有“重叠”零。

我们可以使用data.table结构(或data.frame,但我喜欢使用data.table)并控制我们想要在正数集之间保留多少个零。我们可以指定任何tolerance值,但如果它大于零序列,则只返回最大连续零数。

a <- c(0,0,0,3,5,6,3,0,0,0,4,5,8,5,0,0,0,0,0)

library(data.table)
tolerance <- 1

dt <- data.table( id = seq(1, length(a), by = 1),
                  a = a)

## subset all the 0s, with their 'ids' for joining back on 
dt_zero <- dt[a == 0]

## get the positions where the difference between values is greater than one, 
## and create groups based on their length
changed <- which(c(TRUE, diff(dt_zero$id) > 1))
dt_zero$grps <- rep(changed, diff(c(changed, nrow(dt_zero) + 1)))

## we only need the 'tolerance' number of zeros
## if 'tolerance' is greater than number of entries in a group,
## it will return 'na'
dt_zero <- dt_zero[  dt_zero[ order(id) , .I[c(1:tolerance)], by=grps ]$V1, ]

## join back onto original data.table, 
## and subset only relevant results
dt_zero <- dt_zero[, .(id, a)][ dt  , on = "id"][(is.na(a) & i.a > 0) | a == 0]

res <- dt_zero$i.a
res
# [1] 0 3 5 6 3 0 4 5 8 5 0

## try different tolerances
tolerance <- 2
...
# 0 0 3 5 6 3 0 0 4 5 8 5 0 0

tolerance <- 6
...
# 0 0 0 3 5 6 3 0 0 0 4 5 8 5 0 0 0 0 0