修复由 9 表示的 NA(例如:99、999、9999)匹配 data.table(或 fread)中的列宽

Fixing NAs represented by 9s (ex: 99, 999, 9999) matching column width in data.table (or in fread)

文本文件中的数据通常具有表示 NA 的 9 的可变长度序列。也就是说,表示 NA 的 9 的数量取决于每个变量中的字符数。例如:

清理这些值的最佳方法是什么?

请注意,在fread中,na.values=c('99','999')不是一个理想的选择,因为它会破坏3位变量中合法的99个值。

假设我有 data.table d 和两组数字列

cols_2digit <- c('a','b')
cols_3digit <- c('c','d')

如何一次性用 NA 替换每组所有列中的 9 序列?组数有限,一组一个命令即可。

OBS:这些可变长度的 NA 代码让人想起固定宽度的文件 (fwf),即使现代文件以 csv 格式提供(对于跨列的 NA 可能采用标准的“999999”值)。

我们可以通过遍历 'cols_2digit' 或 'cols_3digit' 中指定的列来使用 set 并在适当的位置更改列中的值

for(j in cols_2digit) set(d, i = which(d[[j]] == '99'), j = j, value = NA_character_)
for(j in cols_3digit) set(d, i = which(d[[j]] == '999'), j = j, value = NA_character_)

或者另一种选择是Map

d[, c(cols_2digit, col2_3digit) := 
     Map(function(dat, y) lapply(dat, function(x) 
         fifelse(x, x == y, NA_character_)), list(.SD[, ..cols_2digit],
                             .SD[, ..cols_3digit]), list('99', '999')) ]

此外,不是在不同的集合上这样做,另一种选择是根据最大频率找到列宽

Mode <- function(x) {
   ux <- unique(x)
   ux[which.max(tabulate(match(x, ux)))]
   }

d[, lapply(.SD, function(x) {
                # get the most frequent column width
                colwidth <-  Mode(nchar(x))
                # if it is max 
                # colwidth <- max(nchar(x))
                # get the elements that are only 9 from start (`^`) to end (`$`)
                i1 <- grepl('^9+$', x) 
                # do the assignment based on the index
                x[i1][nchar(x[i1]) == colwidth] <- NA_character_
                x
              })]