Как динамически изменить тип данных столбцов во фрейме данных - PullRequest
0 голосов
/ 10 января 2019

Платформа, с которой я импортирую данные в R, не поддерживает указание типа данных, поэтому все мои столбцы character. У меня есть файл Excel, в котором указываются столбцы factor, включая соответствующие labels и levels. Сейчас я пытаюсь написать функцию для динамического изменения типа данных в различных столбцах моего data.frame

Благодаря превосходному ответу на этот вопрос ( dplyr - mutate: использовать имена динамических переменных ) мне удалось написать следующую функцию, в которой я динамически устанавливаю имя столбца для функции mutate ,

readFactorData <- function(filepath) {
    t <- read.xlsx(filepath)
    sapply(nrow(t), function(i) {
      colname <- as.character(t[i, "Item"])
      factorLevels <- t[i, 3:ncol(t)][which(!is.na(t[i, 3:ncol(t)]))]
      totalLevels <- length(factorLevels)
      listOfLabels <- as.character(unlist(factorLevels))

      mutate(d, !!colname := factor(d[[colname]], labels=(1:totalLevels), levels=listOfLabels))
        # requires dplyr v.0.7+
        # the syntax `!!variablename:=` forces evaluation of the variablename before evaluating the rest of the function
    })
}

Это работает, и каждая итерация возвращает весь фрейм данных с соответствующим столбцом (colname), замененным на множитель. Но каждая итерация перезаписывает предыдущую, поэтому эта функция возвращает только последний результат i. Как мне убедиться, что у меня получился один фрейм данных, в котором сохранены все соответствующие столбцы?

Пример данных (обязательно закомментируйте первую строку функции выше, так как здесь мы определяем t):

 d <- data.frame("id" = sample(100:999, 10), "age" = sample(18:80, 10), "factor1" = c(rep("a", 3), rep("b", 3), rep("c", 4)), "factor2" = c("x","y","y","y","y","x","x","x","x","y"), stringsAsFactors = FALSE)
 t <- data.frame("Item" = c("factor1","factor2"), "Label" = c("This is factor 1", "This is factor 2"), "level1" = c("a","x"), "level2" = c("b","y"), "level3" = c("c","NA"))

Ответы [ 3 ]

0 голосов
/ 11 января 2019

Если вы хотите преобразовать все свои factor в character в пределах вашего data.frame, вы можете использовать dplyr mutate_if. В противном случае, если вы хотите использовать вектор имен столбцов, ответ @ Eden Z's сделает это за вас.

library(tidyverse)

d_out <- d %>% 
  mutate_if(is.character, as.factor)

d_out

#    id age factor1 factor2
#1  933  61       a       x
#2  208  52       a       y
#3  193  25       a       y
#4  231  47       b       y
#5  595  78       b       y
#6  675  28       b       x
#7  387  71       c       x
#8  386  80       c       x
#9  893  20       c       x
#10 272  23       c       y

Когда вы можете проверить класс на наличие переменных:

sapply(d_out, class)

#       id       age   factor1   factor2 
#"integer" "integer"  "factor"  "factor" 
0 голосов
/ 14 января 2019

Функция ниже отображает через readr::parse_* функции, указанные для каждого именованного столбца, который вы хотите изменить, и затем позволяет вам указывать аргументы для каждого (например, levels, если используется parse_factor).

library(tidyverse)

parse_cols <- function(df, f, col_names, levels, ...){
  # df: dataframe, f: char vec, col_names: char vec, levels: list of char vecs,
  # ...: list of other potential args for parse_*
  params_t <- tibble(x = map(col_names, ~df[[.x]]), levels = levels, ...) %>% transpose()

  new_cols <- map2_df(.x = structure(f, names = col_names), 
                      .y = params_t, 
                      ~R.utils::doCall(.x, args = .y, .ignoreUnusedArgs = TRUE))  

  df[names(new_cols)] <- new_cols
  df
}

# function inputs -- perhaps just requiring a tibble input would be safer
parsings_vec <- c("parse_factor","parse_double", "parse_factor")
cols_vec <- c("manufacturer", "cty", "class")
factors_list <- list(unique(mpg[["manufacturer"]]), NULL, unique(mpg[["class"]]))

parse_cols(df = mpg, f = parsings_vec, col_names = cols_vec, levels = factors_list) 
#> # A tibble: 234 x 11
#>    manufacturer model displ  year   cyl trans drv     cty   hwy fl    cla~
#>    <fct>        <chr> <dbl> <int> <int> <chr> <chr> <dbl> <int> <chr> <fc>
#>  1 audi         a4      1.8  1999     4 auto~ f        18    29 p     com~
#>  2 audi         a4      1.8  1999     4 manu~ f        21    29 p     com~
#>  3 audi         a4      2    2008     4 manu~ f        20    31 p     com~
#>  4 audi         a4      2    2008     4 auto~ f        21    30 p     com~
#>  5 audi         a4      2.8  1999     6 auto~ f        16    26 p     com~
#>  6 audi         a4      2.8  1999     6 manu~ f        18    26 p     com~
#>  7 audi         a4      3.1  2008     6 auto~ f        18    27 p     com~
#>  8 audi         a4 q~   1.8  1999     4 manu~ 4        18    26 p     com~
#>  9 audi         a4 q~   1.8  1999     4 auto~ 4        16    25 p     com~
#> 10 audi         a4 q~   2    2008     4 manu~ 4        20    28 p     com~
#> # ... with 224 more rows
0 голосов
/ 11 января 2019

Если я правильно понимаю, у вас есть один фрейм данных со значениями столбцов факторов другого фрейма данных. Вы хотите извлечь их из первого df и изменить эти столбцы во втором df и превратить их в факторы.

Как насчет сохранения вектора имен столбцов и затем их всех мутировать?

colnames <- t %>%
  pull(Item) %>%
  as.character()

d_with_factors <- d %>%
  mutate_at(colnames, as.factor)

Тогда

sapply(d_with_factors, class)

Возвращает

       id       age   factor1   factor2 
"integer" "integer"  "factor"  "factor" 
...