我有一个包含房屋和购买价格的数据框。以下是一个示例:
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方法可以做到这一点?
#1 楼
这些都使用来自zoo软件包的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
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))
ave
library(zoo)
transform(df, price = ave(price, houseID, FUN = na.locf0))
data.table
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)
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
。#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
和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
替换为您喜欢的函数的名称即可。#6 楼
没有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
评论
由于您正在使用该库,因此应在代码中添加该库。
–steveb
18年2月22日在5:38
#7 楼
从data.table v1.12.4开始,该软件包具有nafill()
功能,类似于tidyr::fill()
或zoo::na.locf()
,您可以执行以下操作: require(data.table)
setDT(df)
df[ , price := nafill(price, type = 'locf'), houseID ]
还有
setnafill()
,尽管不允许分组依据,但要多列。 setnafill(df, type = 'locf', cols = 'price')
已获取数据来自@G。格洛腾迪克的答案:
df = data.frame(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))
评论
我已经显示出输出接近底部。如果您的dplyr版本引起问题,请尝试获取最新版本的dplyr:devtools :: install_github(“ haldey / dplyr”)
– G. Grothendieck
2014-4-28 13:57
我尝试了您的代码并收到错误:na.locf(。)中的错误:对象'。'未找到。我正在服务器上工作,无法即时更改dplyr-version(已2个月大)。
– Peter Stephensen
2014年4月28日在14:19
这是使用dplyr重做的by解决方案:df%。%by(df $ houseID,na.locf)%。%do.call(what = rbind)。如果这些dplyr解决方案都不适合您(可能是由于您的旧版本),请使用我提供的其他解决方案之一。
– G. Grothendieck
14年4月28日在14:29
这可以正常工作:df%。%group_by(houseID)%。%mutate(p2 = na.locf(price,na.rm = F))
– Peter Stephensen
2014年4月28日14:50
其他方式是可读性,简洁性,简单性和缺乏依赖性。
– G. Grothendieck
2014年6月16日22:35