【问题标题】:dplyr: passing a grouped tibble to a custom functiondplyr:将分组的 tibble 传递给自定义函数
【发布时间】:2018-06-19 12:10:16
【问题描述】:

(以下场景简化了我的实际情况)
我的数据来自村庄,我想用村庄变量来总结一个结果变量。

> data
   village     A     Z      Y 
     <chr> <int> <int>   <dbl> 
 1       a     1     1   500     
 2       a     1     1   400     
 3       a     1     0   800  
 4       b     1     0   300  
 5       b     1     1   700  

例如,我想仅使用Z==z 按村庄计算Y 的平均值。在这种情况下,我希望村庄“a”有 (500 + 400)/2 = 450,村庄“b”有 700。

请注意,实际情况比较复杂,我不能直接使用this answer,但是我需要将一个分组的tibble和一个全局变量(z)传递给我的函数

z <- 1 # z takes 0 or 1
data %>%
    group_by(village) %>% # grouping by village
    summarize(Y_village = Y_hat_village(., z)) # pass a part of tibble and a global variable

Y_hat_village <- function(data_village, z){
    # This function takes a part of tibble (`data_village`) and a variable `z`
    # Calculate the mean for a specific z in a village
    data_z <- data_village %>% filter(Z==get("z"))
    return(mean(data_z$Y))
}

但是,我发现. 传递了整个 tibble,并且上面的代码为所有组返回了相同的值。

【问题讨论】:

  • 我想你在找do,你也可以考虑用split然后map

标签: r dplyr


【解决方案1】:

您可以简化几件事。一个在您的函数中:由于您将值z 传递给函数,因此您不需要使用get("z")。你传入的全局环境中有一个z;或者,更安全地,将您的 z 值分配给具有其他名称的变量,这样您就不会遇到范围问题,并将其传递给函数。在这种情况下,我称之为z_val

library(tidyverse)

z_val <- 1

Y_hat_village2 <- function(data, z) {
  data_z <- data %>% filter(Z == z)
  return(mean(data_z$Y))
}

您可以使用do 对每个组进行函数调用,这将为您提供一个列表列,然后取消嵌套该列。再次注意,我将变量 z_val 传递给参数 z

df %>%
  group_by(village) %>%
  do(y_hat = Y_hat_village2(., z = z_val)) %>%
  unnest()
#> # A tibble: 2 x 2
#>   village y_hat
#>   <chr>   <dbl>
#> 1 a         450
#> 2 b         700

但是,do 已被弃用,取而代之的是 purrr::map,我仍然无法掌握它。在这种情况下,您可以分组和嵌套,这会给出一列称为data 的数据框,然后映射到该列并再次提供z = z_val。当您取消嵌套 y_hat 列时,您仍将原始数据作为嵌套列,因为您仍希望访问其余列。

df %>%
  group_by(village) %>%
  nest() %>%
  mutate(y_hat = map(data, ~Y_hat_village2(., z = z_val))) %>%
  unnest(y_hat)
#> # A tibble: 2 x 3
#>   village data             y_hat
#>   <chr>   <list>           <dbl>
#> 1 a       <tibble [3 × 3]>   450
#> 2 b       <tibble [2 × 3]>   700

为了检查一切是否正常,我还传入了z = 0 以检查 1. 范围问题和 2. z 的其他值是否有效。

df %>%
  group_by(village) %>%
  nest() %>%
  mutate(y_hat = map(data, ~Y_hat_village2(., z = 0))) %>%
  unnest(y_hat)
#> # A tibble: 2 x 3
#>   village data             y_hat
#>   <chr>   <list>           <dbl>
#> 1 a       <tibble [3 × 3]>   800
#> 2 b       <tibble [2 × 3]>   300

【讨论】:

    【解决方案2】:

    作为@patL 答案的扩展/修改,您还可以将tidyverse 解决方案包装在purrr:map 中以返回两个tibbles 的list,每个z 值一个:

    z <- c(0, 1);
    map(z, ~df %>% filter(Z == .x) %>% group_by(village) %>% summarise(Y.mean = mean(Y)))
    #[[1]]
    ## A tibble: 2 x 2
    #  village Y.mean
    #  <fct>    <dbl>
    #1 a         800.
    #2 b         300.
    #
    #[[2]]
    ## A tibble: 2 x 2
    #  village Y.mean
    #  <fct>    <dbl>
    #1 a         450.
    #2 b         700.
    

    样本数据

    df <- read.table(text =
        "  village     A     Z      Y
     1       a     1     1   500
     2       a     1     1   400
     3       a     1     0   800
     4       b     1     0   300
     5       b     1     1   700  ", header = T)
    

    【讨论】:

      【解决方案3】:

      您可以使用dplyr 来完成它:

      library(dplyr)
      
      df %>% 
        group_by(village) %>% 
        filter(Z == 1) %>% 
        summarise(Y_village = mean(Y))
      
      ## A tibble: 2 x 2
      #  village Y_village
      #  <chr>       <dbl>
      #1 a             450
      #2 b             700
      

      获取所有列:

      df %>% 
        group_by(village) %>% 
        filter(Z == 1) %>% 
        mutate(Y_village = mean(Y)) %>% 
        distinct(village, A, Z, Y_village)
      
      ## A tibble: 2 x 4
      ## Groups:   village [2]
      #  village     A     Z Y_village
      #  <chr>   <dbl> <dbl>     <dbl>
      #1 a           1     1       450
      #2 b           1     1       700
      

      数据

      df <- data_frame(village = c("a", "a", "a", "b", "b"),
                       A = rep(1, 5),
                       Z = c(1, 1, 0, 0, 1),
                       Y = c(500, 400, 800, 30, 700))
      

      【讨论】:

      • 谢谢!我认为您的答案通过了一列而不是小标题的一部分。对于这个玩具示例,我知道它可以工作,但我想在我的函数中包含所有列。
      • 您想保留包括Y 在内的所有列或所有带有Y_village 的列?
      猜你喜欢
      • 1970-01-01
      • 2015-03-27
      • 1970-01-01
      • 2018-03-18
      • 2015-06-23
      • 2019-09-04
      • 2015-03-14
      • 1970-01-01
      相关资源
      最近更新 更多