【问题标题】:Remove values which are surrounded by a certain number of NAs删除被一定数量的 NA 包围的值
【发布时间】:2015-07-02 21:38:48
【问题描述】:

我希望删除时间序列中的值,这些值被具有一定最小长度的 NA 块包围。

一些玩具数据:

x = seq(0,10,length.out = 100)
y = sin(x) + rnorm(length(x), mean=0, sd=0.1)
y[20:21] = rep(NA, 2)
y[50:54] = rep(NA, 5)
y[55:59] = seq(-0.1, -0.8, length.out = 5)
y[60:64] = rep(NA, 5)
y[90:91] = rep(NA, 2)

df <- data.frame(x, y)

我希望删除长度小于 10 且前后有 5 个或更多 NA 值的任何 y 值序列。

在我的玩具数据中,索引 55-59 处的 y 值 (a) 少于 10 个连续值,并且 (b) 两边 有 (b) 5 个 NA。因此,应该删除这组值。

其他值由较长的值块组成和/或被NA (

用红色绘制要删除的值:

library(ggplot2)
ggplot(data = df, aes(x, y)) +
  geom_line() +
  geom_line(data = df[55:59, ], color = "red")

【问题讨论】:

  • @MamounBenghezal 你能详细说明一下吗?
  • “长期真实价值”对我们来说很难实施。如果您可以为我们提供删除内容的严格标准,那就太好了,例如,“任何少于 10 个条目且夹在NAs 之间的数据序列”。也许编辑您的问题以给出一个更复杂的示例和您期望的输出。
  • 我当前的编辑是否澄清了问题,或者您认为需要进一步澄清? (我还在学习如何在这里提出问题)。
  • 这是我们应该能够使用的东西。当然,这个问题比原来的问题要复杂一些;-) 今天晚些时候我会找时间编辑我的答案。
  • 感谢您的帮助,我会尽量确保我下次第一次问得更好。将问题抽象到其关键组件是很困难的。

标签: r time-series missing-data


【解决方案1】:

首先,我们将定义您指定的两个阈值。 (我将第二个设置为 4,以便我们可以始终使用“”,而不是容易出错的“=”)。

threshold.data <- 10
threshold.NA <- 4

现在,关键是在is.na(y) 上使用运行长度编码。看?rle

foo <- rle(is.na(y))
foo

首先,我们通过检查原始数据在 NA 的位置来提取可能的“候选运行 NA”(因此 foo$values 将是 TRUE并且我们有指定的最小运行长度NAs:

candidate.runs.NA <- which(foo$values & foo$lengths>threshold.NA)

如果我们至少有两个 NA 运行超过阈值,我们只想继续:

if ( diff(range(candidate.runs.NA)) >= 2 ) {

我们的目标是找到我们要删除的非NA 数据的索引。为此,我们找到“(非NA)数据的候选运行”。第一步,包括上面确定的第一次和最后一次NA 运行之间的所有运行:

    candidate.runs.data <- seq(candidate.runs.NA[1]+1,tail(candidate.runs.NA,1)-1)

我们通过两个标准对此进行优化。一方面,我们只想要非NAs的序列,另一方面,这些序列的长度应该低于阈值:

    candidate.runs.data <- candidate.runs.data[!foo$values[candidate.runs.data] &
      foo$lengths[candidate.runs.data]<threshold.data]

在您的示例中,candidate.runs.data 现在只有一个条目 5。这意味着我们需要在is.na 序列的第 5 次运行中删除所有数据。为此,我们需要恢复实际的索引:

    indices.to.remove <- as.vector(sapply(candidate.runs.data,function(kk)
      seq(sum(foo$lengths[1:(kk-1)])+1,sum(foo$lengths[1:kk]))))

这有点复杂,因为我将它包装在一个 sapply() 调用中,以防我们得到 multiple candidate.runs.data 来删除。最后,我们删除这些数据:

    y[indices.to.remove] <- NA
}
plot(x,y,"l")

现在,这似乎可以满足您对特定示例的要求。您可能需要考虑在边界情况下要发生什么。例如,这假设您的系列以非NA 开头。如果您没有两次运行五个或更多NAs,而是三个五个,应该怎么办?在“长”运行之间有或没有更短的NA 运行?此脚本将在第一次和最后一次“长”运行之间最多 9 个非NAs 的运行视为公平游戏。

【讨论】:

  • 看来我必须编辑这个问题,你的答案有效,但这并不是我的想法。在时间序列和多个 NA 值中可以有多个这样的岛。我想我应该更明确一点,我的错。让我编辑问题。
  • 费了些力气才把它拆开,但这只是解决这个问题的绝妙方法。谢谢。
【解决方案2】:

您可以将时间序列视为字符串,并在此处使用正则表达式的优势。借助 stringr 包中的函数 str_locate_all 很容易解决问题。

st <- paste0(as.integer(is.na(df$y)), collapse = '')
# [1] "0000000000000000000110000000000000000000000000000111110000011111000000000000000000000000011000000000"
require("stringr")
str_locate_all(st, "1{5,}0{,10}1{5,}") 
# pattern of at least 5 ones, then not more than 10 zeros, then again not less than 5 ones

# output will be:
# [[1]]
#      start end
# [1,]    50  64

【讨论】:

  • 这也挺聪明的,是横向思维的绝佳例子。
  • 谢谢)我最近不得不使用正则表达式,所以我的想法是朝这个方向工作)
【解决方案3】:

另一个rle的可能性:

NA的运行长度:

r <- rle(is.na(y))

values (sensu rle) 的非NA (FALSE) 应该从数据中删除(运行时间短于 10 次,并且之前和之后运行超过 4 次的 NA ) 替换为TRUE

r$values[!r$values & r$lengths < 10 &
           c(0, head(r$lengths, -1)) > 4 &
           c(tail(r$lengths, -1), 0) > 4] <- TRUE

更新后的rle values 然后与lengths 一起使用以生成布尔索引以用NA 替换相关的y 值:

y[rep(r$values, r$lengths)] <- NA

使用 OP 的绘图代码:

【讨论】:

  • 那就更简单了。也感谢您编辑原始问题以使其更易于理解。
  • 没问题!我认为您的问题很有趣,我希望它可以通过一些编辑来吸引更广泛的受众。
【解决方案4】:

complete.cases() 对你有用吗? 此功能使所有带有 NA 的行消失.. 也许对你来说太激烈了......

【讨论】:

  • 我不是要让 NA 消失,而是要让夹在 NA 之间的数据消失。可以这样想,传感器发生故障,他们尝试将其恢复几天,但它仍在产生异常数据,然后他们再次将其取下,直到修复为止。这是我要删除的中间数据。
猜你喜欢
  • 2018-12-22
  • 2019-09-13
  • 2021-03-06
  • 1970-01-01
  • 1970-01-01
  • 2021-11-26
  • 1970-01-01
  • 2018-08-04
相关资源
最近更新 更多