【问题标题】:automatically code binary variables as factors?自动将二进制变量编码为因子?
【发布时间】:2020-11-26 17:00:51
【问题描述】:

我有一个问题:是否有一个 R 函数可以自动将二进制变量编码为因子?

我有一个包含 80 多个变量(列)的 tibble,其中许多是 R 导入为 numeric 的布尔性质(0、1 和 NA)。由于我想避免手动将它们转换为因子,我想知道data.frame(或@987654322)中是否有能够自动检测二进制数字变量的函数@) 并将它们更改为因子?我可以自己创建这样的函数,但如果它已经存在,为什么还要麻烦呢?

【问题讨论】:

  • 也许是purrr::map_df(df, as.factor) ?
  • 您需要function(x) { if(all(x %in% c(0, 1, NA)) as.factor(x); x } 之类的东西吗?
  • 导入时可以设置colClasses,例如:x <- read.table(..., colClasses = "factor")
  • @markus 是的,可能是这样的。并非我的所有列都是二元变量(我也有连续变量和多水平因子)。 Allan 我不知道这个 map_df 函数,但我会看看它。感谢您的回复!
  • 我不确定 [binary-data] 标签是否真的合适...

标签: r boolean r-factor


【解决方案1】:

下面我们假设一列只要满足就被认为是二进制的

  • 不全是 NA 和
  • 除 NA 外,它仅由数字 0 和 1 值组成。

请注意,完全为 0 和 NA 或完全为 1 和 NA 的列被视为二进制,但如果不希望这样做,我们将展示如何更改代码以要求二进制列同时具有 0 和 1。

首先定义一个函数is_binary,它定义一个列是否被视为二进制。如果要更改二进制的定义,可以更改此功能。如果列必须同时具有 0 和 1 才能将其视为二进制,则特别将下面代码中的 1:2 更改为 2。如果需要,其他定义也是可能的。

接下来将is_binary 应用于每一列,返回一个逻辑向量ok,每列有一个组件,如果该列是二进制则为TRUE,否则为FALSE。

在计算答案DF2 的行中,我们使用参数levels = 0:1factor 应用于每个二进制列,以确保只有0 或只有1 的列仍然具有两个级别。

没有使用任何包。

DF <- data.frame(a = c(0:1, NA), b = 1:3, c = NA, d = 0) # test data frame

is_binary <- function(x) {
  x0 <- na.omit(x)
  is.numeric(x) && length(unique(x0)) %in% 1:2 && all(x0 %in% 0:1)
}
ok <- sapply(DF, is_binary)
DF2 <- replace(DF, ok, lapply(DF[ok], factor, levels = 0:1))

str(DF2)
## 'data.frame':   3 obs. of  4 variables:
##  $ a: Factor w/ 2 levels "0","1": 1 2 NA
##  $ b: int  1 2 3
##  $ c: logi  NA NA NA
##  $ d: Factor w/ 2 levels "0","1": 1 1 1

我们可以像这样交替使用 dplyr 和 is_binary

DF %>% mutate(across(where(is_binary), ~ factor(., levels = 0:1)))

【讨论】:

    【解决方案2】:

    您可以使用where tidyselect 函数:

    library(dplyr)
    data %>% 
      mutate(across(where(~all(unique(.[!is.na(.)]) %in% c("0","1"))), as.factor))
    

    where 的参数必须是返回TRUEFALSE 的函数。这里我使用unique 来确保所有值都是01。使用%in% 有助于解决二进制变量实际编码在字符向量中的边缘情况。由于0L %in% "0"0 %in% "0""0" %in% "0" 都评估TRUE

    只有当提供给where 的函数评估TRUE 时,才会将as.factor 应用于列。

    data %>% 
      dplyr::summarise(across(everything(), class))
    #       V1      V2      V3      V4      V5      V6      V7      V8      V9     V10
    #1 numeric numeric numeric numeric numeric numeric numeric numeric numeric numeric
    
    data %>%
      mutate(across(where(~all(unique(.[!is.na(.)]) %in% c("0","1"))), as.factor)) %>% 
      dplyr::summarise(across(everything(), class))
    #      V1     V2     V3     V4     V5      V6      V7      V8      V9     V10
    #1 factor factor factor factor factor numeric numeric numeric numeric numeric
    

    一些样本数据:

    data <- setNames(as.data.frame(cbind(replicate(5,sample(c(0,1,NA),10, replace = TRUE)),
                                         replicate(5,runif(10,0,100)))),paste0("V",1:10))
    

    【讨论】:

      【解决方案3】:

      如果你喜欢 tidyverse,那么将 @G.Grothendieck 的 is_binary()mutate_if 结合起来效果很好。

      library(dplyr)
      DF <- data.frame(a = c(0:1, NA), b = 1:3, c = NA, d = 0) # test data frame
      is_binary <- function(x) {
       x0 <- na.omit(x)
       length(unique(x0)) %in% 1:2 && all(x0 %in% 0:1)
      }
      DF %>% mutate_if(is_binary, factor)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-03-23
        • 1970-01-01
        • 2021-01-22
        • 2018-10-07
        • 2013-04-08
        • 2015-07-28
        • 1970-01-01
        相关资源
        最近更新 更多