Conditionally add row with variable in same column of other variable

Viewed 48

I have something like df1 (time in ms), but then with thousands of rows:

df1 <- data.frame(time=c(105, 202, 305, 408, 505, 608), event=c("", "", "", "onset", "", ""))

What I want to do is, for every instance of "onset", add the variable "start" in the same column and 200ms before that instance of onset. So, it should end up like this in this simplified case:

df2 <- data.frame(time=c(105, 202, 208, 305, 408, 505, 608), event=c("", "", "start", "", "onset", "", ""))

I haven't been able to figure out how to do this, help would be greatly appreciated!

4 Answers

We can use data.table methods

library(data.table)
rbind(setDT(df1),
        df1[event == 'onset'][, c('time', 'event')
        := .(time - 200, 'start')])[order(time)]

-output

 time event
1:  105      
2:  202      
3:  208 start
4:  305      
5:  408 onset
6:  505      
7:  608      

I am not sure what kind of solution you are looking for so here is R-base code:

df3 <- df1[ df1$event == "onset", ]
df3$time <- df3$time - 200
df3$event <- "start"
df4 <- rbind( df1, df3 )
df4 <- df4[ order(df4$time), ]
df4
#   time event
#1   105      
#2   202      
#41  208 start
#3   305      
#4   408 onset
#5   505      
#6   608 

You can also use the following solution for any number of onset value in your data:

library(dplyr)
library(purrr)

df %>%
  mutate(is_onset = ifelse(event == "onset", 1, 0),
         is_onset = ifelse(is_onset == 1, cumsum(is_onset), 0)) %>%
  group_split(is_onset) %>%
  map_dfr(~ if(.x$is_onset[1]) {
    .x %>% 
      add_row(event = "start", time = .x$time - 200, .before = 1)
  } else {
    .x
  }) %>%
  select(-is_onset) %>%
  arrange(time)


# A tibble: 7 x 2
   time event  
  <dbl> <chr>  
1   105 ""     
2   202 ""     
3   208 "start"
4   305 ""     
5   408 "onset"
6   505 ""     
7   608 "" 

filter for 'onset' event, change the time and event value and bind the data to original dataframe.

library(dplyr)

df1 %>%
  filter(event == 'onset') %>%
  mutate(time = time - 200, 
         event = 'start') %>%
  bind_rows(df1) %>%
  arrange(time)

#  time event
#1  105      
#2  202      
#3  208 start
#4  305      
#5  408 onset
#6  505      
#7  608      

and in base R -

df2 <- rbind(df1, transform(subset(df1, event == 'onset'),
                  time = time - 200,  event = 'start'))

df2[order(df2$time), ]
Related