edunford / tidysynth

A tidy implementation of the synthetic control method in R
Other
98 stars 14 forks source link

Dynamically adding predictors #16

Closed grj336 closed 2 years ago

grj336 commented 2 years ago

I am trying to create a shiny dashboard for an SCM implementation and this requires dynamically adding lag variables. I am running into the problem that I can't dynamically specify the name of the predictor, ie the cigsale_1975 in the example, since it is not a string. Is there a way of doing this, even just under the hood for the time being? It would also be really useful to pass in an array of values.

Edit: For clarification, I can't pass in a variable with a string, ie,

lagVarName = "cigsale_1975"

generate_predictor(time_window = 1975, lagVarName  = 1975)
grj336 commented 2 years ago

I have devised a work around for this. This requires editing the extract_predictor function within the generate_predictor function.

I have included this code below. The additions are, first in creating a name for the lag. This is not quite generic, but for my purpose, it takes the name of the lag variable value (ie the column name) and appends the time window specified to it. The remainder of the function runs until after the summarise, where the default name is replaced by the one specified earlier.

    # INTERNAL FUNCTIONS
    extract_predictor <- function(data,type = "treated",...){
      # Aux. function for processing predictors. Takes in quosure statement,
      # passes it internally to summarize, and organizes data to behave
      # accordingly as a synth_method input.

      name <- paste0(deparse(substitute(...)), "_", time_window) # Creates name for lag var.

      data %>%
        dplyr::filter(.type==type) %>%
        dplyr::select(.original_data) %>%
        tibble::as_tibble() %>%
        tidyr::unnest(cols=c(.original_data)) %>%

        # Only relevant time units (as specified by the window)
        dplyr::filter(.data[[time_index]] %in% time_window) %>%

        # Group by the unit axis
        dplyr::group_by(.data[[unit_index]]) %>%
        dplyr::summarize(...,.groups='drop') %>%
        rename(!!name := limports) %>% # Renames col

        # Convert to the relevant format
        tidyr::gather(variable,value,-.data[[unit_index]]) %>%
        tidyr::spread(.data[[unit_index]],value)
    }
edunford commented 2 years ago

Hi @grj336, thanks for using the package! There is nothing in tidysynth that doesn't comply with the broader quosure logic that underpins the tidyverse. So to generate function that dynamically assigns the a name to provided object/arg containing a variable name string is straightforward using rlang. Specifically, the function you'll be interested in for your application is: rlang::sym().

Here is a function that would do what you propose that fits within the current design of the package.

dynamic_generate_predictor <- function(.data, variable, time_period = NA){
  var_name = rlang::sym(paste(variable,time_period,sep="_"))
  var = rlang::sym(variable)
  .data %>% 
    generate_predictor(time_window = time_period,
                       !!var_name := mean(!!var, na.rm = T))
}

In application, this would look like:

#initialize the first part of the SC pipeline
obj <- 
  smoking %>%

  # initial the synthetic control object
  synthetic_control(outcome = cigsale, # outcome
                    unit = state, # unit index in the panel data
                    time = year, # time index in the panel data
                    i_unit = "California", # unit where the intervention occurred
                    i_time = 1988, # time period when the intervention occurred
                    generate_placebos=T # generate placebo synthetic controls (for inference)
  )

# Information supplied via your application 
variable_name_supplied_through_shiny_app = "lnincome"
time_period_supplied_through_shiny_app = 1975

# Apply
obj %>% 

  dynamic_generate_predictor(
    variable = variable_name_supplied_through_shiny_app, 
    time_period = time_period_supplied_through_shiny_app
  ) %>% 

  grab_predictors()

I hope that helps unblock you! Best of luck with your shiny application.