R Создать новую переменную / столбец на основе последующих наблюдений

#r #vector #dplyr

#r #вектор #dplyr

Вопрос:

Учитывая фрейм данных:

 df <- structure(
  list(
    record_id = c(1,1,1,1,1,1,1,1,1,1), 
    day_count = c(1,2,3,4,5,6,7,8,9,10), 
    change = c(0,2,0,1,0,2,0,1,2,0)), 
  row.names = c(NA, -10L),
  class = c("tbl_df", "tbl", "data.frame"))
 

с:
change (0) = без изменений, change (1) = начать / возобновить и change(2) = остановить.

Я хочу создать новый столбец, который оценивает, была ли остановка последней остановкой (т. Е. Происходит ли изменение (1) в последовательности, следующей за остановкой)

Ожидаемый результат

 df_output <- structure(
  list(
    record_id = c(1,1,1,1,1,1,1,1,1,1), 
    day_count = c(1,2,3,4,5,6,7,8,9,10), 
    change = c(0,2,0,1,0,2,0,1,2,0),
    last_stop = c(0,0,0,0,0,0,0,0,1,0)), 
  row.names = c(NA, -10L),
  class = c("tbl_df", "tbl", "data.frame"))
 

Я считаю, что мне нужно вырезать последующие наблюдения после остановки и создать из них вектор. Затем оцените, произошло ли (1) в векторе. Если да, то это была не последняя остановка, если нет, то это была последняя остановка.

Проблема в том, что я не знаю, как делать это повторно для каждых 2, которые происходят….

Надеюсь, вы сможете помочь

BW

Комментарии:

1. В вашем ожидаемом выводе отсутствует ваша выходная переменная.

2. спасибо, исправлено

Ответ №1:

Решение с базой R. Это работает для каждого record_id , поскольку я полагаю, что это ваша цель.

Я расширил ваши данные, чтобы иметь:

  • record_id == 1 это заканчивается 1 ,
  • record_id == 2 это заканчивается 2 ,
  • record_id == 3 который состоит только из нулей.
 df <- structure(
 list(
  record_id = c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3), 
  day_count = c(1,2,3,4,5,1,2,3,4,5,1,2,3,4,5), 
  change = c(0,2,0,1,0,2,0,1,2,0,0,0,0,0,0)), 
 row.names = c(NA, -15L),
 class = c("tbl_df", "tbl", "data.frame"))
 

Вот решение:

 df$last_stop <- ave(df$change, 
                    df$record_id, 
                    FUN = function(x){
                     
                     l <- numeric(length(x))
                     i <- Position(function(x) x!=0, x, right = TRUE)
                     if(identical(x[i], 2)) l[i] <- 1
                     l
                     
                     })
df

#>    record_id day_count change last_stop
#> 1          1         1      0         0
#> 2          1         2      2         0
#> 3          1         3      0         0
#> 4          1         4      1         0
#> 5          1         5      0         0
#> 6          2         1      2         0
#> 7          2         2      0         0
#> 8          2         3      1         0
#> 9          2         4      2         1
#> 10         2         5      0         0
#> 11         3         1      0         0
#> 12         3         2      0         0
#> 13         3         3      0         0
#> 14         3         4      0         0
#> 15         3         5      0         0
 

Обратите внимание, что у вас есть 1 last_stop только для record_id == 2 строки 9.

ave это интересная функция, которая разбивается df$change на df$record_id и применяет функцию к каждому компоненту.

Функция:

  • создает вектор нулей (который будет вашим df$last_stop )
  • ищет Position первое ненулевое значение справа (или снизу)
  • если это значение равно 2, то оно добавляет 1 в выходной вектор, в противном случае возвращается вектор нулей.

Комментарии:

1. Дорогой Эдо. Спасибо за ваше предложение. Действительно, я хочу, чтобы это работало для нескольких идентификаторов record_id. Однако, как предположил Оньямбу ниже, это не сработает, если 1 произошло после 2, без последующего 2. Есть какие-нибудь предложения? Заранее спасибо

Ответ №2:

Я считаю, что следующий код должен работать.

 df$last_stop = sapply(
   1:length(df$change),
   function(i){
     ifelse(df$change[i] != 2, 0,
            as.numeric(!any(df$change[i:length(df$change)] == 1)))
   })    
 

Функция sapply вызывает у вас вектор изменения df $, выводит 0, если изменение отличается от 2, в противном случае проверяет, есть ли какое-либо значение 1 в оставшихся элементах, если его нет, условие возвращает TRUE, которое затем преобразуется в числовое значение, поскольку as.numeric(TRUE) == 1.

Комментарии:

1. Спасибо, Wawv. Это действительно хорошо работает в примере, который я предоставил. Как предложил edo ниже, я хочу, чтобы этот код работал для каждого record_id . Изображение record_id номер 2, с точными теми же номерами, что и номер 1. Я попробовал следующее в dataframe с record_id 1 и 2, что не работает: df2 <- df2 %>% group_by(record_id) %>% mutate(last_stop = sapply( 1:length(df2$change), function(i){ ifelse(df2$change[i] != 2, 0, as.numeric(!any(df2$change[i:length(df2$change)] == 1))) } )) есть предложения?

2. Это должно сработать, если вы удалите «df2 $» из функции, чтобы она ссылалась только на изменение переменной внутри группы (а не внутри data.frame).

Ответ №3:

Большинство предоставленных кодов не учитывают, что у вас может быть 1 после 2, не имея 2 впоследствии. Чтобы принять это во внимание, мы могли бы сделать:

 a <- max(which(df$change==2))
b <- max(which(df$change==1))
df$change <- numeric(nrow(df))
df$change [if(a>b) a else 0] <- 1
df
 

Комментарии:

1. Привет, Оньямбу, спасибо за вашу помощь. Это действительно хорошо работает для примера, который я предоставил. Как я забыл упомянуть, у меня есть несколько record_id. Каким будет ваше предложение? BW и заранее спасибо