自定义数据相关的重新编码到 R 中的逻辑

Custom data-dependent recoding to logicals in R

我有两个数据框,datametadata 中的一些(但不是全部)列是逻辑值,但它们以许多不同的方式编码。 meta 中的行描述了 data 中的列,指示它们是否被解释为逻辑,如果是,哪些单值代码为 TRUE,哪些单值代码为 FALSE。

我需要一个过程,将概念逻辑列中的所有 data 值替换为相应 meta 行中代码的适当逻辑值。与相应 meta 行中的值不匹配的概念逻辑列中的任何 data 值都应变为 NA。

meta 的小玩具示例:

name                 type     false  true
-----------------------------------------
a.char.var           char     NA     NA
a.logical.var        logical  NA     7
another.logical.var  logical  1      0
another.char.var     char     NA     NA

data 的小玩具示例:

a.char.var  a.logical.var  another.logical.var  another.char.var
----------------------------------------------------------------
aa          7              0                    ba
ab          NA             1                    bb
ac          7              NA                   bc
ad          4              3                    bd

小玩具示例输出:

a.char.var  a.logical.var  another.logical.var  another.char.var
----------------------------------------------------------------
aa          TRUE           TRUE                 ba
ab          FALSE          FALSE                bb
ac          TRUE           NA                   bc
ad          NA             NA                   bd

我这辈子都找不到在惯用的 R 中处理所有极端情况的方法。数据集很大,因此如果可能的话,惯用的解决方案将是理想的。我继承了这个绝对疯狂的数据管理混乱,我将感谢任何能帮助修复它的人。我绝不是 R 大师,但这似乎是一个看似困难的问题。

我编写了一个函数,它接受 data 的列索引并尝试执行您描述的操作。

该函数首先选择 x 作为我们感兴趣的列。然后我们将 data 中的列名称与 meta 的第一列中的条目进行匹配,这引起了我们的兴趣。

然后我们检查列类型是否为 logical,如果不是,我们只是 return x,不需要更改任何内容。如果列类型是 logical,我们将检查其值是否与 meta 中的 truefalse 列匹配。

convert_data <- function(colindex, dat, meta = meta){
    x <- dat[,colindex] #select our data vector

    #match the column name to the first column in meta
    find_in_meta <- match(names(dat)[colindex],
                          meta[,1])

    #what type of column is it
    type_col <- meta[find_in_meta,2]

    if(type_col != 'logical'){
        return(x)
    }else{
        #fix if logical is NA
        true_val <- ifelse(is.na(meta[find_in_meta,4]),'NA_val',
                           meta[find_in_meta,4])

        #fix if logical is NA
        false_val <- ifelse(is.na(meta[find_in_meta,3]), 'NA_val',
                            meta[find_in_meta, 3])

        #fix if logical is NA
        x <- ifelse(is.na(x), 'NA_val', x)
        x <- ifelse(x == true_val, TRUE,
               ifelse(x == false_val, FALSE, NA))
        return(x)
    }
}

然后我们可以使用 lapply 和一些数据操作使其成为可接受的形式:

res <- lapply(1:ncol(df1), function(ind) 
                      convert_data(colindex = ind, dat = df1, meta = meta))

setNames(do.call('cbind.data.frame', res), names(df1))

  a.char.var a.logical.var another.logical.var another.char.var
1         aa          TRUE                TRUE               ba
2         ab         FALSE               FALSE               bb
3         ac          TRUE                  NA               bc
4         ad            NA                  NA               bd

数据

meta <- structure(list(name = c("a.char.var", "a.logical.var", "another.logical.var", 
"another.char.var"), type = c("char", "logical", "logical", "char"
), false = c(NA, NA, 1L, NA), true = c(NA, 7L, 0L, NA)), .Names = c("name", 
"type", "false", "true"), class = "data.frame", row.names = c(NA, 
-4L))

df1 <- structure(list(a.char.var = c("aa", "ab", "ac", "ad"), a.logical.var = c(7L, 
NA, 7L, 4L), another.logical.var = c(0L, 1L, NA, 3L), another.char.var = c("ba", 
"bb", "bc", "bd")), .Names = c("a.char.var", "a.logical.var", 
"another.logical.var", "another.char.var"), class = "data.frame", row.names = c(NA, 
-4L))

首先我们设置数据

meta <- data.frame(name=c('a.char.var', 'a.logical.var', 'another.logical.var', 'another.char.var'),
                   type=c('char', 'logical', 'logical', 'char'),
                   false=c(NA, NA, 1, NA),
                   true=c(NA, 7, 0, NA), stringsAsFactors = F)

data <- data.frame(a.char.var=c('aa', 'ab', 'ac', 'ad'),
                   a.logical.var=c(7, NA, 7, 4),
                   another.logical.var=c(0,1,NA,3),
                   another.char.var=c('ba', 'bb', 'bc', 'bd'), stringsAsFactors = F)

然后我们只对逻辑列进行子集化。我们将遍历这些,使用 name 列到 select data 中的相关列,并将 data_out 中的值从初始化的 NA 更改为 TF 根据 data.

中的匹配值

请注意,如果 logical_meta$name 是一个字符,则 data[,logical_meta$name[1]] 等同于 data[,'a.logical.var']data$a.logical.var。如果它是一个因素(例如,如果我们没有指定 stringsAsFactors=F),我们需要转换为字符,此时我们不妨给它一个名称 - 下面的 colname

让 NA 来应对意味着使用 which 是有利的:c(0, 1,NA,3)==0 returns T,F,NA,F 但是 which 然后忽略 NA 和returns 就是位置 1。由包含 NA 的逻辑向量子集产生 NA 行或列,使用 which 消除了这一点。

logical_meta <- meta[meta$type=='logical',]

data_out <- data #initialize


for(i in 1:nrow(logical_meta)) {
  colname <- as.character(logical_meta$name[i]) #only need as.character if factor
  data_out[,colname] <- NA
  #false column first
  if(is.na(logical_meta$false[i])) {
    data_out[is.na(data[,colname]),colname] <- FALSE
  } else {
    data_out[which(data[,colname]==logical_meta$false[i]),
             colname] <- FALSE
  }
  #true column next
  if(is.na(logical_meta$true[i])) {
    data_out[is.na(data[,colname]),colname] <- TRUE
  } else {
    data_out[which(data[,colname]==logical_meta$true[i]),
             colname] <- TRUE
  }
}

data_out