在保留原始级别的情况下,将不同因素级别下的数据合并

4
我想要一个tidyverse的解决方案来解决以下问题。在我的数据集中,我有各种因子水平的数据。我想创建一个新的因子水平“总计”,它是现有因子水平X的所有Y值的总和。例如,可以使用以下代码实现:
mutate(Data, X = fct_collapse(X, Total = c("A", "B", "C", "D"))) %>%
  group_by(X) %>% 
  summarize(Y = sum(Y))

然而,这也必然会覆盖原始的因子水平。我将不得不在另一个步骤中将原始数据集与新的折叠数据集结合起来。
我过去使用的一种保留原始水平的解决方案是将数据带入宽格式,并继续使用rowwise()mutate()创建一个新变量,其中包含“总计”,然后再转换为长格式。
spread(Data, key = X, value = Y) %>%
  rowwise() %>%
  mutate(Total = sum(A, B, C, D)) %>%
  gather(1:5, key = "X", value = "Y")

然而,我对这个解决方案非常不满意,因为使用rowwise()并不被认为是良好的实践。如果您能指出一个可用的替代方案,将不同因子水平下的数据组合在一起并保留原始水平,那就太好了。

最小可重现示例:

Data<-data.frame(
X = factor(c("A", "B", "C", "D")),
Y = c(1000, 2000, 3000, 4000))

期望结果:

# A tibble: 5 x 2
  X         Y
  <chr> <dbl>
1 A      1000
2 B      2000
3 C      3000
4 D      4000
5 Total 10000

1
df %>% janitor::adorn_totals("row") 这个对你来说可以吗?它需要加载一个额外的包,并且 Total 不会被添加为一个因子。 - M--
原则上,该函数正是我一直在寻找的(稍微有点限制,希望“总计”可以作为一个因子水平)。然而,我确实更喜欢一个tidyverse解决方案来解决这个问题。但我想我将不得不定义自己的函数,例如基于@Rui Barradas的建议。 - miwin
1
我经常做类似的事情,因此我为工作编写的一个包中添加了一个名为bind_self的函数:https://github.com/camille-s/camiller/blob/master/R/bind_self.R 这超出了SO答案的范围。 - camille
@miwin 如果你自己编写函数,那真的算是 tidyverse 解决方案吗?我是在概念上提问,而不是争论你可能更喜欢什么,这显然完全取决于你。 - M--
@M-M 你的观点很有道理 :) 我的意思是要有一个不需要额外包的解决方案。但是你的解决方案肯定是最简洁和非常有帮助的。 - miwin
3个回答

5
使用库,这将很简单。
Data %>% janitor::adorn_totals("row") %>% mutate(X=factor(X))

  # X     Y
  # A     1000
  # B     2000
  # C     3000
  # D     4000
  # Total 10000

查看输出结构:

str(output)

# 'data.frame': 5 obs. of  2 variables:
#  $ X: Factor w/ 5 levels "A","B","C","D",..: 1 2 3 4 5
#  $ Y: num  1000 2000 3000 4000 10000

1
我不知道janitor包,了解简单的解决方案总是很好的。 - Rui Barradas

4

根据@M--的第一个版本评论中的建议,在问题已经编辑后,我添加了bind_rows
我还稍微修改了输入数据集。跟随OP和@camille的评论,此数据集具有因子级别"Z",但保留原始顺序并在末尾添加级别"Total"

Data <- data.frame(
  X = factor(c("A", "B", "C", "Z")),
  Y = c(1000, 2000, 3000, 4000))

Data %>%
  mutate(lvl = levels(X),
         X = fct_collapse(X, Total = c("A", "B", "C", "Z")),
         X = as.character(X)) %>%
  bind_rows(mutate(Data, X = as.character(X)), .) %>%
  mutate(X = factor(X, levels = c(lvl, "Total"))) %>%
  group_by(X) %>% 
  summarize(Y = sum(Y)) -> d

d
## A tibble: 5 x 2
#  X         Y
#  <fct> <dbl>
#1 A      1000
#2 B      2000
#3 C      3000
#4 Z      4000
#5 Total 10000

检查输出因子的级别。

levels(d$X)
#[1] "A"     "B"     "C"     "Z"     "Total"

1
在分组和汇总之前,我会添加一个步骤mutate(X = as_factor(X)),以便保持Total的有序性。如果要查看我的意思,请将级别D更改为Z;如果分组变量不是因子,则summarize使用字母顺序。 - camille
非常感谢!使用 bind_rows() 的额外步骤很好地适应了流程。我唯一的担忧是使用默认情况下会产生警告的工作流程。这是否应该引起关注,还是可以忽略它? - miwin
1
@camille 你说得对,我已经修改了代码,将等级"Total"放在原有等级之后。 - Rui Barradas
2
除了回答本身,对他人的贡献给予认可(是的,我们可能已经自己想到了他们说的内容,但无论如何他们先说了)在我们这个极客社区中非常罕见。如果可以的话,我会给 +2 而不是 +1。一个是为了回答,另一个是为了道德伦理。干杯! - M--

1
这个解决方案也可以在这种情况下使用:

library(dplyr)

Data %>%
  add_row(X = "Total", Y = sum(.$Y)) %>%
  mutate(X = factor(X))

      X     Y
1     A  1000
2     B  2000
3     C  3000
4     D  4000
5 Total 10000

Data %>%
  add_row(X = "Total", Y = sum(.$Y)) %>%
  mutate(X = factor(X)) %>%
  {levels(.$X)}

[1] "A"     "B"     "C"     "D"     "Total"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接