#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