我试图将数据框中变量的数据类型更改为'factor',如果它们是'character'。我试图使用如下的示例数据来复制问题
a <- c("AB","BC","AB","BC","AB","BC")
b <- c(12,23,34,45,54,65)
df <- data.frame(a,b)
str(df)
'data.frame': 6 obs. of 2 variables:
$ a: chr "AB" "BC" "AB" "BC" ...
$ b: num 12 23 34 45 54 65
我写了下面的函数来实现它
abc <- function(x) {
for(i in names(x)){
if(is.character(x[[i]])) {
x[[i]] <- as.factor(x[[i]])
}
}
}
如果我传递数据帧(df),该函数正在正确执行,但它仍然不会将'character'更改为'factor'。
abc(df)
str(df)
'data.frame': 6 obs. of 2 variables:
$ a: chr "AB" "BC" "AB" "BC" ...
$ b: num 12 23 34 45 54 65
注意:它与for循环和条件完美配合。当我试图通过围绕它编写一个函数来概括它时,就会出现问题。
请帮忙。我错过了什么?
除了来自@Roland的评论,你应该利用R的漂亮索引可能性并了解*apply
家族。有了它,你可以重写你的代码
change_to_factor <- function(df_in) {
chr_ind <- vapply(df_in, is.character, logical(1))
df_in[, chr_ind] <- lapply(df_in[, chr_ind, drop = FALSE], as.factor)
df_in
}
说明
vapply
遍历列表的所有元素,将函数应用于每个元素并返回给定类型的值(此处为布尔值logical(1)
)。由于在R
数据帧实际上是lists
,其中每个(列表)元素需要具有相同的长度,您可以方便地循环数据帧的所有列并将函数is.character
应用于每列。然后,vapply
返回一个带有TRUE/FALSE
值的布尔(逻辑)向量,具体取决于列是否为字符列。lapply
是*apply
家族的另一个成员,并循环通过列表元素并返回一个列表。我们现在循环遍历字符列,将as.factor
应用于它们并返回它们的列表,我们可以方便地存储在数据框中的原始位置顺便说一句,如果你看看str(df)
,你会看到b
列已经是一个因素了。这是因为data.frame
会自动将字符列转换为字符。为了避免这种情况,你需要将stringsAsFactors = FALSE
传递给data.frame
:
a <- c("AB", "BC", "AB", "BC", "AB", "BC")
b <- c(12, 23, 34, 45, 54, 65)
df <- data.frame(a, b)
str(df) # column b is factor
# 'data.frame': 6 obs. of 2 variables:
# $ a: Factor w/ 2 levels "AB","BC": 1 2 1 2 1 2
# $ b: num 12 23 34 45 54 65
str(df2 <- data.frame(a, b, stringsAsFactors = FALSE))
# 'data.frame': 6 obs. of 2 variables:
# $ a: chr "AB" "BC" "AB" "BC" ...
# $ b: num 12 23 34 45 54 65
str(change_to_factor(df2))
# 'data.frame': 6 obs. of 2 variables:
# $ a: Factor w/ 2 levels "AB","BC": 1 2 1 2 1 2
# $ b: num 12 23 34 45 54 65
学习tidyverse
语法也许是值得的
library(tidyverse)
df2 %>%
mutate_if(is.character, as.factor) %>%
str()