将数字变量重新编码为有意义的因子变量的过程自动化
Automating the process of recoding numeric variables to meaningful factor variables
我有一个大数据框(数百个变量宽),其中所有分类变量的值都保存为数字,例如,1、2、8,代表否、是和未知。
然而,这并不总是一致的。有十个或更多类别的变量,其中 88 个表示未知等
data <- data.frame("ID" = c(1:5),
"Var1" = c(2,2,8,1,8),
"Var2" = c(5,8,4,88,10))
对于每个变量,我都有关于哪个值代表哪个类别的所有信息。目前,我将此信息存储在每个正确排序的向量中,例如
> Var1_values
[1] 8 2 1
对应的向量包含以下类别:
> Var1_categories
[1] "unknown" "yes" "no"
但我无法弄清楚如何将这些信息整合在一起,以便将重新编码过程自动化到预期结果,例如
| ID | Var1 | Var2 |
|----|---------|-------------------|
| 1 | yes | condition E |
| 2 | yes | condition H |
| 3 | unknown | condition D |
| 4 | no | unknown condition |
| 5 | unknown | condition H |
其中每一列都是一个有意义的因子变量。
正如我所说,数据框非常宽,内部可能会发生变化,因此不能手动执行此操作。我觉得自己很愚蠢,因为我拥有现成的所有必要信息,所以任何见解都将不胜感激,一杯咖啡是我至少可以提供的有用建议。
// 编辑:
我忘了说我已经制作了某种映射数据框,但我还不能真正使用它。它看起来像这样:
mapping <- data.frame("Variable" = c("Var1", "Var2", "Var3", "Var4"),
"Value1" = c(2,2,2,7),
"Word1" = c("yes","yes","yes","condition A"),
"Value2" = c(1,1,1,6),
"Word2" = c("no","no","no","Condition B"),
"Value3" = c(8,8,8,5),
"Word3" = c("unk","unk","unk", "Condition C"),
"Value4" = c(NA,NA,NA,4),
"Word4" = c(NA,NA,NA,"Condition B")
)
我想对它进行“长”转换,以便我可以将它与@r2evan 的解决方案一起使用。
这是一个想法,尽管它需要重塑(两次)数据。
mapping <- data.frame(
Var = c(rep("Var1", 3), rep("Var2", 5)),
Val = c(1, 2, 8, 4, 5, 8, 10, 88),
Words = c("no", "yes", "unk", "D", "E", "H", "H", "unk")
)
mapping
# Var Val Words
# 1 Var1 1 no
# 2 Var1 2 yes
# 3 Var1 8 unk
# 4 Var2 4 D
# 5 Var2 5 E
# 6 Var2 8 H
# 7 Var2 10 H
# 8 Var2 88 unk
library(dplyr)
library(tidyr) # pivot_*
data %>%
pivot_longer(-ID, names_to = "Var", values_to = "Val") %>%
left_join(mapping, by = c("Var", "Val")) %>%
pivot_wider(ID, names_from = "Var", values_from = "Words")
# # A tibble: 5 x 3
# ID Var1 Var2
# <int> <chr> <chr>
# 1 1 yes E
# 2 2 yes H
# 3 3 unk D
# 4 4 no unk
# 5 5 unk H
使用此方法,您可以控制每个变量的数字到单词的映射。
另一种选择是使用地图列表,与上面类似,但不需要双重整形。
maplist <- list(
Var1 = c("1" = "no", "2" = "yes", "8" = "unk"),
Var2 = c("4" = "D", "5" = "E", "8" = "H", "10" = "H", "88" = "unk")
)
maplist
# $Var1
# 1 2 8
# "no" "yes" "unk"
# $Var2
# 4 5 8 10 88
# "D" "E" "H" "H" "unk"
nms <- c("Var1", "Var2")
data[,nms] <- Map(function(val, lookup) lookup[as.character(val)],
data[nms], maplist[nms])
data
# ID Var1 Var2
# 1 1 yes E
# 2 2 yes H
# 3 3 unk D
# 4 4 no unk
# 5 5 unk H
在这两者之间,如果您的数据不会因为重塑它而惩罚您,我想我更喜欢第一个(很多事情可能会降低它的吸引力)。它很好的一个原因是维护 mapping
可以像维护 CSV 一样简单(这可以在您最喜欢的电子表格工具中完成,例如 Excel 或 Calc)。
这是一种不需要重新调整原始数据的方法,并且可以想象地应用于任意数量的列。首先,将所有现有的“值”和“类别”向量放入列表中,将所有内容格式化为字符:
library(tidyverse)
# Recreating your existing vectors
Var1_values <- c(8, 2, 1)
Var2_values <- c(88, 10, 8, 5, 4)
Var1_categories <- c("unknown", "yes", "no")
Var2_categories <- c("unknown condition", "condition H", "condition H", "condition E", "condition D")
Var_values <- list(Var1_values, Var2_values) %>%
map(as.character)
Var_categories <- list(Var1_categories, Var2_categories)
使用 Var_values
为 Var_categories
中每个向量的每个元素添加名称,并获取要从数据集中重新编码的变量名称列表:
for (i in 1:length(Var_categories)) {
names(Var_categories[[i]]) <- Var_values[[i]]
}
vars_names <- str_subset(colnames(data), "Var")
然后,使用 map2
重新编码所有目标变量,然后转换为带有 ID 列的 tibble。
data_recoded <- map2(vars_names, Var_categories, ~ dplyr::recode(unlist(data[.x], use.names = F), !!!.y)) %>%
as_tibble(.name_repair = ~ vars_names) %>%
add_column(ID = 1:5, .before = "Var1")
输出(data_recoded
):
ID Var1 Var2
<int> <chr> <chr>
1 1 yes condition E
2 2 yes condition H
3 3 unknown condition D
4 4 no unknown condition
5 5 unknown condition H
我有一个大数据框(数百个变量宽),其中所有分类变量的值都保存为数字,例如,1、2、8,代表否、是和未知。 然而,这并不总是一致的。有十个或更多类别的变量,其中 88 个表示未知等
data <- data.frame("ID" = c(1:5),
"Var1" = c(2,2,8,1,8),
"Var2" = c(5,8,4,88,10))
对于每个变量,我都有关于哪个值代表哪个类别的所有信息。目前,我将此信息存储在每个正确排序的向量中,例如
> Var1_values
[1] 8 2 1
对应的向量包含以下类别:
> Var1_categories
[1] "unknown" "yes" "no"
但我无法弄清楚如何将这些信息整合在一起,以便将重新编码过程自动化到预期结果,例如
| ID | Var1 | Var2 |
|----|---------|-------------------|
| 1 | yes | condition E |
| 2 | yes | condition H |
| 3 | unknown | condition D |
| 4 | no | unknown condition |
| 5 | unknown | condition H |
其中每一列都是一个有意义的因子变量。
正如我所说,数据框非常宽,内部可能会发生变化,因此不能手动执行此操作。我觉得自己很愚蠢,因为我拥有现成的所有必要信息,所以任何见解都将不胜感激,一杯咖啡是我至少可以提供的有用建议。
// 编辑:
我忘了说我已经制作了某种映射数据框,但我还不能真正使用它。它看起来像这样:
mapping <- data.frame("Variable" = c("Var1", "Var2", "Var3", "Var4"),
"Value1" = c(2,2,2,7),
"Word1" = c("yes","yes","yes","condition A"),
"Value2" = c(1,1,1,6),
"Word2" = c("no","no","no","Condition B"),
"Value3" = c(8,8,8,5),
"Word3" = c("unk","unk","unk", "Condition C"),
"Value4" = c(NA,NA,NA,4),
"Word4" = c(NA,NA,NA,"Condition B")
)
我想对它进行“长”转换,以便我可以将它与@r2evan 的解决方案一起使用。
这是一个想法,尽管它需要重塑(两次)数据。
mapping <- data.frame(
Var = c(rep("Var1", 3), rep("Var2", 5)),
Val = c(1, 2, 8, 4, 5, 8, 10, 88),
Words = c("no", "yes", "unk", "D", "E", "H", "H", "unk")
)
mapping
# Var Val Words
# 1 Var1 1 no
# 2 Var1 2 yes
# 3 Var1 8 unk
# 4 Var2 4 D
# 5 Var2 5 E
# 6 Var2 8 H
# 7 Var2 10 H
# 8 Var2 88 unk
library(dplyr)
library(tidyr) # pivot_*
data %>%
pivot_longer(-ID, names_to = "Var", values_to = "Val") %>%
left_join(mapping, by = c("Var", "Val")) %>%
pivot_wider(ID, names_from = "Var", values_from = "Words")
# # A tibble: 5 x 3
# ID Var1 Var2
# <int> <chr> <chr>
# 1 1 yes E
# 2 2 yes H
# 3 3 unk D
# 4 4 no unk
# 5 5 unk H
使用此方法,您可以控制每个变量的数字到单词的映射。
另一种选择是使用地图列表,与上面类似,但不需要双重整形。
maplist <- list(
Var1 = c("1" = "no", "2" = "yes", "8" = "unk"),
Var2 = c("4" = "D", "5" = "E", "8" = "H", "10" = "H", "88" = "unk")
)
maplist
# $Var1
# 1 2 8
# "no" "yes" "unk"
# $Var2
# 4 5 8 10 88
# "D" "E" "H" "H" "unk"
nms <- c("Var1", "Var2")
data[,nms] <- Map(function(val, lookup) lookup[as.character(val)],
data[nms], maplist[nms])
data
# ID Var1 Var2
# 1 1 yes E
# 2 2 yes H
# 3 3 unk D
# 4 4 no unk
# 5 5 unk H
在这两者之间,如果您的数据不会因为重塑它而惩罚您,我想我更喜欢第一个(很多事情可能会降低它的吸引力)。它很好的一个原因是维护 mapping
可以像维护 CSV 一样简单(这可以在您最喜欢的电子表格工具中完成,例如 Excel 或 Calc)。
这是一种不需要重新调整原始数据的方法,并且可以想象地应用于任意数量的列。首先,将所有现有的“值”和“类别”向量放入列表中,将所有内容格式化为字符:
library(tidyverse)
# Recreating your existing vectors
Var1_values <- c(8, 2, 1)
Var2_values <- c(88, 10, 8, 5, 4)
Var1_categories <- c("unknown", "yes", "no")
Var2_categories <- c("unknown condition", "condition H", "condition H", "condition E", "condition D")
Var_values <- list(Var1_values, Var2_values) %>%
map(as.character)
Var_categories <- list(Var1_categories, Var2_categories)
使用 Var_values
为 Var_categories
中每个向量的每个元素添加名称,并获取要从数据集中重新编码的变量名称列表:
for (i in 1:length(Var_categories)) {
names(Var_categories[[i]]) <- Var_values[[i]]
}
vars_names <- str_subset(colnames(data), "Var")
然后,使用 map2
重新编码所有目标变量,然后转换为带有 ID 列的 tibble。
data_recoded <- map2(vars_names, Var_categories, ~ dplyr::recode(unlist(data[.x], use.names = F), !!!.y)) %>%
as_tibble(.name_repair = ~ vars_names) %>%
add_column(ID = 1:5, .before = "Var1")
输出(data_recoded
):
ID Var1 Var2
<int> <chr> <chr>
1 1 yes condition E
2 2 yes condition H
3 3 unknown condition D
4 4 no unknown condition
5 5 unknown condition H