这是一个相当小的再现代码。真实数据集较大且有许多因素,因此手动列出因素是不实际的。还有更有趣的数据转换,我想继续使用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提出了手动合并因子的解决方案,但这非常详细。
我的实际用例是使用read.table
加载两个.csv文件,进行一些数据转换,然后合并数据,因为它们是互补的。
我目前的解决方法是在数据转换结束时调用data.frame(data)
。
我想知道为什么dplyr / tibble不能自动合并因子,因为它在这种情况下似乎是安全的。这可能会改善吗?
答案 0 :(得分:2)
使用data.table
的解决方案
将您的data.frame
转换为data.table
并使用n
添加:=
(无需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
答案 1 :(得分: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)
对于带有dplyr和未定义符号的非标准平均而言,奇怪的!!rlang::sym
符号只是workaround。
上面的代码会在重新定义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
答案 2 :(得分:2)
我在解决类似任务时遇到了这个问题。使用forcats::lvls_union
,您可以在一系列因素中获得所有级别的字符向量-在这种情况下为a$f
和b$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
。使用map_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 package(v0.2.0)于2018-07-17创建。