将虚拟变量转换为分类变量
Transform dummy variable into categorical variable
这是我的数据框
data<-data.frame(
ID=c(1:8),
Diag1=c(1,0,1,0,1,0,1,0),
Diag2=c(0,1,0,1,0,0,1,0),
Diag3=c(0,0,0,1,0,1,1,0),
Multiple.Diag=c(0,0,1,1,0,0,1,0)
)
我有不同诊断的患者,其中一些有多种诊断。这些诊断是虚拟变量,需要转换为分类变量。如果患者 Mult.diag==1
,他的诊断将是 Multiple.diag
,否则他的诊断将是 Diag1
、Diag2
或 Diag3
。如果患者的整个变量为 0,则诊断将是 "Other"
。
这是我想要的:
ID Diagnosis
1 1 Diag1
2 2 Diag2
3 3 Multiple.Diag
4 4 Multiple.Diag
5 5 Diag1
6 6 Diag3
7 7 Multiple.Diag
8 8 Other
这是一个带有 max.col
的选项,用于在 'Diag' 列的子集的每一行中查找具有最高值的列的索引,使用该索引获取列名,然后更改那些元素到 'Multiple.Diag' 其中它是 1,如果一行中没有 1,我们使用由 rowSums
创建的逻辑表达式将其归类为 'Other'
-代码
out <- cbind(data['ID'], Diagnosis = names(data)[-1][
max.col(data[startsWith(names(data), 'Diag')], 'first')])
i1 <- as.logical(data$Multiple.Diag)
out$Diagnosis[i1] <- 'Multiple.Diag'
i2 <- rowSums(data[-1]) == 0
out$Diagnosis[i2] <- 'Other'
-输出
out
# ID Diagnosis
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
max.col
是矢量化的,应该非常快
或者另一种选择是将 which
与 arr.ind = TRUE
一起使用
m1 <- which(data[-1] == 1, arr.ind = TRUE)
cbind(data['ID'], Diagnosis = ifelse(rowSums(data[-1]) == 0,
'Other', names(data)[-1][tapply(m1[, 2], m1[,1], FUN = max)]))
-输出
# ID Diagnosis
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
或者用tidyverse
用同样的方法
library(dplyr)
data %>%
transmute(ID, Diagnosis = case_when(rowSums(.[-1]) == 0 ~ "Other",
TRUE ~ names(.)[-1][max.col(.[-1], 'last')]))
-输出
# ID Diagnosis
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
或者我们使用 rowwise
和 c_across
的另一种选择
data %>%
rowwise %>%
transmute(ID, Diagnosis = coalesce(case_when(as.logical(Multiple.Diag)
~ 'Multiple.Diag',
TRUE ~ names(.)[-1][as.logical(c_across(-1))][1] ), 'Other'))
-输出
# A tibble: 8 x 2
# Rowwise:
# ID Diagnosis
# <int> <chr>
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
您可以像这样使用 apply()
和 built-in 函数。您可以将结果添加到原始 data
或保存在新数据框中。函数 myfunc
根据您提到的方向提取要处理的变量名称。这里的代码:
#Code
myfunc <- function(x)
{
y <- names(x)[max(which(x==1))]
if(is.na(y))
{
y <- 'Others'
}
return(y)
}
#Add var
data$Var <- apply(data[,-1],1,myfunc)
输出:
ID Diag1 Diag2 Diag3 Multiple.Diag Var
1 1 1 0 0 0 Diag1
2 2 0 1 0 0 Diag2
3 3 1 0 0 1 Multiple.Diag
4 4 0 1 1 1 Multiple.Diag
5 5 1 0 0 0 Diag1
6 6 0 0 1 0 Diag3
7 7 1 1 1 1 Multiple.Diag
8 8 0 0 0 0 Others
使用 tidyverse 你还可以这样做:
data %>%
pivot_longer(-ID) %>%
group_by(ID) %>%
slice(which.max(as.integer(factor(name))*value))%>%
mutate(name = if_else(value == 0, 'other',name), value= NULL)
# A tibble: 8 x 2
# Groups: ID [8]
ID name
<int> <chr>
1 1 Diag1
2 2 Diag2
3 3 Multiple.Diag
4 4 Multiple.Diag
5 5 Diag1
6 6 Diag3
7 7 Multiple.Diag
8 8 other
这是我的数据框
data<-data.frame(
ID=c(1:8),
Diag1=c(1,0,1,0,1,0,1,0),
Diag2=c(0,1,0,1,0,0,1,0),
Diag3=c(0,0,0,1,0,1,1,0),
Multiple.Diag=c(0,0,1,1,0,0,1,0)
)
我有不同诊断的患者,其中一些有多种诊断。这些诊断是虚拟变量,需要转换为分类变量。如果患者 Mult.diag==1
,他的诊断将是 Multiple.diag
,否则他的诊断将是 Diag1
、Diag2
或 Diag3
。如果患者的整个变量为 0,则诊断将是 "Other"
。
这是我想要的:
ID Diagnosis
1 1 Diag1
2 2 Diag2
3 3 Multiple.Diag
4 4 Multiple.Diag
5 5 Diag1
6 6 Diag3
7 7 Multiple.Diag
8 8 Other
这是一个带有 max.col
的选项,用于在 'Diag' 列的子集的每一行中查找具有最高值的列的索引,使用该索引获取列名,然后更改那些元素到 'Multiple.Diag' 其中它是 1,如果一行中没有 1,我们使用由 rowSums
-代码
out <- cbind(data['ID'], Diagnosis = names(data)[-1][
max.col(data[startsWith(names(data), 'Diag')], 'first')])
i1 <- as.logical(data$Multiple.Diag)
out$Diagnosis[i1] <- 'Multiple.Diag'
i2 <- rowSums(data[-1]) == 0
out$Diagnosis[i2] <- 'Other'
-输出
out
# ID Diagnosis
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
max.col
是矢量化的,应该非常快
或者另一种选择是将 which
与 arr.ind = TRUE
m1 <- which(data[-1] == 1, arr.ind = TRUE)
cbind(data['ID'], Diagnosis = ifelse(rowSums(data[-1]) == 0,
'Other', names(data)[-1][tapply(m1[, 2], m1[,1], FUN = max)]))
-输出
# ID Diagnosis
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
或者用tidyverse
用同样的方法
library(dplyr)
data %>%
transmute(ID, Diagnosis = case_when(rowSums(.[-1]) == 0 ~ "Other",
TRUE ~ names(.)[-1][max.col(.[-1], 'last')]))
-输出
# ID Diagnosis
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
或者我们使用 rowwise
和 c_across
data %>%
rowwise %>%
transmute(ID, Diagnosis = coalesce(case_when(as.logical(Multiple.Diag)
~ 'Multiple.Diag',
TRUE ~ names(.)[-1][as.logical(c_across(-1))][1] ), 'Other'))
-输出
# A tibble: 8 x 2
# Rowwise:
# ID Diagnosis
# <int> <chr>
#1 1 Diag1
#2 2 Diag2
#3 3 Multiple.Diag
#4 4 Multiple.Diag
#5 5 Diag1
#6 6 Diag3
#7 7 Multiple.Diag
#8 8 Other
您可以像这样使用 apply()
和 built-in 函数。您可以将结果添加到原始 data
或保存在新数据框中。函数 myfunc
根据您提到的方向提取要处理的变量名称。这里的代码:
#Code
myfunc <- function(x)
{
y <- names(x)[max(which(x==1))]
if(is.na(y))
{
y <- 'Others'
}
return(y)
}
#Add var
data$Var <- apply(data[,-1],1,myfunc)
输出:
ID Diag1 Diag2 Diag3 Multiple.Diag Var
1 1 1 0 0 0 Diag1
2 2 0 1 0 0 Diag2
3 3 1 0 0 1 Multiple.Diag
4 4 0 1 1 1 Multiple.Diag
5 5 1 0 0 0 Diag1
6 6 0 0 1 0 Diag3
7 7 1 1 1 1 Multiple.Diag
8 8 0 0 0 0 Others
使用 tidyverse 你还可以这样做:
data %>%
pivot_longer(-ID) %>%
group_by(ID) %>%
slice(which.max(as.integer(factor(name))*value))%>%
mutate(name = if_else(value == 0, 'other',name), value= NULL)
# A tibble: 8 x 2
# Groups: ID [8]
ID name
<int> <chr>
1 1 Diag1
2 2 Diag2
3 3 Multiple.Diag
4 4 Multiple.Diag
5 5 Diag1
6 6 Diag3
7 7 Multiple.Diag
8 8 other