如何用 na.spline 替换 data.table 中的 NA 值

How to replace NA values in a data.table with na.spline

我正在尝试准备一些从欧盟统计局检索到的人口统计数据以供进一步处理,其中包括用相应的近似数据替换任何缺失的数据。

起初我只使用 data.frames,但后来我确信 data.tables 可能比常规 data.frames 有一些优势,所以我迁移到 data.tables。

我在这样做时观察到的一件事是,将 "na.spline" 与 "apply" 结合使用与 "na.spline" 作为 data.table 的一部分时会得到不同的结果。

#1源数据

(dt0 <- data.table(
            "age,sex,geo\time" = c("TOTAL,F,AD", "TOTAL,F,AL", "TOTAL,F,AM", "TOTAL,F,AT", "TOTAL,F,AZ"),
            "2014" = c(NA,    NA,      NA,      4351253, NA),
            "2013" = c(37408, NA,      NA,      4328238, 4707690),
            "2012" = c(38252, NA,      1684000, 4309977, 4651601),
            "2011" = c(38252, 1409931, 1679066, 4296293, 4594023),
            "2010" = c(40296, NA,      1673656, 4285442, 4542083)
        ))

生成

       age,sex,geo\time    2014    2013    2012    2011    2010
    1:        TOTAL,F,AD      NA   37408   38252   38252   40296
    2:        TOTAL,F,AL      NA      NA      NA 1409931      NA
    3:        TOTAL,F,AM      NA      NA 1684000 1679066 1673656
    4:        TOTAL,F,AT 4351253 4328238 4309977 4296293 4285442
    5:        TOTAL,F,AZ      NA 4707690 4651601 4594023 4542083

分成"row headings"...

(dt0a <- dt0[, 1, with=FALSE])
(cn0a <- colnames(dt0a))

...和人口数据...

(dt0b <- dt0[, 2:ncol(dt0), with=FALSE])
(cn0ba <- colnames(dt0b))

#2 将 NAs 替换为 "na.spline" & "apply"

(dt1b <- data.table(t(apply(dt0b, 1, na.spline, na.rm=FALSE))))
(setnames(dt1b, cn0b))
(dt1 <- cbind(dt0a, dt1b))

这导致...

       age,sex,geo\time    2014    2013    2012    2011    2010
    1:        TOTAL,F,AD   32832   37408   38252   38252   40296
    2:        TOTAL,F,AL 1409931 1409931 1409931 1409931 1409931
    3:        TOTAL,F,AM 1692440 1688458 1684000 1679066 1673656
    4:        TOTAL,F,AT 4351253 4328238 4309977 4296293 4285442
    5:        TOTAL,F,AZ 4755163 4707690 4651601 4594023 4542083

#3 在 "data.table"

内替换 NAs
(dt2b <- dt0b[,lapply(.SD, na.spline, na.rm=FALSE)])
(dt2 <- cbind(dt0a, dt2b))

瞧瞧...

       age,sex,geo\time    2014    2013      2012    2011      2010
    1:        TOTAL,F,AD 4351253   37408   38252.0   38252   40296.0
    2:        TOTAL,F,AL 4351253 1993097 -611513.8 1409931 -629363.2
    3:        TOTAL,F,AM 4351253 3423374 1684000.0 1679066 1673656.0
    4:        TOTAL,F,AT 4351253 4328238 4309977.0 4296293 4285442.0
    5:        TOTAL,F,AZ 4351253 4707690 4651601.0 4594023 4542083.0

#4 比较结果

(identical(dt1, dt2))

考虑到上述情况并不令人意外...

    [1] FALSE

(用方法 #2 计算的替代 NAs 的值是我感兴趣的值,仅通过方法 #3 生成)。

追求 "data.table" 路线(方法 #3)的原因之一是性能(正如在各种帖子中指出的那样,当使用 "apply" 时,矩阵运算正在执行,这比仅促进 "data.table" 的相应方法花费的时间要长得多)。

作为 RI 的新手,我想我做了一些根本性的错误,唯一的问题是,我一点也不知道这可能是什么。

非常感谢能为我指明正确方向的任何帮助!

-Sil68

您在 apply 版本中进行行向运算,在 data.table 版本中进行列向运算。如果设置 by = 1:nrow(dt),则可以在 data.table 中进行按行操作。

dt2b <- dt0b[, as.list(na.spline(unlist(.SD), na.rm=FALSE)), by = 1:nrow(dt0b)]

也可以使用.SDcols,这样就不需要拆分数据了。如果 age,sex,geo\time 是唯一的,以下将起作用:

dt0[, as.list(na.spline(unlist(.SD), na.rm=FALSE)), by = `age,sex,geo\time`, .SDcols = -"age,sex,geo\time"]
##    age,sex,geo\time      V1      V2      V3      V4      V5
## 1:        TOTAL,F,AD   32832   37408   38252   38252   40296
## 2:        TOTAL,F,AL 1409931 1409931 1409931 1409931 1409931
## 3:        TOTAL,F,AM 1692440 1688458 1684000 1679066 1673656
## 4:        TOTAL,F,AT 4351253 4328238 4309977 4296293 4285442
## 5:        TOTAL,F,AZ 4755163 4707690 4651601 4594023 4542083

使用矩阵。在 matrix 上使用矩阵运算并不慢:

mat           <- t(as.matrix(dt0[,-1]))
colnames(mat) <- dt0[[1]]
mat[]         <- na.spline(mat,na.rm=FALSE)

这给出了

     TOTAL,F,AD TOTAL,F,AL TOTAL,F,AM TOTAL,F,AT TOTAL,F,AZ
2014      32832    1409931    1692440    4351253    4755163
2013      37408    1409931    1688458    4328238    4707690
2012      38252    1409931    1684000    4309977    4651601
2011      38252    1409931    1679066    4296293    4594023
2010      40296    1409931    1673656    4285442    4542083

使用 data.table. 如果您想使用 data.table,请执行

mat           <- t(as.matrix(dt0[,-1]))
colnames(mat) <- dt0[[1]]
DT            <- data.table(mat,keep.rownames=TRUE)
DT[,(vn):=lapply(.SD,na.spline,na.rm=FALSE),.SDcols=vn]

通过引用更新 DT,给出

     rn TOTAL,F,AD TOTAL,F,AL TOTAL,F,AM TOTAL,F,AT TOTAL,F,AZ
1: 2014      32832    1409931    1692440    4351253    4755163
2: 2013      37408    1409931    1688458    4328238    4707690
3: 2012      38252    1409931    1684000    4309977    4651601
4: 2011      38252    1409931    1679066    4296293    4594023
5: 2010      40296    1409931    1673656    4285442    4542083

基准测试:

mat           <- t(as.matrix(dt0[,-1]))
colnames(mat) <- dt0[[1]]
DT            <- data.table(mat,keep.rownames=TRUE)
vn            <- names(DT)[-1]
tvn           <- names(dt0)[-1]

require(microbenchmark)
microbenchmark(
  transp = dt0[,as.list(na.spline(unlist(.SD), na.rm=FALSE)),by=1:nrow(dt0),.SDcols=tvn],
  lapply = DT[,lapply(.SD,na.spline,na.rm=FALSE),.SDcols=vn],
  apply  = apply(mat,2,na.spline,na.rm=FALSE),
  fun    = na.spline(mat,na.rm=FALSE),
times=10)

结果:

Unit: milliseconds
   expr      min       lq     mean   median       uq      max neval
 transp 4.666934 4.734891 4.850268 4.787690 4.897202 5.259957    10
 lapply 3.923823 4.010356 4.327646 4.039445 4.049957 6.976446    10
  apply 2.505556 2.525601 2.578890 2.585978 2.592090 2.758801    10
    fun 1.945290 1.994178 2.063063 2.068490 2.085112 2.272846    10

"transp" 结果显示@shadow 解决方案的时间安排,它保留了 OP 的格式。由于 na.spline 的工作原理,这里不需要 apply