按组有效填写NAs

Mr.*_*Zen 8 performance r na

我有一个数据集,我在某个人身上观察变量而不是其他人.对于我观察变量的那些人,我只观察了一次.但是,每个人的观察数量以及观察值的位置会有所不同.

如果存在非NA值,我想用非NA值填充给定个体的所有NA值.否则,NA应该保持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)
Run Code Online (Sandbox Code Playgroud)

到目前为止这么好,不是一个大问题.从另一个线程中获取,我们可以使用dplyr和执行以下操作tidyr:

data <- data %>% 
  group_by(group) %>% #by group
  fill(value) %>% #default direction down
  fill(value, .direction = "up") #also fill NAs upwards
Run Code Online (Sandbox Code Playgroud)

这完全解决了这个问题.但是,我必须为大约80十年代做这件事.观察,需要数小时.有更快的方法吗?我认为data.table可能是一个很好的候选人.

如果可以调整方法以仅填充出现在值之前的NA,那也将是很好的.

谢谢!

tal*_*lat 6

你可以使用一个非常简单的方法,data.table和dplyr - 我相信 - 会非常快速和高效:

在data.table中:

library(data.table)
setDT(data)
data[, value := value[!is.na(value)][1L], by = group]
Run Code Online (Sandbox Code Playgroud)

或者dplyr:

library(dplyr)
data <- data %>% 
  group_by(group) %>% 
  mutate(value = value[!is.na(value)][1L])
Run Code Online (Sandbox Code Playgroud)

关键是你每组的非NA值恰好是o或1次.因此,您不需要最后观察结转的逻辑.只需取第一个非NA值(如果存在).


Cés*_*ero 2

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

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

最后的 UPDATE 函数与 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")
          )
Run Code Online (Sandbox Code Playgroud)

数据 = 10.000、10 次重复和 I5 7400 的结果:

    test replications elapsed
1      P01_dplyr           10  257.78
2        P02_zoo           10   10.35
3 P03_data.table           10    0.09
Run Code Online (Sandbox Code Playgroud)

  • 可能会考虑研究“bench”包或“microbenchmark”。另外,有时您会定义示例数据,有时则不会,OP提到他有非常大的数据。我想在基准测试时应该考虑到这一点。 (2认同)