用NA inplace data.table替换“ NA”(NA字符串)

时间:2018-08-06 17:05:45

标签: r data.table

我有这个虚拟数据集:

abc <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3), c = c("n", "NA", "NA"))

我试图用标准NA代替“ NA”;使用data.table到位。我尝试过:

for(i in names(abc)) (abc[which(abc[[i]] == "NA"), i := NA])
for(i in names(abc)) (abc[which(abc[[i]] == "NA"), i := NA_character_])
for(i in names(abc)) (set(abc, which(abc[[i]] == "NA"), i, NA))

但是我仍然得到:

abc$a 
"NA" "bc" "x"

我想念什么?

编辑:我在这个使用type.convert()的问题中尝试了@frank答案。 (感谢坦率;尽管有用的功能,它也不知道。)在type.convert()的文档中提到:“这主要是read.table的帮助函数。”所以我想彻底测试一下。当您用“ NA”(NA字符串)填充完整的列时,此功能会产生小的副作用。在这种情况下,type.convert()会将列转换为逻辑。在这种情况下,abc将是:

abc <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3), c = c("n", "NA", "NA"), d = c("NA", "NA", "NA"))

EDIT2:要总结原始问题中存在的代码:

for(i in names(abc)) (set(abc, which(abc[[i]] == "NA"), i, NA))

可以正常工作,但只能在data.table(> 1.11.4)的最新版本中使用。因此,如果遇到此问题,则比type.convert()

更新data.table和使用此代码更好

3 个答案:

答案 0 :(得分:5)

我愿意...

chcols = names(abc)[sapply(abc, is.character)]
abc[, (chcols) := lapply(.SD, type.convert, as.is=TRUE), .SDcols=chcols]

产生

> str(abc)
Classes ‘data.table’ and 'data.frame':  3 obs. of  3 variables:
 $ a: chr  NA "bc" "x"
 $ b: num  1 2 3
 $ c: chr  "n" NA NA
 - attr(*, ".internal.selfref")=<externalptr> 

您的DT[, i :=]代码无法正常工作,因为它创建了一个字面名为“ i”的列;正如@AdamSampson所指出的,您的set代码已经可以正常工作。 (请注意:在这种情况下,OP已从data.table 1.10.4-3升级到1.11.4。)


  

所以我想彻底测试一下。当您用“ NA”(NA字符串)填充完整的列时,此功能会产生小的副作用。在这种情况下,type.convert()会将列转换为逻辑。

哦,对了。您最初的方法可以更安全地解决此问题:

# op's new example
abc <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3), c = c("n", "NA", "NA"), d = c("NA", "NA", "NA"))

# op's original code
for(i in names(abc)) 
  set(abc, which(abc[[i]] == "NA"), i, NA)

旁注:NA具有逻辑类型;通常,在将不一致类型的值分配给列时,data.table会发出警告,但我想它们为NA编写了一个例外:

DT = data.table(x = 1:2)
DT[1, x := NA]
# no problem, even though x is int and NA is logi

DT = data.table(x = 1:2)
DT[1, x := TRUE]
# Warning message:
# In `[.data.table`(DT, 1, `:=`(x, TRUE)) :
#   Coerced 'logical' RHS to 'integer' to match the column's type. Either change the target column ['x'] to 'logical' first (by creating a new 'logical' vector length 2 (nrows of entire table) and assign that; i.e. 'replace' column), or coerce RHS to 'integer' (e.g. 1L, NA_[real|integer]_, as.*, etc) to make your intent clear and for speed. Or, set the column type correctly up front when you create the table and stick to it, please.

答案 1 :(得分:1)

我真的很喜欢Frank的回答,但想补充一下,因为它假设您只对字符向量进行更改。我还将尝试包含一些有关“为什么”起作用的信息。

要替换所有NA,您可以执行以下操作:

chcols = names(abc)
abc[,(chcols) := lapply(.SD, function(x) ifelse(x == "NA",NA,x)),.SDcols = chcols]

请细分我们在这里所做的事情。

我们正在查看abc中的每一行(因为第一个逗号之前没有任何内容)。

下一个逗号之后是列。让我们分解一下。

我们将结果放入chcols中列出的所有列中。 (chcols)告诉data.table方法评估chcols对象中保存的名称向量。如果省略括号并使用chcols,它将尝试将结果存储在名为chcols的列中,而不是使用所需的列名。

.SD返回一个data.table,其中包含.SDcols中列出的每一列的结果(在我的情况下,它返回所有列...)。但是我们想一次评估一个列。因此,我们使用lapply一次将函数应用于.SD中的每一列。

您可以使用任何将返回正确值的函数。弗兰克使用type.convert。我正在使用一个匿名函数来评估ifelse语句。我使用ifelse是因为它求值并返回整个向量/列。

您已经知道如何使用:=替换适当的值。

在下一列之后,您可以放置​​by信息,也可以放置其他选项。我们将以.SDcols的形式添加其他选项。

我们需要放置一个.SDcols = chcols来告诉data.table在.SD中包括哪些列。我的代码正在评估所有列,因此,如果您不使用.SDcols,我的代码将仍然有效。但这列是个坏习惯,因为如果您更改为仅评估某些列,则将来可能会浪费时间。 Frank的示例仅评估例如字符类的列。

答案 2 :(得分:0)

这是另外两种方法:

子集

library(data.table)
abcd <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3),
                   c = c("n", "NA", "NA"), d = c("NA", "NA", "NA"))

for (col in names(abcd)) abcd[get(col) == "NA", (col) := NA]
abcd[]
      a b    c    d
1: <NA> 1    n <NA>
2:   bc 2 <NA> <NA>
3:    x 3 <NA> <NA>

加入时更新

在这里,data.table对于变量类型非常严格。

abcd <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3),
                   c = c("n", "NA", "NA"), d = c("NA", "NA", "NA"))

for (col in names(abcd)) 
  if (is.character(abcd[[col]])) 
    abcd[.("NA", NA_character_), on = paste0(col, "==V1"), (col) := V2][]
abcd
      a b    c    d
1: <NA> 1    n <NA>
2:   bc 2 <NA> <NA>
3:    x 3 <NA> <NA>