Q:

Replacing "NA" (NA string) with NA inplace data.table

I have this dummy dataset:
abc <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3), c = c("n", "NA", "NA"))

where I am trying to replace "NA" with standard NA; in place using data.table. I tried:
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))

However still with this I get:
abc$a 
"NA" "bc" "x"

What am I missing?
EDIT: I tried @frank answer in this question which makes use of type.convert(). (Thanks frank; didn't know such obscure albeit useful function) In documentation of type.convert() it is mentioned: "This is principally a helper function for read.table." so I wanted to test this thoroughly. This function comes with small side effect when you have a complete column filled with "NA" (NA string). In such case type.convert() is converting column to logical. For such case abc will be:
abc <- data.table(a = c("NA", "bc", "x"), b = c(1, 2, 3), c = c("n", "NA", "NA"), d = c("NA", "NA", "NA"))

EDIT2: To summerize code present in original question:
for(i in names(abc)) (set(abc, which(abc[[i]] == "NA"), i, NA))

works fine but only in current latest version of data.table (> 1.11.4). So if one is facing this problem then its better to update data.table and use this code than type.convert()

A:

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

which yields
> 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> 

Your DT[, i :=] code did not work because it creates a column literally named "i"; and your set code does work already, as @AdamSampson pointed out. (Note: OP upgraded from data.table 1.10.4-3 to 1.11.4 before this was the case on their comp.)

so I wanted to test this thoroughly. This function comes with small side effect when you have a complete column filled with "NA" (NA string). In such case type.convert() is converting column to logical.

Oh right. Your original approach is safer against this problem:
# 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)

Side note: NA has type logical; and usually data.table would warn when assigning values of an incongruent type to a column, but I guess they wrote in an exception for NAs:
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.

