I have written a function to calculate the mean values from monthly data within a year (starting in the previous Dec to the current Sep). Each dataframe (and there are many) that I'm feeding through this function is presently named for the variable that I'm interested in. But I want the output to use the name of the dataframe as the variable.
Example:
year <- c(2000:2020)
`1` <- runif(21, 0,10)
`2` <- runif(21, 0,10)
`3` <- runif(21, 0,10)
`4` <- runif(21, 0,10)
`5` <- runif(21, 0,10)
`6` <- runif(21, 0,10)
`7` <- runif(21, 0,10)
`8` <- runif(21, 0,10)
`9` <- runif(21, 0,10)
`10` <- runif(21, 0,10)
`11` <- runif(21, 0,10)
`12` <- runif(21, 0,10)
dogs <- as.data.frame(cbind(year, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`))
fun_annual <- function(df) {
name = deparse(substitute(df)) #I know I can use this to get the name of the dataframe
df %>%
mutate(lag12 = lag(`12`)) %>%
rowwise() %>% # complete next calculations row by row
mutate(name = mean(c_across(c(`1`:`9`, lag12)))) %>% #mean from prev Dec to Sep
select(year, name) #by this point 'name' should be 'dogs' (name of original dataframe
}
# Final function should return a dataframe with 2 variables: year and dogs
Bonus points if someone can also help be figure out how to put the months in the function, so I don't have to change the months within the function each time! Especially if I use more months from the previous year (beyond Dec)
CodePudding user response:
Based on Pass a string as variable name in dplyr::mutate, you can use rlang
's !!
, sym()
, and :=
.
sym
takes the value from name
and turn it into a symbol. !!
makes sure that sym
takes effect before R evaluate the code. And, as there is no sym()<-
function, we can't use =
to modify the left hand side, and need to use the glue
package syntax with :=
.
Also, you can use dplyr::all_of
to get the columns for the months.
fun_annual <- function(df, months) {
name = deparse(substitute(df))
df %>%
mutate(lag12 = lag(`12`)) %>%
rowwise() %>%
mutate(!!sym(name) := mean(c_across(c(all_of(months), lag12)))) %>%
select(year, !!sym(name))
}
> fun_annual(dogs, paste(1:9))
# A tibble: 21 × 2
# Rowwise:
year dogs
<dbl> <dbl>
1 2000 NA
2 2001 4.33
3 2002 5.67
4 2003 3.68
5 2004 4.07
6 2005 5.52
7 2006 5.98
8 2007 6.34
9 2008 6.18
10 2009 3.66
# … with 11 more rows
CodePudding user response:
Similar, but slightly different:
library(tidyverse)
fun_annual <- function(df) {
name <- quo_name(enquo(df))
df |>
mutate(lag12 = lag(`12`)) |>
rowwise() |>
mutate("{name}" := mean(c_across(c(`1`:`9`, lag12)))) |>
select(year, !!name)
}
fun_annual(dogs)
#> # A tibble: 21 x 2
#> # Rowwise:
#> year dogs
#> <dbl> <dbl>
#> 1 2000 NA
#> 2 2001 5.58
#> 3 2002 5.72
#> 4 2003 3.89
#> 5 2004 4.77
#> 6 2005 3.98
#> 7 2006 5.98
#> 8 2007 4.61
#> 9 2008 5.23
#> 10 2009 5.91
#> # ... with 11 more rows
EDIT
bonus for months:
fun_annual <- function(df, months) {
name <- quo_name(enquo(df))
df |>
mutate(lag12 = lag(`12`)) |>
rowwise() |>
mutate("{name}" := mean(c_across(c({{months}}, lag12)))) |>
select(year, !!name)
}
fun_annual(dogs, months = c(`1`, `2`))
#> # A tibble: 21 x 2
#> # Rowwise:
#> year dogs
#> <dbl> <dbl>
#> 1 2000 NA
#> 2 2001 4.76
#> 3 2002 4.74
#> 4 2003 5.35
#> 5 2004 8.11
#> 6 2005 2.26
#> 7 2006 1.90
#> 8 2007 5.75
#> 9 2008 8.35
#> 10 2009 4.81
#> # ... with 11 more rows
fun_annual(dogs, months = c(`1`, `4`, `5`))
#> # A tibble: 21 x 2
#> # Rowwise:
#> year dogs
#> <dbl> <dbl>
#> 1 2000 NA
#> 2 2001 3.76
#> 3 2002 5.92
#> 4 2003 3.66
#> 5 2004 5.78
#> 6 2005 4.67
#> 7 2006 3.53
#> 8 2007 5.31
#> 9 2008 7.15
#> 10 2009 5.02
#> # ... with 11 more rows