Выберите значение из предыдущей группы на основе условия

#r #for-loop #dplyr

Вопрос:

У меня есть следующий df

 df<-data.frame(value = c(1,1,1,2,1,1,2,2,1,2),
              group = c(5,5,5,6,7,7,8,8,9,10),
             no_rows = c(3,3,3,1,2,2,2,2,1,1))
 

где одинаковые последовательные значения образуют группу, т. е. Значения в строках 1:3 попадают в группу 5. Столбец «no_rows» сообщает нам, сколько строк/записей в каждой группе, т. е. в группе 5 3 строки/записи.

Я пытаюсь заменить все значения, где no_rows Я ожидаю, что мой конечный df будет выглядеть так:

 df_end<-data.frame(value = c(1,1,1,1,1,1,2,2,2,2),
              group = c(5,5,5,6,7,7,8,8,9,10),
             no_rows = c(3,3,3,1,2,2,2,2,1,1))
 

Я придумал эту комбинацию if…else в цикле for, которая дает мне желаемый результат, однако он очень медленный, и я ищу способ его оптимизации.

   for (i in 2:length(df$group)){
    if (df$no_rows[i] < 2){
      df$value[i] <- df$value[i-1]
    } 
 }
 

Я также пробовал использовать dplyr::mutate и lag (), но это не дает мне желаемого результата (он удаляет только первое значение для каждой группы вместо того, чтобы принимать значение предыдущей группы).

   df<-df%>%
    group_by(group) %>%
    mutate(value = ifelse(no_rows < 2, lag(value), value))
 

Я искал решение уже несколько дней, но не мог найти ничего, что полностью соответствовало бы моей проблеме. Есть какие-нибудь идеи?

Ответ №1:

подход с использованием таблицы данных…

сначала получите значения групп с длиной >=2, затем заполните недостающие значения (NA) с помощью перенесенного последнего наблюдения.

 library(data.table)
# make it a data.table
setDT(df, key = "group")
# get values for groups of no_rows >= 2
df[no_rows >= 2, new_value := value][]
#    value group no_rows new_value
# 1:     1     5       3         1
# 2:     1     5       3         1
# 3:     1     5       3         1
# 4:     2     6       1        NA
# 5:     1     7       2         1
# 6:     1     7       2         1
# 7:     2     8       2         2
# 8:     2     8       2         2
# 9:     1     9       1        NA
#10:     2    10       1        NA

# fill down missing values in new_value
setnafill(df, "locf", cols = c("new_value"))
#    value group no_rows new_value
# 1:     1     5       3         1
# 2:     1     5       3         1
# 3:     1     5       3         1
# 4:     2     6       1         1
# 5:     1     7       2         1
# 6:     1     7       2         1
# 7:     2     8       2         2
# 8:     2     8       2         2
# 9:     1     9       1         2
#10:     2    10       1         2
 

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

1. Спасибо, работает отлично! Что делать, если первые несколько значений имеют длину

2. также добавьте значение для первой группы: df[no_rows >= 2|group == min(group), new_value := value][] перед строкой точить setnafill