从假人中最简单地创建因子变量

时间:2017-01-16 06:02:14

标签: r dplyr

这里问题的选定答案:

creating a factor variable with dplyr?

没有给Hadley留下深刻的印象,后续的回答并不能说明我遇到的一些问题。我想知道社区是否可以通过一个更简单的例子做得更好:

  ### DATA ###
A = round(runif(200,0,1),0)
B = c(1 - A[1:100],rep(0,100))
C = c(rep(0,100), 1 - A[101:200])

dummies <- as.data.frame(cbind(A,B,C))
header <- c("Christian", "Muslim", "Athiest")

names(dummies) <- header

### ONE WAY ###
dummies$Religion <- factor(ifelse(dummies$Christian==1, "Christian",
                            ifelse(dummies$Muslim==1, "Muslim",
                                   ifelse(dummies$Athiest==1, "Athiest", NA))))

解决方案模仿上面链接中提供给OP的结果。是否有一个更简单的函数将虚拟变量折叠为一个因子变量,比如说STATA中的egen组函数?简单的一个班轮就会很棒。

使用Akrun的解决方案和系统时间(谢谢):

set.seed(24)
A = round(runif(2e6,0,1),0)
B = c(1 - A[1:1e6],rep(0,1e6))
C = c(rep(0,1e6), 1 - A[1000001:2000000])

dummies <- as.data.frame(cbind(A,B,C))
header <- c("Christian", "Muslim", "Athiest")

names(dummies) <- header
attach(dummies)



#Alistaire
system.time({
  dummies %>% rowwise() %>% 
    transmute(religion = names(.)[as.logical(c(Christian, Muslim, Athiest))])
})
# user  system elapsed 
# 56.08    0.00   56.08 

system.time({
  dummies %>% transmute(religion = case_when(
    as.logical(Christian) ~ 'Christian', 
    as.logical(Muslim) ~ 'Muslim', 
    as.logical(Athiest) ~ 'Atheist'))
})
# user  system elapsed 
# 0.22    0.04    0.27 



#Curt F.
system.time({
  dummies %>% 
    gather(religion, is_valid) %>% 
    filter(is_valid == T) %>%
    select(-is_valid)
})
# user  system elapsed 
# 0.33    0.03    0.36 




#Akrun
system.time({
  names(dummies)[as.matrix(dummies)%*% seq_along(dummies)]
})
# user  system elapsed 
# 0.13    0.06    0.21 

system.time({
  names(dummies)[max.col(dummies, "first")]
})
# user  system elapsed 
# 0.04    0.07    0.11 

我发现Akrun的解决方案是最快的方法,并提供了2个单行。但是,非常感谢其他人对问题的独特解决方法以及我希望了解更多的编码方法,特别是使用%%names(.)is_valid和qdapTools包。

4 个答案:

答案 0 :(得分:4)

使用dplyr的快捷方法是

dummies %>% rowwise() %>% 
    transmute(religion = names(.)[as.logical(c(Christian, Muslim, Athiest))])

Hadley在答案中真正抱怨的是嵌套ifelse结构。他建造case_when来取代它:

dummies %>% transmute(religion = case_when(
    as.logical(Christian) ~ 'Christian', 
    as.logical(Muslim) ~ 'Muslim', 
    as.logical(Athiest) ~ 'Atheist'))

答案 1 :(得分:2)

我们可以使用

dummies$Religion <- names(dummies)[as.matrix(dummies)%*% seq_along(dummies)]

max.col

dummies$Religion <- names(dummies)[max.col(dummies, "first")]

如果有的行只有0个元素,那么

dummies$Religion <- names(dummies)[max.col(dummies, "first")*NA^(!rowSums(dummies))]

注意:在上述所有解决方案中,它都可以用factor包裹。但是,最好将其保持为character

注意2:两种解决方案都是base R单行解决方案,并且与任何包解决方案相比都非常快(证据显示在下面的基准测试中)

基准

set.seed(24)
A = round(runif(2e6,0,1),0)
B = c(1 - A[1:1e6],rep(0,1e6))


C = c(rep(0,1e6), 1 - A[1000001:2000000])

dummies <- data.frame(A,B,C)
colnames(dummies) <- c("Christian", "Muslim", "Athiest")

system.time({
dummies %>% rowwise() %>% 
    transmute(religion = names(.)[as.logical(c(Christian, Muslim, Athiest))])
})
#  user  system elapsed 
#  49.13    0.06   49.55 

system.time({
dummies %>% transmute(religion = case_when(
    as.logical(Christian) ~ 'Christian', 
    as.logical(Muslim) ~ 'Muslim', 
    as.logical(Athiest) ~ 'Atheist'))
  })
#Error in mutate_impl(.data, dots) : object 'Christian' not found
#Timing stopped at: 0 0 0 

system.time({
names(dummies)[as.matrix(dummies)%*% seq_along(dummies)]
})
#  user  system elapsed 
#   0.11    0.01    0.13 

system.time({
names(dummies)[max.col(dummies, "first")]
})
# user  system elapsed 
#   0.07    0.02    0.08 

答案 2 :(得分:2)

执行此操作的一种方法是合并tidyrdplyr。这可能无法提供最快的性能(我还没有检查过),但对我来说至少它提供了最容易理解的代码。

从OP开始使用dummies数据框:

A = round(runif(200,0,1),0)
B = c(1 - A[1:100],rep(0,100))
C = c(rep(0,100), 1 - A[101:200])

dummies <- as.data.frame(cbind(A, B, C))
header <- c("Christian", "Muslim", "Atheist")
names(dummies) <- header

然后来自gather()的{​​{1}}函数执行繁重的工作,tidyr的{​​{1}}和filter()执行清理工作。

select()

关于这个版本的好处是它没有对初始数据帧的热度作出任何假设。如果初始帧中的某些行既是无神论者又是基督徒,那么您的输出将有两行。

答案 3 :(得分:1)

如果OP的主要目的是创建Religion列,则可以直接在一次调用中完成:

Religion <- sample(c("Christian", "Muslim", "Atheist"), 200, replace = TRUE, 
                   prob = c(60, 20, 20))

参数prob可用于指定概率权重。只是为了检查:

table(Religion)
#Religion
#  Atheist Christian    Muslim 
#       37       115        48 

但是,如果出于某种原因需要dummies data.frame,可以使用以下代码从Religion向量创建它:

mat <- sapply(unique(Religion), function(x) as.integer(Religion == x))
dummies <- cbind(as.data.frame(mat), Religion)

这将导致:

head(dummies)
#  Muslim Christian Atheist  Religion
#1      1         0       0    Muslim
#2      1         0       0    Muslim
#3      0         1       0 Christian
#4      1         0       0    Muslim
#5      0         1       0 Christian
#6      0         0       1   Atheist

请注意,由于我们在调用sample()之前未使用set.seed(),因此sample()的不同运行结果可能会有所不同。

this answer开始,我了解了来自mtabulate()的{​​{1}}函数,它可以用一行代替qdapTools构造:

sapply()