在data.frame中添加缺失值的行最快的方法?

我在我的数据集中有一列,其中时间段( Time )是从ab到整数。 有时可能会有任何给定的组缺less时间段。 我想用NA填写那些行。 以下是一个(几个1000个)组中的一个的示例数据。

 structure(list(Id = c(1, 1, 1, 1), Time = c(1, 2, 4, 5), Value = c(0.568780482159894, -0.7207749516298, 1.24258192959273, 0.682123081696789)), .Names = c("Id", "Time", "Value"), row.names = c(NA, 4L), class = "data.frame") Id Time Value 1 1 1 0.5687805 2 1 2 -0.7207750 3 1 4 1.2425819 4 1 5 0.6821231 

正如你所看到的,时间3缺失。 往往有一个或多个可能会丢失。 我可以自己解决这个问题,但恐怕我不会这样做的最有效的方式。 我的方法是创build一个函数:

生成从min(Time)max(Time)的一系列时间段

然后做一个setdiff来抓取缺less的Time值。

将该vector转换为data.frame

拉取唯一标识符variables( Id和其他未列出的variables),并将其添加到此数据框中。

合并二者。

从函数返回。

那么整个过程就会被执行如下:

  # Split the data into individual data.frames by Id. temp_list <- dlply(original_data, .(Id)) # pad each data.frame tlist2 <- llply(temp_list, my_pad_function) # collapse the list back to a data.frame filled_in_data <- ldply(tlist2) 

更好的方法来实现这个?

继续与本·巴恩斯的评论,并从他的mydf3开始:

 DT = as.data.table(mydf3) setkey(DT,Id,Time) DT[CJ(unique(Id),seq(min(Time),max(Time)))] Id Time Value Id2 [1,] 1 1 -0.262482283 2 [2,] 1 2 -1.423935165 2 [3,] 1 3 0.500523295 1 [4,] 1 4 -1.912687398 1 [5,] 1 5 -1.459766444 2 [6,] 1 6 -0.691736451 1 [7,] 1 7 NA NA [8,] 1 8 0.001041489 2 [9,] 1 9 0.495820559 2 [10,] 1 10 -0.673167744 1 First 10 rows of 12800 printed. setkey(DT,Id,Id2,Time) DT[CJ(unique(Id),unique(Id2),seq(min(Time),max(Time)))] Id Id2 Time Value [1,] 1 1 1 NA [2,] 1 1 2 NA [3,] 1 1 3 0.5005233 [4,] 1 1 4 -1.9126874 [5,] 1 1 5 NA [6,] 1 1 6 -0.6917365 [7,] 1 1 7 NA [8,] 1 1 8 NA [9,] 1 1 9 NA [10,] 1 1 10 -0.6731677 First 10 rows of 25600 printed. 

CJ代表Cross交易,参见?CJ 。 发生NA的填充发生,因为默认情况下不nomatchNA 。 设置不匹配为0而不是去除不匹配。 如果不是使用NA填充主要行,只需添加roll=TRUE 。 这可以比用NA填充然后填充NA来更高效。 请参阅roll in ?data.table的描述。

 setkey(DT,Id,Time) DT[CJ(unique(Id),seq(min(Time),max(Time))),roll=TRUE] Id Time Value Id2 [1,] 1 1 -0.262482283 2 [2,] 1 2 -1.423935165 2 [3,] 1 3 0.500523295 1 [4,] 1 4 -1.912687398 1 [5,] 1 5 -1.459766444 2 [6,] 1 6 -0.691736451 1 [7,] 1 7 -0.691736451 1 [8,] 1 8 0.001041489 2 [9,] 1 9 0.495820559 2 [10,] 1 10 -0.673167744 1 First 10 rows of 12800 printed. setkey(DT,Id,Id2,Time) DT[CJ(unique(Id),unique(Id2),seq(min(Time),max(Time))),roll=TRUE] Id Id2 Time Value [1,] 1 1 1 NA [2,] 1 1 2 NA [3,] 1 1 3 0.5005233 [4,] 1 1 4 -1.9126874 [5,] 1 1 5 -1.9126874 [6,] 1 1 6 -0.6917365 [7,] 1 1 7 -0.6917365 [8,] 1 1 8 -0.6917365 [9,] 1 1 9 -0.6917365 [10,] 1 1 10 -0.6731677 First 10 rows of 25600 printed. 

请参阅Matthew Dowle的回答(到目前为止,希望如上)。

这是使用data.table包的东西,当有多个IDvariables时它可能会有所帮助。 它也可能比merge更快,具体取决于你想要的结果。 我会对基准testing和/或build议的改进感兴趣。

首先,用两个IDvariables创build一些要求更高的数据

 library(data.table) set.seed(1) mydf3<-data.frame(Id=sample(1:100,10000,replace=TRUE), Value=rnorm(10000)) mydf3<-mydf3[order(mydf3$Id),] mydf3$Time<-unlist(by(mydf3,mydf3$Id, function(x)sample(1:(nrow(x)+3),nrow(x)),simplify=TRUE)) mydf3$Id2<-sample(1:2,nrow(mydf3),replace=TRUE) 

创build一个函数(这已被编辑 – 查看历史)

 padFun<-function(data,idvars,timevar){ # Coerce ID variables to character data[,idvars]<-lapply(data[,idvars,drop=FALSE],as.character) # Create global ID variable of all individual ID vars pasted together globalID<-Reduce(function(...)paste(...,sep="SOMETHINGWACKY"), data[,idvars,drop=FALSE]) # Create data.frame of all possible combinations of globalIDs and times allTimes<-expand.grid(globalID=unique(globalID), allTime=min(data[,timevar]):max(data[,timevar]), stringsAsFactors=FALSE) # Get the original ID variables back allTimes2<-data.frame(allTimes$allTime,do.call(rbind, strsplit(allTimes$globalID,"SOMETHINGWACKY")),stringsAsFactors=FALSE) # Convert combinations data.frame to data.table with idvars and timevar as key allTimesDT<-data.table(allTimes2) setnames(allTimesDT,1:ncol(allTimesDT),c(timevar,idvars)) setkeyv(allTimesDT,c(idvars,timevar)) # Convert data to data.table with same variables as key dataDT<-data.table(data,key=c(idvars,timevar)) # Join the two data.tables to create padding res<-dataDT[allTimesDT] return(res) } 

使用该function

 (padded2<-padFun(data=mydf3,idvars=c("Id"),timevar="Time")) # Id Time Value Id2 # [1,] 1 1 -0.262482283 2 # [2,] 1 2 -1.423935165 2 # [3,] 1 3 0.500523295 1 # [4,] 1 4 -1.912687398 1 # [5,] 1 5 -1.459766444 2 # [6,] 1 6 -0.691736451 1 # [7,] 1 7 NA NA # [8,] 1 8 0.001041489 2 # [9,] 1 9 0.495820559 2 # [10,] 1 10 -0.673167744 1 # First 10 rows of 12800 printed. (padded<-padFun(data=mydf3,idvars=c("Id","Id2"),timevar="Time")) # Id Id2 Time Value # [1,] 1 1 1 NA # [2,] 1 1 2 NA # [3,] 1 1 3 0.5005233 # [4,] 1 1 4 -1.9126874 # [5,] 1 1 5 NA # [6,] 1 1 6 -0.6917365 # [7,] 1 1 7 NA # [8,] 1 1 8 NA # [9,] 1 1 9 NA # [10,] 1 1 10 -0.6731677 # First 10 rows of 25600 printed. 

被编辑的函数在将原始数据合并之前,将globalID拆分为组合dataframe中的组成部分。 这应该(我认为)更好。

你可以用这个tidyr

使用tidyr::complete来填充Time行,默认情况下这些值是用NA填充的。

创build数据

我扩展了示例数据以显示它适用于多个Id ,即使在一个Id内,也不存在整个Time范围。

 library(dplyr) library(tidyr) df <- tibble( Id = c(1, 1, 1, 1, 2, 2, 2), Time = c(1, 2, 4, 5, 2, 3, 5), Value = c(0.56, -0.72, 1.24, 0.68, 1.46, 0.74, 0.99) ) df #> # A tibble: 7 x 3 #> Id Time Value #> <dbl> <dbl> <dbl> #> 1 1 1 0.56 #> 2 1 2 -0.72 #> 3 1 4 1.24 #> 4 1 5 0.68 #> 5 2 2 1.46 #> 6 2 3 0.74 #> 7 2 5 0.99 

填写缺失的行

 df %>% complete(nesting(Id), Time = seq(min(Time), max(Time), 1L)) #> # A tibble: 10 x 3 #> Id Time Value #> <dbl> <dbl> <dbl> #> 1 1 1 0.56 #> 2 1 2 -0.72 #> 3 1 3 NA #> 4 1 4 1.24 #> 5 1 5 0.68 #> 6 2 1 NA #> 7 2 2 1.46 #> 8 2 3 0.74 #> 9 2 4 NA #> 10 2 5 0.99 

我的一般做法是使用freqTable <- as.data.frame(table(idvar1, idvar2, idvarN))然后拉出Freq==0的行,根据需要填充,然后堆栈回原始数据。