Как заполнить НС 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))