2

这是一个相当少的复制代码。真实的数据集比较大,因子也比较多,所以手动列出因子是不切实际的。数据上还有更有趣的转换,我想继续使用 dplyr。

library(dplyr)
a = data.frame(f=factor(c("a", "b")), g=c("a", "a"))
b = data.frame(f=factor(c("a", "c")), g=c("a", "a"))
a = a %>% group_by(g) %>% mutate(n=1)
b = b %>% group_by(g) %>% mutate(n=2)
rbind(a,b)

这会产生:

# A tibble: 4 x 3
# Groups:   g [1]
      f      g     n
  <chr> <fctr> <dbl>
1     a      a     1
2     b      a     1
3     a      a     2
4     c      a     2
Warning messages:
1: In bind_rows_(x, .id) : Unequal factor levels: coercing to character
2: In bind_rows_(x, .id) :
  binding character and factor vector, coercing into character vector
3: In bind_rows_(x, .id) :
  binding character and factor vector, coercing into character vector

这些警告很烦人,如果我不使用,它们实际上会消失group_by

> a = data.frame(f=factor(c("a", "b")), g=c("a", "a"))
> b = data.frame(f=factor(c("a", "c")), g=c("a", "a"))
> a = a %>% mutate(n=1)
> b = b %>% mutate(n=2)
> rbind(a,b)
  f g n
1 a a 1
2 b a 1
3 a a 2
4 c a 2

显式转换为data.frame之前rbind也可以:

> rbind(data.frame(a),data.frame(b))
  f g n
1 a a 1
2 b a 1
3 a a 2
4 c a 2

是否有一种简单的方法使用基本 R 或 dplyr rbind/bind_rows自动合并这些因素及其级别,而不是将它们转换为字符(这对我来说意义不大),同时仍然使用 dplyr 进行数据转换?

我发现https://stackoverflow.com/a/30468468/388803提出了一种手动合并因素的解决方案,但这非常冗长。

我的实际用例是加载两个 .csv 文件read.table,进行一些数据转换,然后合并数据,因为它们是互补的。我目前的解决方法是data.frame(data)在数据转换结束时调用。我想知道为什么 dplyr/tibble 不会自动合并因子,因为在这种情况下看起来很安全。这是可以改进的东西吗?

4

3 回答 3

4

我在解决类似任务时遇到了这个问题。使用forcats::lvls_union,您可以获得因子列表中所有级别的特征向量 - 在本例中,a$fb$f。然后,您可以使用forcats::fct_expand扩展每个数据框f以具有该因子级别的联合。

library(tidyverse)

a <- data.frame(f = factor(c("a", "b")), g = c("a")) %>%
  mutate(n = 1) %>%
  group_by(g)

b <- data.frame(f = factor(c("a", "c")), g = c("a")) %>%
  mutate(n = 2) %>%
  group_by(g)

all_lvls <- lvls_union(list(a$f, b$f))

获得级别的联合后,您可以mutate同时使用数据框和调用bind_rows

bind_rows(
  a %>% mutate(f = fct_expand(f, all_lvls)),
  b %>% mutate(f = fct_expand(f, all_lvls))
)
#> # A tibble: 4 x 3
#> # Groups:   g [1]
#>   f     g         n
#>   <fct> <fct> <dbl>
#> 1 a     a         1
#> 2 b     a         1
#> 3 a     a         2
#> 4 c     a         2

或者,为了获得相同的结果,您可以map在两个数据框的列表上展开每个f. Usingmap_dfr是一种简写,例如调用map,然后通过管道输入bind_rows

map_dfr(list(a, b), ~mutate(., f = fct_expand(f, all_lvls)))
#> # A tibble: 4 x 3
#> # Groups:   g [1]
#>   f     g         n
#>   <fct> <fct> <dbl>
#> 1 a     a         1
#> 2 b     a         1
#> 3 a     a         2
#> 4 c     a         2

reprex 包(v0.2.0)于 2018 年 7 月 17 日创建。

于 2018-07-18T01:41:10.743 回答
3

解决方案使用data.table.
将您的转换data.frame为 adata.table并添加nusing :=(不需要dplyr)。

a <- data.frame(f=factor(c("a", "b")), g=c("a", "a"))
b <- data.frame(f=factor(c("a", "c")), g=c("a", "a"))
library(data.table)
rbind(setDT(a)[, n := 1], 
      setDT(b)[, n := 2])
   f g n
1: a a 1
2: b a 1
3: a a 2
4: c a 2
于 2017-10-22T16:17:17.120 回答
2

如果因子只是一种有效的字符串存储,可以在合并之前将它们转换为字符串,然后再转换为因子:

bind_rowsFactors <- function(
  ### bind_rows on two data.frames with merging factors levels
  a      ##<< first data.frame to bind
  , b    ##<< second data.frame to bind
  , ...  ##<< further arguments to \code{bind_rows}
){
  isInconsistentFactor <- sapply( names(a),  function(col){
    (is.factor(a[[col]]) | is.factor(b[[col]])) &&
      any(levels(a[[col]]) != levels(b[[col]]))
  })
  if (sum(isInconsistentFactor)) warning(
    "releveling factors ", paste(names(a)[isInconsistentFactor], collapse = ","))
  for (col in names(a)[isInconsistentFactor]) {
    a <- mutate(ungroup(a), !!col := as.character(!!rlang::sym(col)))
    b <- mutate(ungroup(b), !!col := as.character(!!rlang::sym(col)))
  }
  ans <- bind_rows(a, b, ...)
  # convert former factors form string back to factor
  for (col in names(ans)[isInconsistentFactor]) {
    ans <- mutate(ungroup(ans), !!col := factor(!!rlang::sym(col)))
  }
  ##value<< result of \code{bind_rows} with inconsistend factor columns still factors
  ans
}

library(dplyr)
a = data.frame(f = factor(c("a", "b")), g = c("a", "a"))
b = data.frame(f = factor(c("a", "c")), g = c("a", "a"))
a = a %>% group_by(g) %>% mutate(n = 1)
b = b %>% group_by(g) %>% mutate(n = 2)
#bind_rows(a,b)
bind_rowsFactors(a,b)

奇怪的!!rlang::sym符号只是使用 dplyr 和未定义符号进行非标准评估的一种解决方法。

上面的代码在重新定义因子级别时发出警告f,但否则会返回绑定的 data.frame,其中列f是一个因子。

# A tibble: 4 x 3
  f     g         n
  <fct> <fct> <dbl>
1 a     a        1.
2 b     a        1.
3 a     a        2.
4 c     a        2.
Warning message:
In bind_rowsFactors(a, b) : releveling factors f
于 2018-05-24T07:27:36.440 回答