R 按组将缺失值(NA)替换为最新的非NA

R 按组将缺失值(NA)替换为最新的非NA,r,dplyr,R,Dplyr,我想用dplyr解决以下问题。最好使用其中一个窗口函数。 我有一个关于房屋和购买价格的数据框架。以下是一个例子: houseID year price 1 1995 NA 1 1996 100 1 1997 NA 1 1998 120 1 1999 NA 2 1995 NA 2 1996

我想用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
library(dplyr)
library(zoo)

df %>% read.zoo(index = 2, split = 1, FUN = identity) %>% na.locf2
以下是一些格式正确的数据:

# 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方法可以做到这一点?

您可以通过
数据进行滚动自联接。表

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
require(data.table)
setDT(df)##将其更改为data.table in place
快速加入需要设置键(df、houseID、year)

df.woNA这些都使用动物园包装中的
na.locf
。还要注意的是,
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
这方面的一个变化是:

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

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)

transform(df, price = ave(price, houseID, FUN = na.locf0))
library(data.table)
library(zoo)

data.table(df)[, na.locf2(.SD), by = houseID]
zoo此解决方案仅使用zoo。它返回一个宽而不是长的结果:

library(zoo)

z <- read.zoo(df, index = 2, split = 1, FUN = identity)
na.locf2(z)
此解决方案可与dplyr组合,如下所示:

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

df不带
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
prices$price纯dplyr解决方案(无动物园)

示例解决方案的有趣部分位于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

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

A
dplyr
imputeTS
组合

library(dplyr)
library(imputeTS)
df %>% group_by(houseID) %>% 
mutate(price = na.locf(price, na.remaining="keep"))  
您还可以使用
imputeTS
中更高级的缺失数据替换(插补)函数替换
na.locf
。例如
na.interpolation
na.kalman
。为此,只需将
na.locf
替换为您喜欢的函数名。

因为,包有一个
nafill()
函数,类似于
tidyr::fill()
zoo::na.locf()
,您可以执行以下操作:

require(data.table)
setDT(df)
df[,价格:=nafill(价格,类型='locf'),房屋ID]
还有
setnafill()
,虽然不允许group by,但允许多文件列

setnafill(df,type='locf',cols='price')

数据取自@G.Grothendieck的回答:

df=data.frame(房屋ID=c(1L、1L、1L、1L、2L、2L、2L、,
2L、3L、3L、3L、3L、3L、,
年份=c(1995L、1996L、1997L、1998L、1999L、1995L、1996L、,
1997L、1998L、1999L、1995L、1996L、1997L、1998L、1999L),
价格=c(不适用,100升,不适用,120升,不适用,不适用,不适用,30升,不适用,不适用,44升,
不,不,不)

我已经显示了我在底部附近得到的输出。尝试获取最新版本的dplyr,以防是您的dplyr版本导致问题:
devtools::install\u github(“haldey/dplyr”)
我尝试了您的代码,但得到了错误:na.locf(.):object.中的错误。未找到“”。我正在服务器上工作,无法动态更改dplyr版本(已经2个月了)。以下是重新修改为使用dplyr的
by
解决方案:
df%.%by(df$houseID,na.locf)%.%do.call(what=rbind)
。如果这些dplyr解决方案都不适用于您,可能是因为您的旧版本,那么请使用我提供的其他解决方案之一。此解决方案有效:df%。%groupby(houseID)%。%mutate(p2=na.locf(price,na.rm=F))其他方法是可读性、简洁性、简单性和缺乏依赖性。您应该在
库(zoo)上添加广告
在您的代码中,因为您正在使用该库。