Подтвердить что ты не робот

Замените отсутствующие значения (NA) на самые последние не-NA по группам

Я хотел бы решить следующую проблему с dplyr. Предпочтительно с одной из оконных функций. У меня есть кадр данных с домами и ценами на покупку. Ниже приведен пример:

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

Я хотел бы создать такой кадр данных:

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

Вот некоторые данные в правильном формате:

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

Есть ли dplyr-способ сделать это?

4b9b3361

Ответ 1

Все они используют na.locf из пакета zoo. Также обратите внимание, что na.locf0 (также определенный в zoo) похож на na.locf за исключением того, что по умолчанию он na.rm = FALSE и требует одного векторного аргумента. na.locf2 определенный в первом решении, также используется в некоторых других.

dplyr

library(dplyr)
library(zoo)

na.locf2 <- function(x) na.locf(x, na.rm = FALSE)
df %>% group_by(houseID) %>% do(na.locf2(.)) %>% ungroup

давая:

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

Вариант этого:

df %>% group_by(houseID) %>% mutate(price = na.locf0(price)) %>% ungroup

Другие решения ниже дают вывод, который очень похож, поэтому мы не будем повторять его, за исключением случаев, когда формат существенно отличается.

Еще одна возможность заключается в том, чтобы объединить с by раствора ( как показано ниже) с dplyr:

df %>% by(df$houseID, na.locf2) %>% bind_rows

от

library(zoo)

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

пр

library(zoo)

transform(df, price = ave(price, houseID, FUN = na.locf0))

Таблица данных

library(data.table)
library(zoo)

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

зоопарк Это решение использует только зоопарк. Возвращает широкий, а не длинный результат:

library(zoo)

z <- read.zoo(df, index = 2, split = 1, FUN = identity)
na.locf2(z)

давая:

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

Это решение может быть объединено с dplyr следующим образом:

library(dplyr)
library(zoo)

df %>% read.zoo(index = 2, split = 1, FUN = identity) %>% na.locf2

вход

Вот входные данные, используемые для примеров выше:

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

ПЕРЕСМОТРЕНО Перестроено и добавлено больше решений. Пересмотренное решение dplyr/zoo для соответствия последним изменениям dplyr. Применяется фиксированный и na.locf2 из всех решений na.locf2.

Ответ 2

tidyr::fill теперь делает это глупо:

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

Ответ 3

Вы можете выполнить автоматическое объединение, поддерживаемое data.table:

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

Ответ 4

Чистое решение dplyr (без зоопарка).

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

Входящая часть примерного решения находится в конце 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

Ответ 5

Без dplyr:

  prices$price <-unlist(lapply(split(prices$price,prices$houseID),
function(x) zoo::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

Ответ 6

dplyr и imputeTS.

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

Вы также можете заменить na.locf более продвинутыми функциями замены (вменения) отсутствующих данных из imputeTS. Например na.interpolation или na.kalman. Для этого просто замените na.locf на имя функции, которая вам нравится.