How to fill every 2nd row with start time and end time of non-events

Viewed 83

I have data with start times and end times of certain events (subjects gazing at an object called Center).

test <- data.frame(
  gaze_at = c("Center", "Center", "Center"),
  start = c(1.4, 2.5, 4.4),
  end = c(1.66, 2.8, 4.93)
)

I would like to 'fill up' the dataframe with the start times and end times of the participant not gazing at Center by inserting these times into every second row of the dataframe. The desired output is this:

test1 <- data.frame(
  gaze_at = c("Center", "NonCenter", "Center", "NonCenter", "Center", "NonCenter"),
  start = c(1.4, 1.66, 2.5, 2.8, 4.4, 4.93),
  end = c(1.66, 2.5, 2.8, 4.4, 4.93, NA)
)

I've tried using tidyverse::unnest but that didn't work. How can this transformation be achieved?

6 Answers

Does this work:

library(dplyr)
library(tidyr)
test %>% uncount(2) %>% mutate(gaze_at = case_when(!row_number()%%2 ~ 'NonCenter', TRUE ~ gaze_at), 
                               start = case_when(!row_number()%%2 ~ lag(end), TRUE ~ start), 
                               end = case_when(!row_number()%%2 ~ lead(start), TRUE ~ end))
    gaze_at start  end
1    Center  1.40 1.66
2 NonCenter  1.66 2.50
3    Center  2.50 2.80
4 NonCenter  2.80 4.40
5    Center  4.40 4.93
6 NonCenter  4.93   NA

Another tidyr solution

library(tidyverse)

test_1 <- test %>% mutate(id = row_number())

test_2 <- test_1 %>% mutate(gaze_at2 = "non-centre",
                start2 = end,
                end2 = lead(start)) %>%
  select(id:end2) %>% rename(gaze_at = gaze_at2,
                             start = start2,
                             end = end2)
test1 <- rbind(test_1, test_2) %>% arrange(id) %>% select(-id)

> test1
    gaze_at start  end
1    Center  1.40 1.66
2 NonCenter  1.66 2.50
3    Center  2.50 2.80
4 NonCenter  2.80 4.40
5    Center  4.40 4.93
6 NonCenter  4.93   NA

You can create the missing lines using the original table with data.frame and then rbind them and resorting the result to alternating lines using matrix.

i <- seq_len(nrow(test))
x <- data.frame("NonCenter", test[,3], test[i+1,2])
names(x) <- names(test)
rbind(test, x)[c(matrix(c(i,i+length(i)), 2, byrow = TRUE)),]
#rbind(test, x)[c(matrix(seq_len(length(i)*2), 2, byrow = TRUE)),] #Alternative
#    gaze_at start  end
#1    Center  1.40 1.66
#4 NonCenter  1.66 2.50
#2    Center  2.50 2.80
#5 NonCenter  2.80 4.40
#3    Center  4.40 4.93
#6 NonCenter  4.93   NA

or first repeat each row and update the content afterwards.

i <- seq_len(nrow(test))
x <- test[rep(i, each=2),]
j <- seq(2, nrow(x), 2)
#j <- c(FALSE, TRUE) #Alternative
x[j,1] <- "NonCenter"
x[j,2] <- test[,3]
x[j,3] <- test[i+1,2]
x
#      gaze_at start  end
#1      Center  1.40 1.66
#1.1 NonCenter  1.66 2.50
#2      Center  2.50 2.80
#2.1 NonCenter  2.80 4.40
#3      Center  4.40 4.93
#3.1 NonCenter  4.93   NA

I have only used for loop. Does that help?

New_gaze <- vector()
New_start <- vector()
New_end <- vector()
for (l in 1:nrow(test)) {
  k = l
  New_gaze <- append(New_gaze,"center")
  New_start <- append(New_start,test$start[k])
  New_end <- append(New_end,test$end[k])
  k = l+1
  New_gaze <- append(New_gaze,"Non center")
  New_start <- append(New_start,test$end[k-1])
  New_end <- append(New_end,test$start[k])

}
df <- data.frame(New_gaze,New_start,New_end)

> df
    New_gaze New_start New_end
1     center      1.40    1.66
2 Non center      1.66    2.50
3     center      2.50    2.80
4 Non center      2.80    4.40
5     center      4.40    4.93
6 Non center      4.93      NA

I would also have a two step solution: Create a new df with the missing "Non-Center"-times using lead.

# create second df
df_helper <- data.frame(
  gaze_at = rep("Non-Center", nrow(test)),
  start = test$end,
  end = lead(test$start)
)

# combine & order
df <- rbind(test, df_helper)
df[order(df$start), ]```

Alternatively could use lead to select the start from next row, add_row to add the result of mutate as a new row, select to select the columns, and then arrange to sort by start and end columns.

test %>% 
mutate(start_n = end, end_n = lead(start), gaze_at_n = "NonCenter") %>% 
add_row(start = .$start_n, end= .$end_n, gaze_at = .$gaze_at_n) %>% 
select(gaze_at, start,end) %>% 
arrange(start,end)

Should give:

  gaze_at start  end
1    Center  1.40 1.66
2 NonCenter  1.66 2.50
3    Center  2.50 2.80
4 NonCenter  2.80 4.40
5    Center  4.40 4.93
6 NonCenter  4.93   NA
Related