当列是因子时,R data.table用"NA"替换"NULL"

Box*_*uan 5 r data.table

我通过ODBC从SQL数据库中提取了一些数据,并且列自动设置为factor.它类似于以下内容:

library(RODBC)
library(data.table)
data <- data.table(sqlQuery(channel, query))
Run Code Online (Sandbox Code Playgroud)

我的数据看起来像这样,只有更多的列:

data <- data.table("C1"=as.factor(c(letters[1:4], "NULL", letters[5])),
                   "C2"=as.factor(c(rnorm(3), "NULL", rnorm(2))),
                   "C3"=as.factor(c(letters[1], "NULL", letters[2:4], "NULL")))
> data
     C1                 C2   C3
1:    a -0.190200079604691    a
2:    b  0.310548914832963 NULL
3:    c 0.0153099116493453    b
4:    d               NULL    c
5: NULL  0.157187027626419    d
6:    e  0.118537540781528 NULL
> str(data)
Classes ‘data.table’ and 'data.frame':  6 obs. of  3 variables:
 $ C1: Factor w/ 6 levels "a","b","c","d",..: 1 2 3 4 6 5
 $ C2: Factor w/ 6 levels "-0.190200079604691",..: 1 5 2 6 4 3
 $ C3: Factor w/ 5 levels "a","b","c","d",..: 1 5 2 3 4 5
 - attr(*, ".internal.selfref")=<externalptr> 
Run Code Online (Sandbox Code Playgroud)

如何用"NULL"替换NA?在这里,我想R将这些SQL"NULL"字符串视为缺失值NA.我尝试了以下,但似乎NA导致问题.

for (col in names(data)) {
  set(data, which(data[[col]]=="NULL"), col, NA)
}

> Error in set(data, which(data[[col]] == "NULL"), col, NA) : 
  Can't assign to column 'C1' (type 'factor') a value of type 'logical' (not character, factor, integer or numeric)
Run Code Online (Sandbox Code Playgroud)

RODBC解决方案

感谢@ user20650的建议,您可以sqlQuery通过执行来控制缺失值data <- data.table(sqlQuery(channel, query, na.strings=c("NA", "NULL"))).但是,如果您的数据源格式不正确,仍然可能出现此问题,因此这不是该帖子的通用解决方案.

42-*_*42- 6

这具有所需的效果,并且更加紧凑:

is.na(data) <- data == "NULL"
Run Code Online (Sandbox Code Playgroud)

有一种is.na.data.frame方法,但不是一种is.na[<-.dataframe方法.所以不确定这是否是纯粹的引用策略,因为它没有用[.data.frame语法实现.它可能使用"is.na < - .default".

我认为在更多地说明"is.na < - .default"(这只是{x[value] <- NA; x})之后,所以最终会调用此调用,[<-.data.table因此它可能会"通过引用"完成.


Fra*_*ank 5

这是一种方式:

data[,names(data):=lapply(.SD,function(x){
  z <- levels(x)
  z[z=="NULL"] <- NA
  `levels<-`(x,z)
})]
Run Code Online (Sandbox Code Playgroud)

要看看发生了什么,看看,lapply(data,levels)你会发现它"NULL"已经消失了.


(谢谢,@ kruun :)使用该car软件包可以获得更简洁直观的变体:

library(car)
data[,names(data):=lapply(.SD, recode, '"NULL"=NA')]
Run Code Online (Sandbox Code Playgroud)

data.table世界上,通常可以通过引用进行修改.在这种情况下,这看起来像......

for (j in names(data)) setattr(data[[j]],"levels",{
  z <- levels(data[[j]])
  z[z=="NULL"] <- NA
  z
})
Run Code Online (Sandbox Code Playgroud)

这样可以避免复制整个矢量`levels<-`.