Каков элегантный способ изменить типы данных столбцов фреймов данных из списка имен типов данных?
Вот пример (функция change_to_data_types — это то, что я ищу):
my_df <- iris
my_types <- c("factor", "character", "double", "logical", "character")
my_df <- my_df %>% change_to_data_types(my_types)
my_types
имеет то же количество элементов, что и количество столбцов в my_df
, и преобразование выполняется в том же порядке.
Это пример «неэлегантного» способа
my_df$Sepal.Length <- my_df$Sepal.Length %>% as.factor()
my_df$Sepal.Width <- my_df$Sepal.Width %>% as.character()
#etc...
Может быть, определить colClasses при чтении данных? Что-то вроде: mydf <- read.table(..., colClasses = c("factor", "character", "double", "logical", "character")
Фреймы данных находятся в списке после разделения большой таблицы со смешанными типами данных.
Это может превратиться в «проблему XY», почему бы тогда не убедиться, что список создается с помощью одних и тех же структур данных?
Разделение выполняется по одному столбцу в исходном фрейме данных, и этот столбец является единственным столбцом, общим для всех фреймов данных. Я не контролирую исходный фрейм данных или структуры.
Вариант был бы
library(tidyverse)
my_df[] <- map2(my_df, str_c("as.", my_types), ~ get(.y)(.x))
Или в base R
my_df[] <- Map(function(x, y) get(y)(x), my_df, paste0("as.", my_types))
-проверить класс еще раз
sapply(my_df, class)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# "factor" "character" "numeric" "logical" "character"
вау, что происходит с get()
? Он получает функцию из строки и принимает объект во вторых круглых скобках в качестве входных данных для указанной функции? Не видел этого раньше.
@Humpelsielzchen Возвращает значение функции, а ()
является входом для этой функции. вот это as.factor(column)
РЕДАКТИРОВАТЬ: Чтобы избежать искажения факторов из-за прямого преобразования числовых значений в факторы, мы можем сделать:
lapply(seq_along(names(my_df)),
function(x){
if (is.numeric(my_df[,x]) &
my_types[x] = = "factor"){
as.factor(as.character(my_df[,x]))
}
else{
as(my_df[,x],my_types[x])
}
}
)
ОРИГИНАЛ:
Мы можем сделать:
sapply(seq_along(names(my_df)),
function(x) as(my_df[,x],my_types[x]))
Мне было трудно использовать с map2
, сформированными уродливыми факторами.
Это возвращает матрицу, что означает, что все столбцы относятся к одному классу.
Я думаю, вам нужно изменить sapply
на lapply
и вернуть вывод
должно быть что-то вроде my_df[] <- lapply(seq_along(my_df), function(x) as(my_df[[x]], my_types[x]))
@IceCreamToucan Отредактировано, ваше мнение о редактировании будет оценено.
Развлекаемся с match.fun:
my_df[] <- lapply(seq_along(names(my_df)),
function(i) match.fun(paste0("as.", my_types[ i ]))(my_df[[ i ]]))
sapply(my_df, class)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# "factor" "character" "numeric" "logical" "character"
Мы можем использовать mapply
для подачи utils::as
пар столбцов и типов. Это не будет работать для столбцов факторов, поэтому они обрабатываются отдельно.
fcols <- my_types == "factor"
my_df[!fcols] <- mapply(as, my_df[!fcols], my_types[!fcols], SIMPLIFY = FALSE)
my_df[fcols] <- lapply(my_df[fcols], factor)
почему бы не сделать это всего одним вызовом lapply()?
@sindri_baldur Личный вкус, правда. Мне кажется, это легче читать, чем создавать функцию с логикой if
/else
для одного mapply
. Ваш пробег может отличаться.
Просто сомнение, не лучше ли это делать при чтении самих данных, т.е. есть более простой вариант в
colClasses
при чтении