将 NA 替换为 0,仅在 data.table 中的数字列中
Replace NA with 0, only in numeric columns in data.table
我有一个包含不同数据类型的列的 data.table。我的目标是 select 只有数字列,并将这些列中的 NA 值替换为 0。
我知道用零替换 na-values 是这样的:
DT[is.na(DT)] <- 0
对于 select 只有数字列,我找到了这个解决方案,效果很好:
DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE]
我可以通过分配
来实现我想要的
DT2 <- DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE]
然后做:
DT2[is.na(DT2)] <- 0
但我当然希望通过引用修改我的原始 DT。但是,具有以下内容:
DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE]
[is.na(DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE])]<- 0
我明白了
"Error in [.data.table
([...] i is invalid type (matrix)"
我错过了什么?
非常感谢任何帮助!!
我们可以使用set
for(j in seq_along(DT)){
set(DT, i = which(is.na(DT[[j]]) & is.numeric(DT[[j]])), j = j, value = 0)
}
或者为数字列创建一个索引,遍历它并且 set
NA 值变为 0
ind <- which(sapply(DT, is.numeric))
for(j in ind){
set(DT, i = which(is.na(DT[[j]])), j = j, value = 0)
}
数据
set.seed(24)
DT <- data.table(v1= c(NA, 1:4), v2 = c(NA, LETTERS[1:4]), v3=c(rnorm(4), NA))
我想探索并可能改进@akrun 上面给出的出色答案。这是他在示例中使用的数据:
library(data.table)
set.seed(24)
DT <- data.table(v1= c(NA, 1:4), v2 = c(NA, LETTERS[1:4]), v3=c(rnorm(4), NA))
DT
#> v1 v2 v3
#> 1: NA <NA> -0.5458808
#> 2: 1 A 0.5365853
#> 3: 2 B 0.4196231
#> 4: 3 C -0.5836272
#> 5: 4 D NA
以及他建议使用的两种方法:
fun1 <- function(x){
for(j in seq_along(x)){
set(x, i = which(is.na(x[[j]]) & is.numeric(x[[j]])), j = j, value = 0)
}
}
fun2 <- function(x){
ind <- which(sapply(x, is.numeric))
for(j in ind){
set(x, i = which(is.na(x[[j]])), j = j, value = 0)
}
}
我认为上面的第一种方法真的很天才,因为它利用了 NA 是有类型的这一事实。
首先,即使 .SD
在 i
参数中不可用,也可以使用 get()
提取列名,所以我想我可以子分配data.table
这样:
fun3 <- function(x){
nms <- names(x)[sapply(x, is.numeric)]
for(j in nms){
x[is.na(get(j)), (j):=0]
}
}
一般情况下,当然是依靠 .SD
和 .SDcols
仅在数字列上工作
fun4 <- function(x){
nms <- names(x)[sapply(x, is.numeric)]
x[, (nms):=lapply(.SD, function(i) replace(i, is.na(i), 0)), .SDcols=nms]
}
但后来我心想“嘿,谁说我们不能一直使用 R 来进行这种操作。这是简单的 lapply()
和条件语句,包裹在 setDT()
fun5 <- function(x){
setDT(
lapply(x, function(i){
if(is.numeric(i))
i[is.na(i)]<-0
i
})
)
}
最后,我们可以使用相同的条件思想来限制我们应用 set()
的列
fun6 <- function(x){
for(j in seq_along(x)){
if (is.numeric(x[[j]]) )
set(x, i = which(is.na(x[[j]])), j = j, value = 0)
}
}
以下是基准测试:
microbenchmark::microbenchmark(
for.set.2cond = fun1(copy(DT)),
for.set.ind = fun2(copy(DT)),
for.get = fun3(copy(DT)),
for.SDcol = fun4(copy(DT)),
for.list = fun5(copy(DT)),
for.set.if =fun6(copy(DT))
)
#> Unit: microseconds
#> expr min lq mean median uq max neval cld
#> for.set.2cond 59.812 67.599 131.6392 75.5620 114.6690 4561.597 100 a
#> for.set.ind 71.492 79.985 142.2814 87.0640 130.0650 4410.476 100 a
#> for.get 553.522 569.979 732.6097 581.3045 789.9365 7157.202 100 c
#> for.SDcol 376.919 391.784 527.5202 398.3310 629.9675 5935.491 100 b
#> for.list 69.722 81.932 137.2275 87.7720 123.6935 3906.149 100 a
#> for.set.if 52.380 58.397 116.1909 65.1215 72.5535 4570.445 100 a
您需要 tidyverse purrr
函数 map_if
和 ifelse
才能在一行代码中完成这项工作。
library(tidyverse)
set.seed(24)
DT <- data.table(v1= sample(c(1:3,NA),20,replace = T), v2 = sample(c(LETTERS[1:3],NA),20,replace = T), v3=sample(c(1:3,NA),20,replace = T))
下面的单行代码采用带有数字和非数字列的 DT,并仅对数字列进行操作以将 NA 替换为 0:
DT %>% map_if(is.numeric,~ifelse(is.na(.x),0,.x)) %>% as.data.table
所以,有时 tidyverse 可以比 data.table 更简洁 :-)
我有一个包含不同数据类型的列的 data.table。我的目标是 select 只有数字列,并将这些列中的 NA 值替换为 0。 我知道用零替换 na-values 是这样的:
DT[is.na(DT)] <- 0
对于 select 只有数字列,我找到了这个解决方案,效果很好:
DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE]
我可以通过分配
来实现我想要的DT2 <- DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE]
然后做:
DT2[is.na(DT2)] <- 0
但我当然希望通过引用修改我的原始 DT。但是,具有以下内容:
DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE]
[is.na(DT[, as.numeric(which(sapply(DT,is.numeric))), with = FALSE])]<- 0
我明白了
"Error in
[.data.table
([...] i is invalid type (matrix)"
我错过了什么? 非常感谢任何帮助!!
我们可以使用set
for(j in seq_along(DT)){
set(DT, i = which(is.na(DT[[j]]) & is.numeric(DT[[j]])), j = j, value = 0)
}
或者为数字列创建一个索引,遍历它并且 set
NA 值变为 0
ind <- which(sapply(DT, is.numeric))
for(j in ind){
set(DT, i = which(is.na(DT[[j]])), j = j, value = 0)
}
数据
set.seed(24)
DT <- data.table(v1= c(NA, 1:4), v2 = c(NA, LETTERS[1:4]), v3=c(rnorm(4), NA))
我想探索并可能改进@akrun 上面给出的出色答案。这是他在示例中使用的数据:
library(data.table)
set.seed(24)
DT <- data.table(v1= c(NA, 1:4), v2 = c(NA, LETTERS[1:4]), v3=c(rnorm(4), NA))
DT
#> v1 v2 v3
#> 1: NA <NA> -0.5458808
#> 2: 1 A 0.5365853
#> 3: 2 B 0.4196231
#> 4: 3 C -0.5836272
#> 5: 4 D NA
以及他建议使用的两种方法:
fun1 <- function(x){
for(j in seq_along(x)){
set(x, i = which(is.na(x[[j]]) & is.numeric(x[[j]])), j = j, value = 0)
}
}
fun2 <- function(x){
ind <- which(sapply(x, is.numeric))
for(j in ind){
set(x, i = which(is.na(x[[j]])), j = j, value = 0)
}
}
我认为上面的第一种方法真的很天才,因为它利用了 NA 是有类型的这一事实。
首先,即使 .SD
在 i
参数中不可用,也可以使用 get()
提取列名,所以我想我可以子分配data.table
这样:
fun3 <- function(x){
nms <- names(x)[sapply(x, is.numeric)]
for(j in nms){
x[is.na(get(j)), (j):=0]
}
}
一般情况下,当然是依靠 .SD
和 .SDcols
仅在数字列上工作
fun4 <- function(x){
nms <- names(x)[sapply(x, is.numeric)]
x[, (nms):=lapply(.SD, function(i) replace(i, is.na(i), 0)), .SDcols=nms]
}
但后来我心想“嘿,谁说我们不能一直使用 R 来进行这种操作。这是简单的 lapply()
和条件语句,包裹在 setDT()
fun5 <- function(x){
setDT(
lapply(x, function(i){
if(is.numeric(i))
i[is.na(i)]<-0
i
})
)
}
最后,我们可以使用相同的条件思想来限制我们应用 set()
fun6 <- function(x){
for(j in seq_along(x)){
if (is.numeric(x[[j]]) )
set(x, i = which(is.na(x[[j]])), j = j, value = 0)
}
}
以下是基准测试:
microbenchmark::microbenchmark(
for.set.2cond = fun1(copy(DT)),
for.set.ind = fun2(copy(DT)),
for.get = fun3(copy(DT)),
for.SDcol = fun4(copy(DT)),
for.list = fun5(copy(DT)),
for.set.if =fun6(copy(DT))
)
#> Unit: microseconds
#> expr min lq mean median uq max neval cld
#> for.set.2cond 59.812 67.599 131.6392 75.5620 114.6690 4561.597 100 a
#> for.set.ind 71.492 79.985 142.2814 87.0640 130.0650 4410.476 100 a
#> for.get 553.522 569.979 732.6097 581.3045 789.9365 7157.202 100 c
#> for.SDcol 376.919 391.784 527.5202 398.3310 629.9675 5935.491 100 b
#> for.list 69.722 81.932 137.2275 87.7720 123.6935 3906.149 100 a
#> for.set.if 52.380 58.397 116.1909 65.1215 72.5535 4570.445 100 a
您需要 tidyverse purrr
函数 map_if
和 ifelse
才能在一行代码中完成这项工作。
library(tidyverse)
set.seed(24)
DT <- data.table(v1= sample(c(1:3,NA),20,replace = T), v2 = sample(c(LETTERS[1:3],NA),20,replace = T), v3=sample(c(1:3,NA),20,replace = T))
下面的单行代码采用带有数字和非数字列的 DT,并仅对数字列进行操作以将 NA 替换为 0:
DT %>% map_if(is.numeric,~ifelse(is.na(.x),0,.x)) %>% as.data.table
所以,有时 tidyverse 可以比 data.table 更简洁 :-)