在R中,根据变量名将各种虚拟/逻辑变量转换为单个分类变量/因子。

9
我的问题与这篇文章这篇文章有很强的相似之处,但我的数据集有点不同,我似乎无法让这些解决方案起作用。如果我误解了什么并且这个问题是多余的,请原谅我。

我的数据集如下所示:

df <- data.frame(
  id = c(1:5),
  conditionA = c(1, NA, NA, NA, 1),
  conditionB = c(NA, 1, NA, NA, NA),
  conditionC = c(NA, NA, 1, NA, NA),
  conditionD = c(NA, NA, NA, 1, NA)
  )
# id conditionA conditionB conditionC conditionD
# 1  1          1         NA         NA         NA
# 2  2         NA          1         NA         NA
# 3  3         NA         NA          1         NA
# 4  4         NA         NA         NA          1
# 5  5          1         NA         NA         NA

注意,除了这些列之外,我还有很多其他的列不应受当前操作的影响。
因此,我观察到条件A、条件B、条件C和条件D是互斥的,最好将它们作为单个类别变量呈现,即“factor”,应该像这样:
#   id       type
# 1  1 conditionA
# 2  2 conditionB
# 3  3 conditionC
# 4  4 conditionD
# 5  5 conditionA

我进行了调查,使用tidyr中的gatherunite方法并不能解决这个问题(使用unite会丢失变量名称的信息)。

我尝试使用第一个提到的答案中建议的kimisc::coalescence.na,但是:1. 需要先为每一列设置基于名称的因子值;2. 它不能按预期工作,只包括第一列:

library(kimisc)
# first, factor each condition with a specific label
df$conditionA <- df$conditionA %>%
  factor(levels = 1, labels = "conditionA")
df$conditionB <- df$conditionB %>%
  factor(levels = 1, labels = "conditionB")
df$conditionC <- df$conditionC %>%
  factor(levels = 1, labels = "conditionC")
df$conditionD <- df$conditionD %>%
  factor(levels = 1, labels = "conditionD")

# now coalesce.na to merge into a single variable
df$type <- coalesce.na(df$conditionA, df$conditionB, df$conditionC, df$conditionD)

df
#   id conditionA conditionB conditionC conditionD       type
# 1  1 conditionA       <NA>       <NA>       <NA> conditionA 
# 2  2       <NA> conditionB       <NA>       <NA>       <NA> 
# 3  3       <NA>       <NA> conditionC       <NA>       <NA> 
# 4  4       <NA>       <NA>       <NA> conditionD       <NA> 
# 5  5 conditionA       <NA>       <NA>       <NA> conditionA

我尝试了第二个问题中的其他建议,但没有找到一个能带给我预期结果的。


将虚拟变量编码为NA/1而不是0/1没有任何优点,至少我不知道有。最近在SO上我看到了很多这样的情况。 - Frank
2
@Frank:实际上,这不是作为虚拟变量(例如用于回归)考虑的:我手动在Excel中输入了所有这些数据,在每次满足条件时放置一个“1”(并且没有费心将其余部分填充为“0”)。我不确定是否应该称之为虚拟变量(但这是我遇到的术语)... - iNyar
3个回答

7

尝试:

library(dplyr)
library(tidyr)

df %>% gather(type, value, -id) %>% na.omit() %>% select(-value) %>% arrange(id)

这将会得到:

#  id       type
#1  1 conditionA
#2  2 conditionB
#3  3 conditionC
#4  4 conditionD
#5  5 conditionA

更新

为了处理您在评论中详细描述的情况,您可以对数据框的所需部分进行操作,然后再使用left_join()连接其他列:

df %>% 
  select(starts_with("condition"), id) %>% 
  gather(type, value, -id) %>% 
  na.omit() %>% 
  select(-value) %>% 
  left_join(., df %>% select(-starts_with("condition"))) %>%
  arrange(id)

太好了!我没有想到可以将gatherna.omit结合起来得到这个结果!现在,我仍然有一个大问题:实际数据集有很多列,其中很多部分为空。 na.omit 将删除这些观测值,即使它们满足条件...(另外,在 gather(type, value, -id) 中,如果我必须排除(-x)70列之类的东西,有没有快速的方法可以做到?) - iNyar
2
@SergeBibauw,像这样的东西对您有用吗:df%>%选择(以“condition”开头的内容,id)%>%收集(type,value,-id)%>% na.omit()%>%选择(-value)%>% left_join(。,df%>%选择(-starts_with(“condition”)))%>%排列(id) - Steven Beaupré
谢谢!我确实意识到我需要仅选择想要的列,应用gather等过程,然后将结果“重新注入”到原始数据框中。 - iNyar

5

您也可以尝试以下方法:

colnames(df)[2:5][max.col(!is.na(df[,2:5]))]
#[1] "conditionA" "conditionB" "conditionC" "conditionD" "conditionA"

上述方法仅在每行中仅有一个列的值不为NA时有效。如果一行中的所有值都是NA,则可以尝试:
mat<-!is.na(df[,2:5])
colnames(df)[2:5][max.col(mat)*(NA^!rowSums(mat))]

1
非常确定这是最有效的。比Stephen在big_df <- rbind(df,df); for (i in 1:18) big_df <- rbind(big_df,big_df)上快4倍。 - Frank
1
它的工作相当不错,除了一种情况:如果一行中的四个条件都是<NA>,那么这段代码会给它其中一个条件(并不总是相同的,我甚至不太明白为什么),而实际上应该将结果值保留为<NA>。有没有办法避免这种情况? - iNyar

1
library(tidyr)
library(dplyr)

df <- df %>%
  gather(type, count, -id)
df <- df[complete.cases(df),][,-3]
df[order(df$id),]
   id       type
1   1 conditionA
7   2 conditionB
13  3 conditionC
19  4 conditionD
5   5 conditionA

1
更 "dplyr" 风格的方法是 df %>% gather(type, value, -id) %>% na.omit() %>% select(-value) %>% arrange(id) - Steven Beaupré

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接