【问题标题】:Bind rows of data frames with some factor columns将数据框行与一些因子列绑定
【发布时间】:2017-07-05 19:18:30
【问题描述】:

我想创建一个升级版的dplyr::bind_rows,当我们尝试组合的dfs 中存在因子列时避免Unequal factor levels: coercing to character 警告(可能也有非因子列)。这是一个例子:

df1 <- dplyr::data_frame(age = 1:3, gender = factor(c("male", "female", "female")), district = factor(c("north", "south", "west")))
df2 <- dplyr::data_frame(age = 4:6, gender = factor(c("male", "neutral", "neutral")), district = factor(c("central", "north", "east")))

然后bind_rows_with_factor_columns(df1, df2) 返回(没有警告):

dplyr::data_frame(
  age = 1:6,
  gender = factor(c("male", "female", "female", "male", "neutral", "neutral")),
  district = factor(c("north", "south", "west", "central", "north", "east"))
)

这是我目前所拥有的:

bind_rows_with_factor_columns <- function(...) {
  factor_columns <- purrr::map(..., function(df) {
      colnames(dplyr::select_if(df, is.factor))
  })

  if (length(unique(factor_columns)) > 1) {
      stop("All factor columns in dfs must have the same column names")
  }

  df_list <- purrr::map(..., function (df) {
    purrr::map_if(df, is.factor, as.character) %>% dplyr::as_data_frame()
  })

  dplyr::bind_rows(df_list) %>%
    purrr::map_at(factor_columns[[1]], as.factor) %>%
    dplyr::as_data_frame()
}

我想知道是否有人对如何合并 forcats 包有任何想法,以潜在地避免对字符强制因素,或者是否有人有任何一般性的建议来提高性能同时保持相同的功能(我想坚持tidyverse 语法)。谢谢!

【问题讨论】:

  • 为什么不直接do.call(rbind, list(df1, df2))
  • suppressWarningspurrr::quietly?

标签: r dplyr purrr tidyverse


【解决方案1】:

使用dplyr::bind_rows 并抑制警告可能更简单,然后将所有新字符列转换回因子。这具有通过列名绑定data.frames 的优势(允许列的不同顺序和包含额外的列),并且当因子变量有时记录为字符时仍然有效。

library(tidyverse)

bind_rows_keep_factors <- function(...) {
  ## Identify all factors
  factors <- unique(unlist(
    map(list(...), ~ select_if(..., is.factor) %>% names())
  ))
  ## Bind dataframes, convert characters back to factors
  suppressWarnings(bind_rows(...)) %>% 
    mutate_at(vars(one_of(factors)), factor)  
}

dat1 <- tibble(
  id = 1:2,
  fruit = factor(c("banana", "apple"))
)

dat2 <- tibble(
  id = 3:4,
  fruit = c("pear", "banana"),
  taste = c("Mmmm", "yum")
)

bind_rows_keep_factors(dat1, dat2)
# A tibble: 4 x 3
     id fruit  taste
  <int> <fct>  <chr>
1     1 banana NA   
2     2 apple  NA   
3     3 pear   Mmmm 
4     4 banana yum 

当然,因子水平的顺序被打乱了(恢复为字母顺序)。

【讨论】:

    【解决方案2】:

    根据朋友的一个很好的解决方案来回答我自己的问题:

    bind_rows_with_factor_columns <- function(...) {
      purrr::pmap_df(list(...), function(...) {
        cols_to_bind <- list(...)
        if (all(purrr::map_lgl(cols_to_bind, is.factor))) {
          forcats::fct_c(cols_to_bind)
        } else {
          unlist(cols_to_bind)
        }
      })
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-07
      • 2012-11-16
      • 1970-01-01
      • 2021-10-13
      • 2018-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-12
      相关资源
      最近更新 更多