По сути, у меня есть список фреймов данных, к которым я хочу применить as.character()
.
Чтобы получить список фреймов данных, у меня есть список файлов, которые я прочитал с помощью функции map()
и функции чтения, которую я создал. Я не могу использовать map_df()
, потому что есть столбцы, которые считываются как разные типы данных. Все файлы одинаковы, и я знаю, что мог бы жестко закодировать типы данных в функции чтения, если бы захотел, но я хочу избежать этого, если смогу.
В этот момент я бросаю список фреймов данных в цикл for и применяю другую функцию map()
, чтобы применить функцию as.character()
. Этот окончательный список кадров данных затем сжимается с помощью bind_rows()
.
В целом, это кажется чрезвычайно запутанным процессом, см. код ниже.
audits <- list.files()
my_reader <- function(x) {
my_file <- read_xlsx(x)
}
audits <- map(audits, my_reader)
for (i in 1:length(audits)) {
audits[[i]] <- map_df(audits[[i]], as.character)
}
audits <- bind_rows(audits)
Есть ли у кого-нибудь идеи о том, как я могу улучшить это? В идеале до такой степени, что я могу делать все в одной векторизованной map()
функции?
Для воспроизводимости вы можете использовать два набора данных радужной оболочки с измененным типом данных одного из столбцов.
iris2 <- iris
iris2[1] <- as.character(iris2[1])
my_list <- list(iris, iris2)
Кстати, знаете ли вы, где я могу прочитать немного больше о том, как семейства map/apply используют ~. Наверное, tidyverse вещь, чтобы быть справедливым?
Я бы сначала проверил виньетки tidyverse, которые были бы более современными.
@HanselPalencia ~
и .x
на самом деле просто заменяют x
в function(x)
. Таким образом, вы также можете написать это как map(my_list, function(x) x %>% mutate(across(everything(), as.character)))
@HanselPalencia Вы, вероятно, также увидите это сокращение: map(my_list, \(x) x %>% mutate(across(everything(), as.character)))
. Здесь \ просто заменяет слово function
.
as.character
работает с vector
, тогда как data.frame
является list
из vector
s. Можно использовать across
, если мы хотим использовать map
только один раз.
library(dplyr)
library(purrr)
map_dfr(my_list, ~ .x %>%
mutate(across(everything(), as.character)))
В качестве дополнения, чтобы закончить вопрос, вы можете изменить map()
на map_df()
для финальных bind_rows, совершенно необязательно и в зависимости от задачи.
@HanselPalencia добавил _dfr
, спасибо
Я хотел показать базовое решение R на тот случай, если оно поможет кому-то еще. Вы можете использовать rapply
для рекурсивного просмотра списка и применения функции. вы можете указать класс, и если вы хотите заменить или исключить/перечислить возвращаемый объект:
iris2 <- iris
iris2[1] <- as.character(iris2[1])
my_list <- list(iris, iris2)
mylist2 <- rapply(my_list, class = "ANY", f = as.character, how = "replace")
bigdf <- do.call(rbind, mylist2)
Вам нужно
map(my_list, ~ .x %>% mutate(across(everything(), as.character)))