【问题标题】:How to apply functions on grouped elements in dplyr while keeping part of the data如何在 dplyr 中对分组元素应用函数,同时保留部分数据
【发布时间】:2021-01-19 23:18:34
【问题描述】:

问题总结

目前,我正在尝试应用一个以某种方式计算滞后数据的函数,该函数必须应用于不同的组集,我似乎无法理解如何做到这一点Dplyr.

预期结果

例如,数据如下所示:

   date group value
1 Jan 1     A     0
2 Feb 1     A     1
3 Mar 1     A     0
4 Jan 1     B     2
5 Feb 1     B     4
6 Mar 1     B     0

我们的想法是获得这样的输出,例如 2 个月的滚动平均值:

   date group value roll_mean
1 Feb 1     A     1       0.5
2 Mar 1     A     0       0.5
3 Feb 1     B     4         3
4 Mar 1     B     0         2

可重现的例子

为了示例,我准备了一个可在 R 中使用的示例,其中包含数据和函数:

data <- data.frame(
  date = as.yearmon(
    as.Date(
        c("01/01/2020", "01/02/2020", "01/03/2020", "01/01/2020", "01/02/2020", "01/03/2020"),
        origin = "1970-01-01")
    ),
  group = c("A", "A", "A", "B", "B", "B"),
  value = c(0, 1, 0, 2, 4, 0)
)

computing_function <- function(data) {
  output_data <- data %>% 
    mutate(
      roll_mean = rollmean(data$value, k = 2, fill = NA, align="right")
    ) %>% 
    drop_na(roll_mean)
  return (output_data)
}

问题

我知道如何使用循环来做到这一点,但这会很耗时,不可靠并且维护起来很痛苦。例如,组的输出有效。但是,即使使用summarise(),我也无法使其在 Dplyr 中工作。我在我的逻辑中遗漏了一些东西,我无法确定它是什么。

computing_function(data %>% filter(group == "B"))
#    date group value roll_mean
# 1 Feb 1     B     4         3
# 2 Mar 1     B     0         2

data %>%
  group_by(group) %>% 
  summarise(computing_function(.))
# Error

有没有办法在 dplyr-wise 方面做到这一点?

函数的逻辑应该留在里面,想法是不要把它偏移到dplyr表达式中。

使用的包:

  • Dplyr
  • 动物园

【问题讨论】:

    标签: r dplyr zoo


    【解决方案1】:

    summarise 不是正确的函数,因为它每组返回一行。 此外,您的函数获取一个 data.frame 作为输入,这在summarisemutate 中不起作用。你要么想要这样的东西,它需要一个向量作为输入:

    computing_function1 <- function(values) {
        rollmean(values, k = 2, fill = NA, align="right")
    }
    
    data %>%
      group_by(group) %>% 
      mutate(roll_mean = computing_function1(value)) %>% 
      filter(!is.na(roll_mean))
    

    或者如果你真的想保持相同的结构,你必须拆分数据集,将函数应用于“组”并重新组装数据集:

    data %>%
      split(.$group) %>% 
      map_dfr( computing_function ) 
    

    【讨论】:

    • 很好的答案,我没有这样想,在深入研究这个主题后,我找到了一个更 dplyr 的解决方案,我稍后会放在这里。
    【解决方案2】:

    可以在 Dplyr 的文档中找到有关这种情况的答案,该文档具有函数 modify_by。它是一种 purrr 风格的函数,可以在数据帧(以及扩展的 tibbles)上使用函数。

    此函数允许输入为小标题,输出为分组小标题。 这是 Dplyr 代码和逻辑的答案:

    data %>%
      group_by(group) %>% 
      group_modify(~computing_function(.x))
    
    # # A tibble: 4 x 4
    # # Groups:   group [2]
    #   group date      value roll_mean
    #   <chr> <yearmon> <dbl>     <dbl>
    # 1 A     Feb 1         1       0.5
    # 2 A     Mar 1         0       0.5
    # 3 B     Feb 1         4       3  
    # 4 B     Mar 1         0       2 
    

    如果需要,您可以在 group_modify() 表达式之后使用 ungroup() 来输出未分组的 tibble 以传递给其他函数。

    【讨论】:

      【解决方案3】:

      1) 使用read.zoodata 转换为宽格式的动物园对象,其中每个日期对应一行,每个组对应一个列。然后使用rollmeanr(与rollmean 相同,但默认为align="right")并创建一个复杂对象,该对象由原始值作为实部,均值作为虚部。使用 fortify.zoo 将其融化回长格式数据框并提取实部和虚部。这仅使用 magrittr 管道和动物园。

      data %>%
        read.zoo(split = "group") %>%
        { fortify.zoo(. + rollmeanr(., 2) * 1i, melt = TRUE, names = names(data)) } %>%
        transform(value = Re(value), rollmean = Im(value))
      

      给予:

            date group value rollmean
      1 Feb 0001     A     1      0.5
      2 Mar 0001     A     0      0.5
      3 Feb 0001     B     4      3.0
      4 Mar 0001     B     0      2.0
      

      2) 上面的变体如下。它计算均值的宽动物园对象,然后将其转换回长格式数据框并将值列重新合并。如果您真的不需要结果中的值列,则可以省略最后一行代码,并且如果方法的 zoo 对象就足够了,倒数第二个可以省略。

      data %>%
         read.zoo(split = "group") %>%
         rollmeanr(2) %>%
         fortify.zoo(melt = TRUE, names = c(names(data)[1:2], "rollmean")) %>%
         merge(data, ., by = 2:1)
      

      【讨论】:

      • 虽然我同意这是应用该特定功能的好方法,但我正在寻找在保留原始功能的同时仅使用 Dplyr 的东西。原因是我在现实生活中的应用程序使用了一个更复杂的函数,它对数据的影响有些相同。
      • 除了输入和输出一个动物园对象外,这里没有任何特定于 rollmeanr 的东西,但根据你所拥有的,即使这样也可以放宽。计算本身可以任意复杂。
      • 谢谢您的意见。但是,正如我的问题所述,我需要 函数保持原样 并找到一种 dplyr 方法来进行此计算。这个想法是输入一个分组的data.frame 并输出一个data.frame
      猜你喜欢
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-21
      • 2018-01-02
      • 1970-01-01
      • 1970-01-01
      • 2019-08-27
      相关资源
      最近更新 更多