【问题标题】:Create new dummy variable columns from categorical variable从分类变量创建新的虚拟变量列
【发布时间】:2011-03-24 00:09:48
【问题描述】:

我有几个数据集,包含 75,000 个观察值和一个 type 变量,其值可以为 0-4。我想为所有类型的每个数据集添加五个新的虚拟变量。我能想到的最佳方法如下:

# For the 'binom' data set create dummy variables for all types in all data sets
binom.dummy.list<-list()
for(i in 0:4){
    binom.dummy.list[[i+1]]<-sapply(binom$type,function(t) ifelse(t==i,1,0))
}

# Add and merge data
binom.dummy.df<-as.data.frame(do.call("cbind",binom.dummy.list))
binom.dummy.df<-transform(binom.dummy.df,id=1:nrow(binom))
binom<-merge(binom,binom.dummy.df,by="id")

虽然这可行,但速度非常慢(合并功能甚至崩溃了几次)。有没有更有效的方法来做到这一点?也许这个功能是我不熟悉的包的一部分?

【问题讨论】:

    标签: r


    【解决方案1】:

    model.matrix() 函数我运气不佳,因为无论出于何种原因,它都忽略了一些因子水平。不过,library(fastDummies) 提供的这个简单功能让我很幸运:

    转换为二进制虚拟变量的列必须是分类的。

    fastDummies::dummy_cols(fastDummies_example, select_columns = "numbers", remove_selected_columns = "numbers")

    【讨论】:

      【解决方案2】:

      如果您愿意使用 data.table 包,mltools 有一个 one_hot() 方法。

      library(data.table)
      library(mltools)
      
      binom <- data.table(y=runif(1e5), x=runif(1e5), catVar=as.factor(sample(0:4,1e5,TRUE)))
      one_hot(binom)
      
                       y          x catVar_0 catVar_1 catVar_2 catVar_3 catVar_4
           1: 0.90511891 0.83045050        0        0        1        0        0
           2: 0.91375984 0.73273830        0        0        0        1        0
           3: 0.01926608 0.10301409        0        0        1        0        0
           4: 0.48691138 0.24428157        0        1        0        0        0
           5: 0.60660396 0.09132816        0        0        1        0        0
          ---                                                                   
       99996: 0.12908356 0.26157731        0        1        0        0        0
       99997: 0.96397273 0.98959000        0        1        0        0        0
       99998: 0.16818414 0.37460941        1        0        0        0        0
       99999: 0.72610508 0.72055867        1        0        0        0        0
      100000: 0.89710998 0.24155507        0        0        0        0        1
      

      用法

      one_hot(dt, cols = "auto", sparsifyNAs = FALSE, 
              naCols = FALSE, dropCols = TRUE,
              dropUnusedLevels = FALSE)
      

      应该对哪些列进行单热编码? cols = "auto" 对所有无序因子列进行编码。因此,下面的命令是等效的。这仅在 data.table 包含不应编码的因素时才重要。

      one_hot(binom, cols="catVar")
      

      【讨论】:

        【解决方案3】:

        你可以使用名为dummies的包

        binom <- data.frame(y=runif(1e5), x=runif(1e5), catVar=as.factor(sample(0:4,1e5,TRUE)))
        head(binom)
        
                  y          x catVar
        1 0.4143348 0.09721401      1
        2 0.3140782 0.54340539      3
        3 0.1262037 0.51820499      2
        4 0.7159850 0.13167720      3
        5 0.8203528 0.94116026      3
        6 0.2169781 0.82020216      1
        

        解决方案:

        library(dummies)
        binom<-dummy.data.frame(binom)
        head(binom)
        
                  y          x catVar0 catVar1 catVar2 catVar3 catVar4
        1 0.4143348 0.09721401       0       1       0       0       0
        2 0.3140782 0.54340539       0       0       0       1       0
        3 0.1262037 0.51820499       0       0       1       0       0
        4 0.7159850 0.13167720       0       0       0       1       0
        5 0.8203528 0.94116026       0       0       0       1       0
        6 0.2169781 0.82020216       0       1       0       0       0
        

        【讨论】:

          【解决方案4】:

          单层神经网络(不理解因子)的nnet包有一个转换命令:class.ind。

          【讨论】:

            【解决方案5】:

            recipes 包也可以非常强大地做到这一点。下面的示例非常冗长,但只要您添加更多预处理步骤,它就会变得非常干净。

            library(recipes)
            
            binom <- data.frame(y = runif(1e5), 
                                x = runif(1e5),
                                catVar = as.factor(sample(0:4, 1e5, TRUE))) # use the example from gappy
            head(binom)
            
            new_data <- recipe(y ~ ., data = binom) %>% 
              step_dummy(catVar) %>% # add dummy variable
              prep(training = binom) %>% # apply the preprocessing steps (could be more than just adding dummy variables)
              bake(newdata = binom) # apply the recipe to new data
            head(new_data)
            

            其他步骤示例有 step_scale、step_center、step_pca 等。

            【讨论】:

              【解决方案6】:

              R 有一种“子语言”可以将公式转换为设计矩阵,并且本着语言的精神,您可以利用它。它快速而简洁。示例:您有一个基数预测变量 x、一个分类预测变量 catVar 和一个响应 y。

              > binom <- data.frame(y=runif(1e5), x=runif(1e5), catVar=as.factor(sample(0:4,1e5,TRUE)))
              > head(binom)
                        y          x catVar
              1 0.5051653 0.34888390      2
              2 0.4868774 0.85005067      2
              3 0.3324482 0.58467798      2
              4 0.2966733 0.05510749      3
              5 0.5695851 0.96237936      1
              6 0.8358417 0.06367418      2
              

              你只是这样做

              > A <- model.matrix(y ~ x + catVar,binom) 
              > head(A)
                (Intercept)          x catVar1 catVar2 catVar3 catVar4
              1           1 0.34888390       0       1       0       0
              2           1 0.85005067       0       1       0       0
              3           1 0.58467798       0       1       0       0
              4           1 0.05510749       0       0       1       0
              5           1 0.96237936       1       0       0       0
              6           1 0.06367418       0       1       0       0
              

              完成。

              【讨论】:

              • 反方向的任何简单方法 - 即您有虚拟变量但想将它们折叠成一个变量?
              • 请注意,如果您更改所使用的对比类型,您将获得不同的结果。此外,对于有序的无序因子,您会得到不同的答案。 R 中设置的默认对比度是options(contrasts = c("contr.treatment", "contr.poly"))。请参阅?contrasts 以增加您的困惑。
              • 还要注意这里的例子有5个分类,因为索引从0sample(0:4, 1e5 , TRUE)开始。我认为在基础 R 中不可能自动生成所有级别的虚拟变量。这个特定示例恰好省略了任何 0 样本,这将在模型矩阵中显示为一行零。
              • 这种方法会丢弃带有 NA 的行,这让我更喜欢 Joshua Ullrich 的回答。为了澄清geneorama的观点,对于n个变量级别,您只需要n-1个虚拟变量来表示信息。 (如果出于某种原因您想破解 model.matrix() 以显式表示所有列,您可以添加一个没有成员的引用级别,如 levels(binom$catVar) &lt;- c("dummy", levels(binom$catVar)); A &lt;- model.matrix(y ~ x + catVar,binom, contrasts = "contr.treatment") 但如果您正在进行建模,这种冗余似乎有风险。)
              • 如果您不想拦截,请使用A &lt;- model.matrix(y ~ x + catVar -1, binom)
              【解决方案7】:

              使用 model.matrix() 怎么样?

              > binom <- data.frame(data=runif(1e5),type=sample(0:4,1e5,TRUE))
              > head(binom)
                     data type
              1 0.1412164    2
              2 0.8764588    2
              3 0.5559061    4
              4 0.3890109    3
              5 0.8725753    3
              6 0.8358100    1
              > inds <- model.matrix(~ factor(binom$type) - 1)
              > head(inds)
                factor(binom$type)0 factor(binom$type)1 factor(binom$type)2 factor(binom$type)3 factor(binom$type)4
              1                   0                   0                   1                   0                   0
              2                   0                   0                   1                   0                   0
              3                   0                   0                   0                   0                   1
              4                   0                   0                   0                   1                   0
              5                   0                   0                   0                   1                   0
              6                   0                   1                   0                   0                   0
              

              【讨论】:

              • 如果类型变量也有 NA 值怎么办?除了 1 和 0,我们如何将 NA 值作为单独的变量保留?
              • 将 NA 值编码为 NA 以外的值。
              【解决方案8】:

              ifelse 是矢量化的,所以如果我正确理解您的代码,您就不需要sapply。而且我不会使用合并 - 我会使用 SQLite 或 PostgreSQL。

              一些示例数据也会有所帮助:-)

              【讨论】:

                【解决方案9】:

                Drew,这要快得多,不会导致任何崩溃。

                > binom <- data.frame(data=runif(1e5),type=sample(0:4,1e5,TRUE))
                > for(t in unique(binom$type)) {
                +   binom[paste("type",t,sep="")] <- ifelse(binom$type==t,1,0)
                + }
                > head(binom)
                        data type type2 type4 type1 type3 type0
                1 0.11787309    2     1     0     0     0     0
                2 0.11884046    4     0     1     0     0     0
                3 0.92234950    4     0     1     0     0     0
                4 0.44759259    1     0     0     1     0     0
                5 0.01669651    2     1     0     0     0     0
                6 0.33966184    3     0     0     0     1     0
                

                【讨论】:

                • 不错的解决方案。我可以建议,在“粘贴”之前包含一点“make.names”,以防关卡名称包含一些诉讼字符。
                猜你喜欢
                • 1970-01-01
                • 2013-04-14
                • 2020-05-13
                • 2015-08-11
                • 2017-05-31
                • 1970-01-01
                • 2015-08-17
                • 2019-05-05
                • 2020-06-22
                相关资源
                最近更新 更多