Jak wypełnić serwer NAS LOCF według czynników w ramce danych, w podziale na kraje

Mam następującą ramkę danych (uproszczoną) ze zmienną kraju jako czynnikiem i zmienna wartości ma brakujące wartości:

country value
AUT     NA
AUT     5
AUT     NA
AUT     NA
GER     NA
GER     NA
GER     7
GER     NA
GER     NA

Powyższa ramka danych generuje:

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))

Teraz chciałbym zastąpić wartości NA w każdym podzbiorze kraju metodą last observation carried forward (LOCF). Znam komendę na.locf w pakiecie zoo. data <- na.locf(data) dałoby mi następującą ramkę danych:

country value
AUT     NA
AUT     5
AUT     5
AUT     5
GER     5
GER     5
GER     7
GER     7
GER     7

Jednakże, funkcja powinien być stosowany tylko na poszczególnych podzbiorach podzielonych przez państwo. Poniżej znajduje się wyjście, którego potrzebuję:

country value
AUT     NA
AUT     5
AUT     5
AUT     5
GER     NA
GER     NA
GER     7
GER     7
GER     7
Nie mogę wymyślić łatwego sposobu na jego wdrożenie. Zanim zacząłem z for-loops, zastanawiałem się, czy ktoś ma jakiś pomysł, jak to rozwiązać.

Wielkie dzięki!!

Author: smci, 2012-11-29

7 answers

Oto ddply rozwiązanie. Try this

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 Z pomocy ddply znajdziesz to

.variables:  variables to split data frame by, 
as quoted variables, a formula or character vector.

Więc inne alternatywy, aby dostać to, co chcesz są:

ddply(DF, "country", na.locf)
ddply(DF, ~country, na.locf)

Zauważ, że zastąpienie .variables przez DF$variable jest niedozwolone, dlatego masz błąd podczas robienia tego.

DF to Twoje dane.frame

 13
Author: Jilber Urbina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-29 08:45:20

Nowoczesną wersją rozwiązania ddply jest użycie pakietu dplyr:

library(dplyr)
DF %>%
  group_by(county) %>% 
  mutate(value = na.locf(value, na.rm = F))      
 13
Author: Gregor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 23:07:42

Podziel data.frame z by i użyj na.locf na podzbiorach:

do.call(rbind,by(data,data$country,na.locf))

Jeśli chcesz usunąć nazwy wierszy:

do.call(rbind,unname(by(data,data$country,na.locf)))
 6
Author: nograpes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-29 16:50:16

Po prostu trzeba podzielić według kraju, a następnie zrobić albo A zoo:: na.locf () lub na.wypełnij, wypełnij w prawo. Oto przykład jawnie pokazujący składnię arg trójskładnikowej na.wypełnienie:

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
 4
Author: smci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-21 00:18:17

Jeśli prędkość jest rozważana, to unstack/stack rozwiązanie jest około 4 do 6 razy szybsze niż inne w moim systemie, chociaż pociąga za sobą nieco dłuższą linię kodu: {]}

stack(lapply(unstack(data, value ~ country), na.locf, na.rm = FALSE))

Inne podejście to:

transform(data, value = ave(value, country, FUN = na.locf0))
 4
Author: G. Grothendieck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 18:38:38

Sposobem tidyverse, aczkolwiek nie używającym locf, jest:

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
 3
Author: Yun Ching,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-13 02:54:53

Jestem trochę spóźniony na tę rozmowę, ale Oto data.table sposób, który będzie znacznie szybszy dla większych zbiorów danych:

library(zoo)
library(data.table)

# Convert to data table
setDT(data)

data[, value := na.locf(value, na.rm = FALSE), by = country]

data
   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

# And if you want to convert "data" back to a data frame...
setDF(data)
 2
Author: sehock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 16:21:51