19

原始数据框:

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 毫升
4巴
5 毫升
6 立方米
7个氨基酸
8抗体
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

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

任何人都知道不使用循环的方法吗?

4

7 回答 7

24

在@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(再次)对model.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. 相当明显,但as.data.frame(.)如果你想要一个data.frame.

于 2013-04-24T19:19:10.643 回答
9

插入符号的包中有一个函数可以满足您的要求,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

于 2015-01-17T03:54:39.660 回答
4

刚刚看到一个针对这里的封闭问题,还没有人提到使用这个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=Fdummy.classes

您也可以只使用该dummy()功能将其应用于一列。

于 2017-12-18T15:58:24.283 回答
3

一种相当直接的方法是仅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

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

于 2013-04-25T07:10:29.753 回答
3

我最近遇到了另一种方式。我注意到,当您使用contrastsset to运行任何对比函数时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
于 2016-03-31T21:43:37.400 回答
0

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

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))
于 2013-04-24T20:20:42.693 回答
0
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
于 2020-06-14T04:09:49.507 回答