Помимо комментария от @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()