按组有条件地替换前导和滞后的NA

时间:2019-12-16 12:20:57

标签: r

根据第一个和最后一个非缺失值的不同条件,我很难以不同的方式填写NA。
背景:人们接受白内障手术并将晶状体状态从phak更改为pseudo。如果第一个非缺失值是“ phak”,则该值必须存在于以前未记录的观测值中。如果最后一个非缺失值是'pseudo',则可以继续执行此操作,因为状态不能更改回'phak'。
一个人不能简单地使用zoo::na.locf之类的函数,因为如果开关之间不存在NA,我们将无法分辨确切的时间是什么,这些值应该保持NA。 Thus, it is not a "sandwich" problem

library(tidyverse)

a <- c(NA, 'phak', NA, 'pseudo', NA)
b <- c(NA, 'pseudo', NA, NA, NA)
c <- c('phak', 'phak', NA, NA, NA)

dfoo <- data.frame(a,b,c, stringsAsFactors = FALSE) %>% gather(eye, status)
dfoo
#>      eye status
#> 1      a   <NA>
#> 2      a   phak
#> 3      a   <NA>
#> 4      a pseudo
#> 5      a   <NA>
#> 6      b   <NA>
#> 7      b pseudo
#> 8      b   <NA>
#> 9      b   <NA>
#> 10     b   <NA>
#> 11     c   phak
#> 12     c   phak
#> 13     c   <NA>
#> 14     c   <NA>
#> 15     c   <NA>

所需数据帧:

#>      eye  status
#> 1      a   phak
#> 2      a   phak
#> 3      a   <NA>
#> 4      a pseudo
#> 5      a pseudo
#> 6      b   <NA>
#> 7      b pseudo
#> 8      b pseudo
#> 9      b pseudo
#> 10     b pseudo
#> 11     c   phak
#> 12     c   phak
#> 13     c   <NA>
#> 14     c   <NA>
#> 15     c   <NA>

以下内容适用于简单的向量,但我很难在一个数据帧中按组使用此解决方案。

# by vector:
# first conditionally replace leading NAs
if(a[min(which(!is.na(a)))] == 'phak') {a[1 : min(which(!is.na(a)))] <- 'phak'}
# next conditionally replace last NAs
if(a[max(which(!is.na(a)))] == 'pseudo') {a[max(which(!is.na(a))): length(a)] <- 'pseudo'}

a
#> [1] "phak"   "phak"   NA       "pseudo" "pseudo"

reprex package(v0.3.0)于2019-12-16创建

4 个答案:

答案 0 :(得分:3)

这是一种可能的整理方法:

  1. group_by视线水平
  2. 创建两个临时列phakpseudo,分别将所有"pseudo"值和"phak"值替换为NA
  3. fill使用phak的{​​{1}}列
  4. .direction = "up"使用fill的{​​{1}}列
  5. pseudo .direction = "down"coalesce列一起回到列phak
pseudo

数据

status

答案 1 :(得分:2)

这是使用自定义函数的另一种方法

apply_fun <- function(x) {
   inds1 <- which(x == 'phak')
   if(length(inds1) > 0) x[1:min(inds1)] <- 'phak'

   inds2 <- which(x == 'pseudo')
   if(length(inds2) > 0) x[max(inds2):length(x)] <- 'pseudo'
   return(x)
}

library(dplyr)
dfoo %>%  group_by(eye) %>% mutate(status = apply_fun(status))

#  eye   status
#  <chr> <chr> 
# 1 a     phak  
# 2 a     phak  
# 3 a     NA    
# 4 a     pseudo
# 5 a     pseudo
# 6 b     NA    
# 7 b     pseudo
# 8 b     pseudo
# 9 b     pseudo
#10 b     pseudo
#11 c     phak  
#12 c     phak  
#13 c     NA    
#14 c     NA    
#15 c     NA    

答案 2 :(得分:2)

我找到了在group_by %>% mutate中使用if else构造的解决方案。此后可以删除中间变量,或者重写case_when以包括它们。


dfoo %>% 
  group_by(eye) %>% 
  mutate(
    changePhak = if(any(status == "phak", na.rm = TRUE))  row_number() < which(status == "phak")   else FALSE,
    changePseudo = if(any(status == "pseudo", na.rm = TRUE)) row_number() > which(status == "pseudo") else FALSE,
    status = case_when(
      changePhak ~ "phak",
      changePseudo ~ "pseudo",
      TRUE ~ status
    )
  )

答案 3 :(得分:0)

我想出了一种方法,但这似乎不是最好的解决方案,尤其是当我有成千上万的群组时,这种解决方案会杀死我的计算机:

1)首先按组拆分数据帧
2)按列使用lapply:

@model VidlyApp.Models.Movie

@{
 ViewBag.Title = "Details";
  Layout = "~/Views/Shared/_Layout.cshtml";
 }

 <div class="col-lg-3">
      <h4><strong>Released Date: </strong> @Model.ReleasedDate</h4>
 </div>
 <div class="col-lg-3">
 <h4><strong>Added Date: </strong>@Model.DateAdded</h4>
 </div>

reprex package(v0.3.0)于2019-12-16创建