【问题标题】:Combining data under different factor levels while retaining original levels结合不同因子水平下的数据,同时保留原始水平
【发布时间】:2019-02-12 16:21:38
【问题描述】:

我想为以下问题提供一个 tidyverse 解决方案。在我的数据集中,我有各种因素水平的数据。我想创建一个新的因子水平“总计”,它是 X 现有因子水平上所有 Y 值的总和。这可以通过以下方式完成,例如:

mutate(Data, X = fct_collapse(X, Total = c("A", "B", "C", "D"))) %>%
  group_by(X) %>% 
  summarize(Y = sum(Y))

但是,这也必然会覆盖原始因子水平。我必须在一个额外的步骤中将原始数据集与新折叠的数据集结合起来。

我过去用来保留原始级别的一种解决方案是采用宽格式数据并继续使用rowwise()mutate() 来创建一个带有“Total”的新变量,然后重新调整为 long .

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

【问题讨论】:

  • df %&gt;% janitor::adorn_totals("row") 这对你有用吗?它需要加载一个额外的包,Total 不会被添加为一个因素。
  • 原则上,该函数完全符合我一直在寻找的功能(稍微限定一下,将“Total”作为因子级别会很棒)。但是,我确实更喜欢这个问题的 tidyverse 解决方案。但我想然后必须定义我自己的功能,例如基于@Rui Barradas 的建议。
  • 我经常做类似的事情,以至于我在我为工作而编写的包中添加了一个函数 bind_selfgithub.com/camille-s/camiller/blob/master/R/bind_self.R 这有点超出了 SO 答案的范围
  • @miwin 如果您编写自己的函数,这真的是tidyverse 解决方案吗?我是在概念上询问,而不是争论你可能更喜欢什么,显然,这完全取决于你。
  • @M-M 你的观点很好:) 我的意思是有一个不需要额外软件包的答案。但是您的解决方案无疑是最简洁的,而且非常有帮助。

标签: r dplyr factors forcats


【解决方案1】:

使用 库,这很简单。

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

【讨论】:

  • 我不知道包janitor,知道简单的解决方案总是好的。
【解决方案2】:

使用@M--'s first version of his comment to the question中的建议,现在已编辑,我添加了bind_rows
我还稍微更改了输入数据集。在 OP 和 @camille 的 comment 之后,此数据集具有因子级别 "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"

【讨论】:

  • 我会在分组和汇总之前添加一个带有mutate(X = as_factor(X)) 的步骤,以使总计保持合理的顺序。要明白我的意思,将级别D 更改为Zsummarize 如果分组变量不是一个因素,则使用字母顺序
  • 非常感谢! bind_rows() 的额外步骤非常适合流程。我唯一担心的是使用默认会产生警告的工作流程。这应该是一个问题还是可以忽略?
  • @camille 你是对的,我已经将代码更改为在原始级别之后具有级别"Total"
  • 除了答案本身之外,将功劳归功于他人(是的,我们可能已经弄清楚他们自己说了什么,但无论如何他们先说了)在我们的书呆子社区中是非常罕见的事情。如果可以的话,我会给出 +2 而不是 +1。一个用于答案,一个用于道德。干杯。
【解决方案3】:

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

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"

【讨论】:

    猜你喜欢
    • 2012-02-06
    • 1970-01-01
    • 2016-06-06
    • 2020-11-13
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    • 1970-01-01
    • 2014-11-14
    相关资源
    最近更新 更多