Как заполнить НС LOCF по факторам в кадре данных, разбитым по странам
У меня есть следующий фрейм данных (упрощенный) с переменной страны как фактор, а переменная значения имеет отсутствующие значения:
country value
AUT NA
AUT 5
AUT NA
AUT NA
GER NA
GER NA
GER 7
GER NA
GER NA
Следующее генерирует приведенный выше кадр данных:
data <- data.frame(country=c("AUT", "AUT", "AUT", "AUT", "GER", "GER", "GER", "GER", "GER"), value=c(NA, 5, NA, NA, NA, NA, 7, NA, NA))
Теперь я хотел бы заменить значения NA в каждом подмножестве страны, используя метод последнего наблюдения, перенесенного вперед (LOCF). Я знаю команду na.locf
в пакете zoo. data <- na.locf(data)
предоставит мне следующий фрейм данных:
country value
AUT NA
AUT 5
AUT 5
AUT 5
GER 5
GER 5
GER 7
GER 7
GER 7
Однако функция должна использоваться только для отдельных подмножеств, разделенных страной. Ниже представлен вывод, который мне нужен:
country value
AUT NA
AUT 5
AUT 5
AUT 5
GER NA
GER NA
GER 7
GER 7
GER 7
Я не могу придумать простой способ его реализации. Прежде чем начать с for-loops, мне было интересно, есть ли у кого-нибудь идеи, как это решить.
Большое спасибо!
Ответы
Ответ 1
Здесь a ddply
решение. Попробуйте это
library(plyr)
ddply(DF, .(country), na.locf)
country value
1 AUT <NA>
2 AUT 5
3 AUT 5
4 AUT 5
5 GER <NA>
6 GER <NA>
7 GER 7
8 GER 7
9 GER 7
Edit
Из справки ddply
вы можете найти, что
.variables: variables to split data frame by,
as quoted variables, a formula or character vector.
так что другие альтернативы, чтобы получить то, что вы хотите:
ddply(DF, "country", na.locf)
ddply(DF, ~country, na.locf)
обратите внимание, что замена .variables
на DF$variable
недопустима, поэтому при этом вы получили сообщение об ошибке.
DF
- ваш data.frame
Ответ 2
Современной версией решения ddply
является использование пакета dplyr
:
library(dplyr)
DF %>%
group_by(county) %>%
mutate(value = na.locf(value, na.rm = F))
Ответ 3
Разделите data.frame
на by
и используйте na.locf
на подмножествах:
do.call(rbind,by(data,data$country,na.locf))
Если вы хотите удалить имена строк:
do.call(rbind,unname(by(data,data$country,na.locf)))
Ответ 4
Вам просто нужно разделить по странам, затем выполните либо zoo:: na.locf(), либо na.fill, заполнив право.
Вот пример, явно показывающий трехкомпонентный синтаксис аргумента na.fill:
library(plyr)
library(zoo)
data <- data.frame(country=c("AUT", "AUT", "AUT", "AUT", "GER", "GER", "GER", "GER", "GER"), value=c(NA, 5, NA, NA, NA, NA, 7, NA, NA))
# The following is equivalent to na.locf
na.fill.right <- function(...) { na.fill(..., list(left=NA,interior=NA,right="extend")) }
ddply(data, .(country), na.fill.right)
country value
1 AUT <NA>
2 AUT 5
3 AUT 5
4 AUT 5
5 GER <NA>
6 GER <NA>
7 GER 7
8 GER 7
9 GER 7
Ответ 5
Обратный путь, хотя и не используемый locf, равен:
library(tidyverse)
data %>%
group_by(country) %>%
fill(value)
Source: local data frame [9 x 2]
Groups: country [2]
country value
(fctr) (dbl)
1 AUT NA
2 AUT 5
3 AUT 5
4 AUT 5
5 GER NA
6 GER NA
7 GER 7
8 GER 7
9 GER 7
Ответ 6
Если скорость учитывается, то это решение unstack
/stack
примерно в 4-6 раз быстрее, чем другие в моей системе, хотя это влечет за собой несколько более длинную строку кода:
stack(lapply(unstack(data, value ~ country), na.locf, na.rm = FALSE))