【问题标题】:Recode categorical factor with N categories into N binary columns将具有 N 个类别的分类因子重新编码为 N 个二进制列
【发布时间】:2013-04-18 11:47:17
【问题描述】:

原始数据框:

v1 = sample(letters[1:3], 10, replace=TRUE)
v2 = sample(letters[1:3], 10, replace=TRUE)
df = data.frame(v1,v2)
df
v1 v2 1公元前 2个 3 cc 4个 5 摄氏度 6 c b 7个 8 a b 9个 10个

新数据框:

new_df = data.frame(row.names=rownames(df))
for (i in colnames(df)) {
    for (x in letters[1:3]) {
        #new_df[x] = as.numeric(df[i] == x)
        new_df[paste0(i, "_", x)] = as.numeric(df[i] == x)
    }
}
v1_a v1_b v1_c v2_a v2_b v2_c 1 0 1 0 0 0 1 2 1 0 0 1 0 0 3 0 0 1 0 0 1 4 0 1 0 1 0 0 5 0 0 1 0 0 1 6 0 0 1 0 1 0 7 1 0 0 1 0 0 8 1 0 0 0 1 0 9 1 0 0 0 0 1 10 1 0 0 0 1 0

对于小型数据集,这很好,但对于更大的数据集,它会变得很慢。

有人知道不使用循环的方法吗?

【问题讨论】:

  • 您的第一个数据框有两个变量,但看起来您只转换了第二个。你能澄清一下吗?
  • 您正在覆盖您的数据。它的输出应该有 6 列。
  • 对不起,这是我的错误——我在上面的代码中修复了它。在上面的示例中,每个原始列应该有三个新列。感谢您接听!
  • @Keith,你检查过已经发布的答案了吗?
  • @Arun 完成。有很多有用的解决方案。感谢大家的意见!

标签: r dataframe factors


【解决方案1】:

在@AnandaMahto 的搜索功能的帮助下会更好,

model.matrix(~ . + 0, data=df, contrasts.arg = lapply(df, contrasts, contrasts=FALSE))
#    v1a v1b v1c v2a v2b v2c
# 1    0   1   0   0   0   1
# 2    1   0   0   1   0   0
# 3    0   0   1   0   0   1
# 4    0   1   0   1   0   0
# 5    0   0   1   0   0   1
# 6    0   0   1   0   1   0
# 7    1   0   0   1   0   0
# 8    1   0   0   0   1   0
# 9    1   0   0   0   0   1
# 10   1   0   0   0   1   0

我想这就是你要找的。如果不是这样,我很乐意删除。感谢@G.Grothendieck(再次)提供excellent usagemodel.matrix

cbind(with(df, model.matrix(~ v1 + 0)), with(df, model.matrix(~ v2 + 0)))
#    v1a v1b v1c v2a v2b v2c
# 1    0   1   0   0   0   1
# 2    1   0   0   1   0   0
# 3    0   0   1   0   0   1
# 4    0   1   0   1   0   0
# 5    0   0   1   0   0   1
# 6    0   0   1   0   1   0
# 7    1   0   0   1   0   0
# 8    1   0   0   0   1   0
# 9    1   0   0   0   0   1
# 10   1   0   0   0   1   0

注意:您的输出只是:

with(df, model.matrix(~ v2 + 0))

注 2:这给出了 matrix。相当明显,但如果你想要data.frame,还是用as.data.frame(.) 包裹它。

【讨论】:

    【解决方案2】:

    插入符号的包中有一个函数可以满足您的要求,dummyVars。 这是取自作者文档的用法示例: http://topepo.github.io/caret/preprocess.html

    library(earth)
    data(etitanic)
    
    dummies <- caret::dummyVars(survived ~ ., data = etitanic)
    head(predict(dummies, newdata = etitanic))
    
      pclass.1st pclass.2nd pclass.3rd sex.female sex.male     age sibsp parch
    1          1          0          0          1        0 29.0000     0     0
    2          1          0          0          0        1  0.9167     1     2
    3          1          0          0          1        0  2.0000     1     2
    4          1          0          0          0        1 30.0000     1     2
    5          1          0          0          1        0 25.0000     1     2
    6          1          0          0          0        1 48.0000     0     0
    

    model.matrix 选项在您有稀疏数据并想使用 Matrix::sparse.model.matrix

    的情况下很有用

    【讨论】:

      【解决方案3】:

      刚刚看到一个针对此处的已关闭问题,还没有人提到使用dummies 包:

      您可以使用dummy.data.frame() 函数重新编码您的变量,该函数建立在model.matrix() 之上,但语法更简单,有一些不错的选项,并且会返回一个数据框:

      > dummy.data.frame(df, sep="_")
         v1_a v1_b v1_c v2_a v2_b v2_c
      1     0    1    0    0    0    1
      2     1    0    0    1    0    0
      3     0    0    1    0    0    1
      4     0    1    0    1    0    0
      5     0    0    1    0    0    1
      6     0    0    1    0    1    0
      7     1    0    0    1    0    0
      8     1    0    0    0    1    0
      9     1    0    0    0    0    1
      10    1    0    0    0    1    0
      

      此函数的一些不错的方面是您可以轻松地为新名称指定分隔符 (sep=),省略非编码变量 (all=F) 并带有自己的选项 dummy.classes,允许您指定哪个列的类别应该被编码。

      您也可以只使用dummy() 函数将其应用于一列。

      【讨论】:

        【解决方案4】:

        一种相当直接的方法是在每一列上使用table,将列中的值按data.frame 中的行数制成表格:

        allLevels <- levels(factor(unlist(df)))
        do.call(cbind, 
                lapply(df, function(x) table(sequence(nrow(df)), 
                                             factor(x, levels = allLevels))))
        #    a b c a b c
        # 1  0 1 0 0 0 1
        # 2  1 0 0 1 0 0
        # 3  0 0 1 0 0 1
        # 4  0 1 0 1 0 0
        # 5  0 0 1 0 0 1
        # 6  0 0 1 0 1 0
        # 7  1 0 0 1 0 0
        # 8  1 0 0 0 1 0
        # 9  1 0 0 0 0 1
        # 10 1 0 0 0 1 0
        

        我在“x”上使用了factor,以确保即使在列中没有“c”值的情况下,输出中仍然会有一个“c”列,已填充用零。

        【讨论】:

          【解决方案5】:

          我最近遇到了另一种方式。我注意到,当您运行任何将contrasts 设置为FALSE 的对比函数时,它会为您提供一种热编码。例如,contr.sum(5, contrasts = FALSE) 给出

            1 2 3 4 5
          1 1 0 0 0 0
          2 0 1 0 0 0
          3 0 0 1 0 0
          4 0 0 0 1 0
          5 0 0 0 0 1
          

          要为所有因素获得此行为,您可以创建一个新的对比函数并将其设置为默认值。例如,

          contr.onehot = function (n, contrasts, sparse = FALSE) {
            contr.sum(n = n, contrasts = FALSE, sparse = sparse)
          }
          
          options(contrasts = c("contr.onehot", "contr.onehot"))
          model.matrix(~ . - 1, data = df)
          

          这会导致

             v1a v1b v1c v2a v2b v2c
          1    0   0   1   0   0   1
          2    0   1   0   1   0   0
          3    0   0   1   0   1   0
          4    1   0   0   0   1   0
          5    0   1   0   0   1   0
          6    0   1   0   0   0   1
          7    1   0   0   0   1   0
          8    0   1   0   0   1   0
          9    0   1   0   1   0   0
          10   0   0   1   0   0   1
          

          【讨论】:

            【解决方案6】:

            这是一个更一般情况的解决方案,当字母数量没有先验指定时:

            convertABC <- function(x) {
            
                hold <- rep(0,max(match(as.matrix(df),letters))) # pre-format output
            
                codify <- function(x) {                          # define function for single char
            
                    output <- hold                               # take empty vector
                    output[match(x,letters)] <- 1                # place 1 according to letter pos
                    return(output)
                }
            
                to.return <- t(sapply(as.character(x),codify))   # apply it to whole vector
                rownames(to.return) <- 1:nrow(to.return)         # nice rownames
                colnames(to.return) <- do.call(c,list(letters[1:max(match(as.matrix(df),letters))])) # nice columnnames
                return(to.return)
            }
            

            此函数接受一个字符向量,并将其重新编码为二进制值。处理df中的所有变量:

            do.call(cbind,lapply(df,convertABC))
            

            【讨论】:

              【解决方案7】:
              library(correlationfunnel)
              library(dplyr)
              v1 = sample(letters[1:3], 10, replace=TRUE)
              v2 = sample(letters[1:3], 10, replace=TRUE)
              df = data.frame(v1,v2)
              df
              
                 v1 v2
              1   b  c
              2   c  c
              3   c  a
              4   c  c
              5   a  a
              6   b  b
              7   b  c
              8   b  c
              9   c  a
              10  b  c
              
              df$id= 1:nrow(df)
              df %>%
                 select(-id) %>%
                 binarize()
              
              # A tibble: 10 x 6
                 v1__a v1__b v1__c v2__a v2__b v2__c
                 <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
               1     0     1     0     0     0     1
               2     0     0     1     0     0     1
               3     0     0     1     1     0     0
               4     0     0     1     0     0     1
               5     1     0     0     1     0     0
               6     0     1     0     0     1     0
               7     0     1     0     0     0     1
               8     0     1     0     0     0     1
               9     0     0     1     1     0     0
              10     0     1     0     0     0     1
              

              【讨论】:

                猜你喜欢
                • 2022-01-11
                • 2017-03-23
                • 2018-09-30
                • 1970-01-01
                • 2018-03-11
                • 2013-04-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多