我想生成一个包含以下列的数据框:
- 另一个数据框中每个因子的名称
- 每个因素的每个水平
- 相应的级别编号。
我最终能够想出下面的代码,它几乎可以工作,但它似乎有点复杂(我的 R 经验相当有限,并且涉及很多谷歌搜索)。我的代码存在哪些问题,是否有更好的方法来生成相同格式的相同输出?
mydata <- iris
#Get vector of column types
type <- sapply(mydata,class)
# Filter out just the ones that are factors
factors = type[type=="factor"]
# Allocate a vector to hold 1 data frame per factor
listOfFactors <- vector(mode = "list", length = length(factors))
# For each factor, list all the levels of that factor, and the level number
for (j in 1:length(factors)) {
cur_colname <- names(factors[j])
cur_colnum <- which(colnames(mydata)==cur_colname)
cur_nlevels <- nlevels(mydata[,cur_colnum])
listOfFactors[[j]] <- data.frame(VarName=character(cur_nlevels),
Level=character(cur_nlevels),
Number=integer(cur_nlevels),
stringsAsFactors=FALSE
)
for (i in 1:cur_nlevels) {
cur_level <- levels(mydata[,cur_colnum])[i]
listOfFactors[[j]]$VarName[i] <- cur_colname
listOfFactors[[j]]$Level[i] <- cur_level
listOfFactors[[j]]$Number[i] <- i
}
}
allfactorlevels <- do.call("rbind", listOfFactors)
代码的主要问题是不使用矢量化操作。从其他语言转换时可能会很棘手,但 for 循环在 R 中几乎从来都不是答案,尤其是当您使用它们一次访问一个矢量/列表/数据帧的元素时。我保留了代码的第一部分,然后采用了(更)简洁的方法来获取输出。
type <- sapply(mydata,class)
factors = type[type=="factor"]
现在我使用"lapply"来迭代因子列的名称。这意味着我可以使用这些名称来访问原始数据帧并提取我们需要的信息。
output <- lapply(names(factors),function(x){
res <- data.frame(VarName=x,
Level=levels(mydata[,x]),
Number=1:nlevels(mydata[,x]))
return(res)
})
然后,创建数据帧就很容易了:
do.call(rbind, output)
使用dplyr
函数的快速方法:选择因子变量,为每个变量创建因子水平和数字的数据框,然后将这些数据框重新绑定在一起。 purrr::map_dfr
将执行此迭代并将 ID 变量添加到生成的数据框中;在本例中,它是原始变量的名称。
我正在向数据添加另一个因子列,以便更好地说明和测试。
set.seed(1)
library(dplyr)
mydata <- iris %>%
mutate(Group = as.factor(sample(letters[1:4], nrow(.), replace = TRUE)))
mydata %>%
select(where(is.factor)) %>%
purrr::map_dfr(function(f) {
data.frame(Level = levels(f),
Number = seq_along(levels(f)))
}, .id = "VarName")
#> VarName Level Number
#> 1 Species setosa 1
#> 2 Species versicolor 2
#> 3 Species virginica 3
#> 4 Group a 1
#> 5 Group b 2
#> 6 Group c 3
#> 7 Group d 4