dplyr:将分组的小标题传递给自定义函数

时间:2018-06-19 12:10:17

标签: r dplyr

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

> 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  

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

请注意,实际情况更为复杂,我无法直接使用this answer,但是关键是我需要将分组的小节和全局变量(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))
}

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

3 个答案:

答案 0 :(得分:4)

您可以简化两件事。其中之一就是您的函数:由于您要向函数传递值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

答案 1 :(得分:1)

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

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.

答案 2 :(得分:0)

您可以使用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))