过滤相对于事件的第一次和最后一次出现的数据

时间:2018-05-21 09:53:07

标签: r dplyr tidyverse data-munging

我有一个实验的数据框,其中向参与者显示刺激,并且持续测量时间。

# reprex
df <- 
    tibble(stim = c(NA, NA, NA, NA, "a", "b", NA, "c", NA, "d", NA, NA, NA),
           time = 0:12)
# A tibble: 13 x 2
   stim   time
   <chr> <int>
 1 NA        0
 2 NA        1
 3 NA        2
 4 NA        3
 5 a         4
 6 b         5
 7 NA        6
 8 c         7
 9 NA        8
10 d         9
11 NA       10
12 NA       11
13 NA       12

我想创建一个通用解决方案,使用tidyverse函数分别在第一个和最后一个标记之前1秒和之后2秒丢弃数据。使用tidyverse,我认为这会起作用,但它会引发一个无法解释的错误。

df %>% 
# store times for first and last stim
    mutate(first_stim = drop_na(stim) %>% pull(time) %>% first(),
           last_stim =  drop_na(stim) %>% pull(time) %>% last()) %>% 
# filter df based on new variables
    filter(time >= first(first_stim) - 1 &
           time <= first(last_stim) + 2)
Error in mutate_impl(.data, dots) : bad value

所以我做了一个非常难看的基础代码来通过改变mutate克服这个问题:

df2 <- df %>% 
    mutate(first_stim = .[!is.na(.$stim), "time"][1,1],
           last_stim = .[!is.na(.$stim), "time"][nrow(.[!is.na(.$stim), "time"]), 1])
    # A tibble: 13 x 4
       stim   time first_stim last_stim
       <chr> <int> <tibble>   <tibble> 
     1 NA        0 4          9        
     2 NA        1 4          9        
     3 NA        2 4          9        
     4 NA        3 4          9        
     5 a         4 4          9        
     6 b         5 4          9        
     7 NA        6 4          9        
     8 c         7 4          9        
     9 NA        8 4          9        
    10 d         9 4          9        
    11 NA       10 4          9        
    12 NA       11 4          9        
    13 NA       12 4          9   

现在我只需要根据新变量first_stim - 1last_stim + 2进行过滤。但过滤器也失败了:

df2 %>% 
    filter(time >= first(first_stim) - 1 &
           time <= first(last_stim) + 2)
Error in filter_impl(.data, quo) : 
  Not compatible with STRSXP: [type=NULL].

我能够在基地R中做到这一点,但它真的很难看:

df2[(df2$time >= (df2[[1, "first_stim"]] - 1)) & 
    (df2$time <= (df2[[1, "last_stim"]] + 2))    
    ,]

所需的输出应如下所示:

# A tibble: 13 x 2
   stim   time
   <chr> <int>
 4 NA        3
 5 a         4
 6 b         5
 7 NA        6
 8 c         7
 9 NA        8
10 d         9
11 NA       10
12 NA       11

我认为这些错误与dplyr::nth()及相关功能有关。我发现了一些与此行为相关的旧问题,但不应再存在https://github.com/tidyverse/dplyr/issues/1980 如果有人能突出问题是什么,以及如何以一种整洁的方式做到这一点,我将非常感激。

2 个答案:

答案 0 :(得分:2)

您可以使用is.nawhich ...

的组合
library(dplyr)

df <- 
  tibble(stim = c(NA, NA, NA, NA, "a", "b", NA, "c", NA, "d", NA, NA, NA),
         time = 0:12)

df %>% 
  filter(row_number() >= first(which(!is.na(stim))) - 1 & 
         row_number() <= last(which(!is.na(stim))) + 2)

# # A tibble: 9 x 2
#   stim   time
#   <chr> <int>
# 1 NA        3
# 2 a         4
# 3 b         5
# 4 NA        6
# 5 c         7
# 6 NA        8
# 7 d         9
# 8 NA       10
# 9 NA       11

你也可以通过一点修改让你的第一次尝试工作......

df %>% 
  mutate(first_stim = first(drop_na(., stim) %>% pull(time)),
         last_stim =  last(drop_na(., stim) %>% pull(time))) %>% 
  filter(time >= first(first_stim) - 1 &
           time <= first(last_stim) + 2)

答案 1 :(得分:0)

我们可以创建非NA值的累积和,然后找到我们遇到第一个非NA值和最后一个值的行索引。然后,我们根据需求选择行。 (从开始的-1和结束的+2)。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="false"
    android:interpolator="@android:anim/bounce_interpolator">

    <scale
        android:duration="500"
        android:fromXScale="1.0"
        android:fromYScale="0.0"
        android:toXScale="1.0"
        android:toYScale="1.0" />

</set>

只是想知道library(tidyverse) df %>% mutate(count_cumsum = cumsum(!is.na(stim))) %>% slice((which.max(count_cumsum == 1) -1):(which.max(count_cumsum) + 2)) %>% select(-count_cumsum) # stim time # <chr> <int> #1 NA 3 #2 a 4 #3 b 5 #4 NA 6 #5 c 7 #6 NA 8 #7 d 9 #8 NA 10 #9 NA 11 看起来如何:

count_cumsum