R 按组高效填充NAs

R 按组高效填充NAs,r,performance,na,R,Performance,Na,我有一个数据集,我观察到一些人的变量,而不是其他人的变量。对于那些我观察变量的人,我只观察它一次。但是,每个人的观察次数以及观察值的位置各不相同 我想用非NA值填充给定个体的所有NA值,以防存在非NA值。否则,NAs应该保持NA 以下是一个示例数据集: #data.frame of 100 individuals with 10 observations each data <- data.frame(group = rep(1:100,each=10),value = NA) #fir

我有一个数据集,我观察到一些人的变量,而不是其他人的变量。对于那些我观察变量的人,我只观察它一次。但是,每个人的观察次数以及观察值的位置各不相同

我想用非NA值填充给定个体的所有NA值,以防存在非NA值。否则,NAs应该保持NA

以下是一个示例数据集:

#data.frame of 100 individuals with 10 observations each
data <- data.frame(group = rep(1:100,each=10),value = NA)

#first 50 individuals get a value at the fifth observation, others don't have value
data$value[seq(5,500,10)] <- rnorm(50)
这很好地解决了这个问题。然而,我必须花大约8000万美元来做这件事。观察,这需要几个小时。有没有更快的方法?我认为
data.table
可能是一个很好的候选者

如果能够调整该方法以仅填充出现在值之前的NAs,那将是一件非常棒的事情


谢谢

我们可以使用
data.table
进行适当的分配。这里,
na.locf
来自
zoo
用于用相邻的非na元素填充na元素

library(data.table)
library(zoo)
setDT(data)[, value := na.locf(na.locf(value, na.rm = FALSE), fromLast = TRUE), group]
基准
set.seed(24)
数据1%#默认方向向下
填充(值,.direction=“向上”)
})
#剩余17%~33m
注意:这花了很多时间。因此,我们必须中止会话


注2:这种方法是基于这样一个假设,即我们希望用非NA相邻元素替换NA元素,并且每组有多个非NA元素

您可以使用一种非常简单的方法处理data.table和dplyr,我相信这将非常快速有效:

在data.table中:

library(data.table)
setDT(data)
data[, value := value[!is.na(value)][1L], by = group]
或dplyr:

library(dplyr)
data <- data %>% 
  group_by(group) %>% 
  mutate(value = value[!is.na(value)][1L])
库(dplyr)
数据%
分组依据(分组)%>%
变异(值=值[!is.na(值)][1L])

关键是你有一个非NA值,每个组正好0或1次。因此,您不需要将最后一个观察结果带入逻辑。只需取第一个非NA值(如果存在)。

这是我使用的代码:您的代码vs akrun vs我的代码。有时动物园不是最快的过程,但它是最干净的。无论如何,你可以测试它

更新: 到目前为止,它已经用更多的数据(100.000)和过程03(子集和合并)进行了测试

上次更新 与rbenchmark的功能比较:

library(dplyr)
library(tidyr)
library(base)
library(data.table)
library(zoo)
library(rbenchmark)

#data.frame of 100 individuals with 10 observations each
data <- data.frame(group = rep(1:10000,each=10),value = NA)
data$value[seq(5,5000,10)] <- rnorm(50) #first 50 individuals get a value at the fifth observation, others don't have value

#Process01
P01 <- function (data){
    data01 <- data %>% 
        group_by(group) %>% #by group
            fill(value) %>% #default direction down
            fill(value, .direction = "up") #also fill NAs upwards
    return(data01)
}

#Process02
P02 <- function (data){
    data02 <- setDT(data)[, value := na.locf(na.locf(value, na.rm = FALSE), 
                                             fromLast = TRUE), group]
    return(data02)
}

#Process03
P03 <- function (data){
    dataU <- subset(unique(data), value!='NA') #keep row number
    dataM <- merge(data, dataU, by = "group", all=T) #merge tables
    data03 <- data.frame(group=dataM$group, value = dataM$value.y) #idem shape of data
    return(data03)
}

benchmark("P01_dplyr" = {data01 <- P01(data)},
          "P02_zoo" = {data02 <- P02(data)},
          "P03_data.table" = {data03 <- P03(data)},
          replications = 10,
          columns = c("test", "replications", "elapsed")
          )

非常感谢!我也进行了基准测试(10万次观察),得出您的方法大约需要tidyverse方法的1/5时间。看看它的规模会很有趣@Zen先生,对于我来说,DPLYR方法只完成了27%个,而且更多的时间考虑了学习<代码> Tabe<代码>包或<代码>微标尺< /代码>。有时还包括定义示例数据,有时不包括,OP提到他有非常大的数据。。我想在打替补时应该考虑到这一点。到目前为止,我已经尝试增加数据并处理03胜。
library(dplyr)
data <- data %>% 
  group_by(group) %>% 
  mutate(value = value[!is.na(value)][1L])
library(dplyr)
library(tidyr)
library(base)
library(data.table)
library(zoo)
library(rbenchmark)

#data.frame of 100 individuals with 10 observations each
data <- data.frame(group = rep(1:10000,each=10),value = NA)
data$value[seq(5,5000,10)] <- rnorm(50) #first 50 individuals get a value at the fifth observation, others don't have value

#Process01
P01 <- function (data){
    data01 <- data %>% 
        group_by(group) %>% #by group
            fill(value) %>% #default direction down
            fill(value, .direction = "up") #also fill NAs upwards
    return(data01)
}

#Process02
P02 <- function (data){
    data02 <- setDT(data)[, value := na.locf(na.locf(value, na.rm = FALSE), 
                                             fromLast = TRUE), group]
    return(data02)
}

#Process03
P03 <- function (data){
    dataU <- subset(unique(data), value!='NA') #keep row number
    dataM <- merge(data, dataU, by = "group", all=T) #merge tables
    data03 <- data.frame(group=dataM$group, value = dataM$value.y) #idem shape of data
    return(data03)
}

benchmark("P01_dplyr" = {data01 <- P01(data)},
          "P02_zoo" = {data02 <- P02(data)},
          "P03_data.table" = {data03 <- P03(data)},
          replications = 10,
          columns = c("test", "replications", "elapsed")
          )
    test replications elapsed
1      P01_dplyr           10  257.78
2        P02_zoo           10   10.35
3 P03_data.table           10    0.09