根据特定规则替换NA值

时间:2019-06-01 05:52:17

标签: r

我正在研究一个数据集,其中基于从临床记录收集的数据计算出分数。在某些情况下,该数据已被省略,因此无法计算分数并将其记录为NA。

在某些情况下,我可以将NA值替换为先前的值。这种方法的局限性是:

如果得分为NA,请检查上一个和下一个值是否为NA。如果上一个和下一个值都不是NA,则插入这些得分的平均值。

如果得分为NA,请检查上一个和下一个值是否为NA。如果仅先前值不是NA,则将第一个NA值替换为先前值。

如果序列中有两个或多个NA值,则仅替换第一个NA值,而将其他值保留为NA。

我已经尝试过函数zoo :: na.locf(),但是它会不加选择地替换所有NA或限制替换比多个NA大的间隙。

我查看了整齐的填充,但是文档中没有包含有关设置填充限制的任何内容。

对于以下数据:

ID,episode,score
1,1,1
1,2,1
1,3,1
1,4,NA
1,5,NA
1,6,NA
1,7,2
1,8,NA
1,9,4
1,10,NA
2,1,NA
2,2,2
2,3,3
2,4,4
2,5,NA
2,6,NA
2,7,3
2,8,NA
2,9,NA
2,10,NA

因此,我认为使用下面的嵌套ifelse突变是正确的,但是我缺少有关可用于将替换项限制为一定数量的NA值的函数的知识

data <- data %>%
group_by(ID) %>%
arrange(episode) %>%
mutate(score = ifelse(is.na(score) & lag(!is.na(score)) & lead(!is.na(score)), average(sum(lag(score),lead(score))),
    ifelse(is.na(score) & lag(!is.na(score)) & lead(is.na(score)), lag(score), ...) #And this is where I get stuck as I am unsure how to code for NA runs greater than 1

我的预期输出是:

ID,episode,score
1,1,1
1,2,1
1,3,1
1,4,*1
1,5,NA
1,6,NA
1,7,2
1,8,*3
1,9,4
1,10,*4
2,1,NA
2,2,2
2,3,3
2,4,4
2,5,*4
2,6,NA
2,7,3
2,8,*3
2,9,NA
2,10,NA

添加

*可以清楚地将值复制到何处。

3 个答案:

答案 0 :(得分:2)

如果我理解正确,那么只有两个规则可以为每个NA替换列score中的ID值:

  1. 如果有一个NA值,请用之前和之后(非NA)值的平均值替换。
  2. 如果存在两个或多个NA值序列,请仅用前一个(非NA)值替换第一个NA值,并保留其他NA值。

这两个规则的实现可以归结为两个简单的mutate()语句: 首先,根据规则1通过用NA调用zoo::na.approx()来替换所有单个maxgap = 1L值。因此,仅保留具有两个以上NA值的序列(如果有)。最后,使用NAif_else()将每个lag()值替换为前面的值,以符合规则2。

library(dplyr)
data %>% 
  group_by(ID) %>% 
  mutate(new_score = zoo::na.approx(score, x = row_number(), maxgap = 1, na.rm = FALSE)) %>% 
  mutate(new_score = if_else(is.na(new_score), lag(new_score), new_score))
# A tibble: 20 x 4
# Groups:   ID [2]
      ID episode score new_score
   <dbl>   <dbl> <dbl>     <dbl>
 1     1       1     1         1
 2     1       2     1         1
 3     1       3     1         1
 4     1       4    NA         1
 5     1       5    NA        NA
 6     1       6    NA        NA
 7     1       7     2         2
 8     1       8    NA         3
 9     1       9     4         4
10     1      10    NA         4
11     2       1    NA        NA
12     2       2     2         2
13     2       3     3         3
14     2       4     4         4
15     2       5    NA         4
16     2       6    NA        NA
17     2       7     3         3
18     2       8    NA         3
19     2       9    NA        NA
20     2      10    NA        NA

请注意,这里会创建一个新列new_score以便进行比较。

要替换score,请使用

data %>% 
  group_by(ID) %>% 
  mutate(score = zoo::na.approx(score, x = row_number(), maxgap = 1, na.rm = FALSE)) %>% 
  mutate(score = if_else(is.na(score), lag(score), score))

数据

data <- readr::read_csv("ID,episode,score
1,1,1
1,2,1
1,3,1
1,4,NA
1,5,NA
1,6,NA
1,7,2
1,8,NA
1,9,4
1,10,NA
2,1,NA
2,2,2
2,3,3
2,4,4
2,5,NA
2,6,NA
2,7,3
2,8,NA
2,9,NA
2,10,NA")

答案 1 :(得分:2)

从计算上讲,您可以将三个规则简化为一个复合条件:

  

如果NA,则将每个is.na(score[i]) && !is.na(score[i - 1])替换为其邻居的平均值,即元素为NA而先前的元素不是NA

要实现此目的,您只需要将na.rm = T传递到mean(),即mean(x[(i-1):(i+1)], na.rm = T)中,即可在*apply函数或{{1}中使用},如下所述。请注意,我还选择了按索引位置引用和分配值,而不是使用maplead来生成额外的向量。可能不那么令人兴奋,但它也更有效:

lag

答案 2 :(得分:0)

一个选项是

library(dplyr)
data %>%
   group_by(ID) %>% 
  group_by(grp = cumsum(lead(is.na(score) & !is.na(lead(score) & 
      !is.na(lag(score)) ))), add = TRUE) %>% 
  mutate(score1 = if(n() == 3 & is.na(score[2]) & sum(is.na(score))== 1) 
    replace(score, is.na(score), mean(score, na.rm = TRUE)) else score) %>% 
  ungroup %>% 
  select(-grp) %>%
  mutate(score1 = coalesce(score1, lag(score1)))
# A tibble: 20 x 4
#      ID episode score score1
#   <int>   <int> <int>  <dbl>
# 1     1       1     1      1
# 2     1       2     1      1
# 3     1       3     1      1
# 4     1       4    NA      1
# 5     1       5    NA     NA
# 6     1       6    NA     NA
# 7     1       7     2      2
# 8     1       8    NA      3
# 9     1       9     4      4
#10     1      10    NA      4
#11     2       1    NA     NA
#12     2       2     2      2
#13     2       3     3      3
#14     2       4     4      4
#15     2       5    NA      4
#16     2       6    NA     NA
#17     2       7     3      3
#18     2       8    NA      3
#19     2       9    NA     NA
#20     2      10    NA     NA