wake-up-neo.net

Ersetzen Sie fehlende Werte (NA) durch die letzte Nicht-NA durch eine Gruppe

Ich möchte das folgende Problem mit Dplyr lösen. Am liebsten mit einer der Fensterfunktionen . Ich habe einen Datenrahmen mit Häusern und Kaufpreisen. Das folgende ist ein Beispiel: 

houseID      year    price 
1            1995    NA
1            1996    100
1            1997    NA
1            1998    120
1            1999    NA
2            1995    NA
2            1996    NA
2            1997    NA
2            1998    30
2            1999    NA
3            1995    NA
3            1996    44
3            1997    NA
3            1998    NA
3            1999    NA

Ich möchte einen Datenrahmen wie folgt erstellen:

houseID      year    price 
1            1995    NA
1            1996    100
1            1997    100
1            1998    120
1            1999    120
2            1995    NA
2            1996    NA
2            1997    NA
2            1998    30
2            1999    30
3            1995    NA
3            1996    44
3            1997    44
3            1998    44
3            1999    44

Hier sind einige Daten im richtigen Format:

# Number of houses
N = 15

# Data frame
df = data.frame(houseID = rep(1:N,each=10), year=1995:2004, price =ifelse(runif(10*N)>0.15, NA,exp(rnorm(10*N))))

Gibt es eine Möglichkeit, dies zu tun?

36
Peter S

Diese verwenden alle na.locf aus dem Zoo-Paket:

dplyr

library(dplyr)
library(Zoo)

df %>% group_by(houseID) %>% na.locf %>% ungroup

geben:

Source: local data frame [15 x 3]
Groups: houseID

   houseID year price
1        1 1995    NA
2        1 1996   100
3        1 1997   100
4        1 1998   120
5        1 1999   120
6        2 1995    NA
7        2 1996    NA
8        2 1997    NA
9        2 1998    30
10       2 1999    30
11       3 1995    NA
12       3 1996    44
13       3 1997    44
14       3 1998    44
15       3 1999    44

Andere Lösungen bieten eine ähnliche Ausgabe, so dass wir sie nicht wiederholen, es sei denn, das Format unterscheidet sich erheblich.

Eine andere Möglichkeit ist die Kombination der by-Lösung (weiter unten gezeigt) mit dplyr:

df %>% by(df$houseID, na.locf) %>% rbind_all

durch

library(Zoo)

do.call(rbind, by(df, df$houseID, na.locf))

ave

library(Zoo)

na.locf2 <- function(x) na.locf(x, na.rm = FALSE)
transform(df, price = ave(price, houseID, FUN = na.locf2))

Datentabelle

library(data.table)
library(Zoo)

data.table(df)[, na.locf(.SD), by = houseID]

Zoo Diese Lösung verwendet nur Zoo. Es gibt ein breites und kein langes Ergebnis zurück:

library(Zoo)

z <- read.Zoo(df, index = 2, split = 1, FUN = identity)
na.locf(z, na.rm = FALSE)

geben:

       1  2  3
1995  NA NA NA
1996 100 NA 44
1997 100 NA 44
1998 120 30 44
1999 120 30 44

Diese Lösung könnte mit dplyr folgendermaßen kombiniert werden:

library(dplyr)
library(Zoo)

df %>% read.Zoo(index = 2, split = 1, FUN = identity) %>% na.locf(na.rm = FALSE)

Eingabe

Hier ist die Eingabe für die obigen Beispiele:

df <- structure(list(houseID = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
  2L, 3L, 3L, 3L, 3L, 3L), year = c(1995L, 1996L, 1997L, 1998L, 
  1999L, 1995L, 1996L, 1997L, 1998L, 1999L, 1995L, 1996L, 1997L, 
  1998L, 1999L), price = c(NA, 100L, NA, 120L, NA, NA, NA, NA, 
  30L, NA, NA, 44L, NA, NA, NA)), .Names = c("houseID", "year", 
  "price"), class = "data.frame", row.names = c(NA, -15L))

&UUML;BERARBEITETNeu angeordnet und weitere Lösungen hinzugefügt. Dplyr/Zoo-Lösung überarbeitet, um den neuesten Änderungen von dplyr zu entsprechen.

40
G. Grothendieck

tidyr::fill macht dies nun dumm einfach:

library(dplyr)
library(tidyr)
# or library(tidyverse)

df %>% group_by(houseID) %>% fill(price)
# Source: local data frame [15 x 3]
# Groups: houseID [3]
# 
#    houseID  year price
#      (int) (int) (int)
# 1        1  1995    NA
# 2        1  1996   100
# 3        1  1997   100
# 4        1  1998   120
# 5        1  1999   120
# 6        2  1995    NA
# 7        2  1996    NA
# 8        2  1997    NA
# 9        2  1998    30
# 10       2  1999    30
# 11       3  1995    NA
# 12       3  1996    44
# 13       3  1997    44
# 14       3  1998    44
# 15       3  1999    44
54
alistaire

Sie können einen rollenden Self-Join erstellen, der von data.table unterstützt wird:

require(data.table)
setDT(df)   ## change it to data.table in place
setkey(df, houseID, year)     ## needed for fast join
df.woNA <- df[!is.na(price)]  ## version without the NA rows

# rolling self-join will return what you want
df.woNA[df, roll=TRUE]  ## will match previous year if year not found
13
ilir

Reine Dplyr-Lösung (kein Zoo).

df %>% 
 group_by(houseID) %>%
 mutate(price_change = cumsum(0 + !is.na(price))) %>%
 group_by(price_change, add = TRUE) %>%
 mutate(price_filled = nth(price, 1)) %>%
 ungroup() %>%
 select(-price_change) -> df2

Interessanter Teil der Beispiellösung ist am Ende von df2.

> tail(df2, 20)
Source: local data frame [20 x 4]

    houseID year     price price_filled
 1       14 1995        NA           NA
 2       14 1996        NA           NA
 3       14 1997        NA           NA
 4       14 1998        NA           NA
 5       14 1999 0.8374778    0.8374778
 6       14 2000        NA    0.8374778
 7       14 2001        NA    0.8374778
 8       14 2002        NA    0.8374778
 9       14 2003 2.1918880    2.1918880
10       14 2004        NA    2.1918880
11       15 1995        NA           NA
12       15 1996 0.3982450    0.3982450
13       15 1997        NA    0.3982450
14       15 1998 1.7727000    1.7727000
15       15 1999        NA    1.7727000
16       15 2000        NA    1.7727000
17       15 2001        NA    1.7727000
18       15 2002 7.8636329    7.8636329
19       15 2003        NA    7.8636329
20       15 2004        NA    7.8636329
9
Wojciech Sobala

ohne dplyr:

prices$price <-unlist(lapply(split(prices$price,prices$houseID),function(x) na.locf(x,na.rm=FALSE)))

prices
   houseID year price
1        1 1995    NA
2        1 1996   100
3        1 1997   100
4        1 1998   120
5        1 1999   120
6        2 1995    NA
7        2 1996    NA
8        2 1997    NA
9        2 1998    30
10       2 1999    30
11       3 1995    NA
12       3 1996    44
13       3 1997    44
14       3 1998    44
15       3 1999    44
1
hvollmeier

Hier eine dplyr und imputeTS Kombination.

library(dplyr)
library(imputeTS)
df %>% group_by(houseID) %>% 
mutate(price = na.locf(price, na.remaining="keep"))  

Sie könnten auch na.locf mit fortgeschritteneren fehlenden Datenersetzungsfunktionen (Imputation) aus imputeTS ersetzen. Zum Beispiel na.interpolation oder na.kalman . Ersetzen Sie dazu einfach na.locf durch den Namen der gewünschten Funktion.

0
stats0007