GabriellaS-K / Shinyapp

my app
0 stars 0 forks source link

feedback and ideas #1

Open stefanoanzani opened 1 year ago

stefanoanzani commented 1 year ago

Hi Gabriella! I've tested the app and looked at the code behind it. I see what you mean with the repetition, I have some ideas that might help with some of the copy-paste and repeated code! I'll focus on the first boxplot/dataset, as the suggestions should work for all. Also, these are not really shiny-related tips: to be honest I'm not expert at all in shiny, and there might be better ways to do the same thigs.

First of all, looking at the render_plot() call for the boxplot I don't think that you are using anything from the model fit in there, so you could remove those lines. This might also help with performance, as you are computing the model fit twice every time you select a new marker in the dropdown.

A lot of the code to compute and fit the model stays the same, so it's a good candidate to be converted into a custom built function.

Here is your code:

olink_plot_data <- olink_data %>%
  select(timepoint, ID, !!sym(column_name)) %>%
  filter(!is.na(.[[column_name]]) & is.finite(.[[column_name]]))  # Filter the data, removing rows with NA or infinite values

# Perform linear mixed-effects regression and calculate ANOVA
lmer_model <- lmer(formula = as.formula(paste(column_name, "~ timepoint + (1 | ID)")), data = olink_plot_data)
anova_result <- anova(lmer_model, type = "III")
p_value <- formatC(anova_result[["Pr(>F)"]][1], format = "f", digits = 5)  # Format the p-value
p_value_display <- ifelse(as.numeric(p_value) < 0.05, paste0(p_value, "*"), p_value)  # Add asterisk if p-value is significant

paste("Repeated Measures ANOVA p-value:", p_value_display)

And here is a function that takes as inputs the dataframe of interest and the column_name that you want to use as DV as a string:

compute_model <- function(df, column_name) {

  filtered_df <- df %>%
    select(timepoint, ID, !!sym(column_name)) %>%
    filter(!is.na(.[[column_name]]) & is.finite(.[[column_name]]))  # Filter the data, removing rows with NA or infinite values

  # Perform linear mixed-effects regression and calculate ANOVA
  lmer_model <- lmer(formula = as.formula(paste(column_name, "~ timepoint + (1 | ID)")), data = filtered_df)
  anova_result <- anova(lmer_model, type = "III")
  p_value <- formatC(anova_result[["Pr(>F)"]][1], format = "f", digits = 5)  # Format the p-value
  p_value_display <- ifelse(as.numeric(p_value) < 0.05, paste0(p_value, "*"), p_value)  # Add asterisk if p-value is significant

  textual_result <- paste("Repeated Measures ANOVA p-value:", p_value_display)

  ## this put all the things in a list in case you might need it,
  # but if you only need the textual label, you can simply
  # return textual_result
  results <- list(
    lmer_model = lmer_model,
    anova_result = anova_result,
    p_value = p_value,
    p_value_display = p_value_display,
    textual_result = textual_result
  )

  return(results)

}

This way you can reduce the amount of code that you see inside the textual rendering function to a compact

selected_marker <- input$olink_select
selected_column_name <- paste0(selected_marker, "_log10")

model_stuff <- compute_model(olink_data, column_name = selected_column_name)

return(model_stuff$textual_result)

The same applies to the plotting code, that could become

plot_boxplot <- function(df, column_name) {

  ggplot(data = df, aes(x = timepoint, y = .data[[column_name]], color = timepoint)) +
    geom_boxplot() +
    labs(x = "timepoint", y = column_name) +
    theme_classic()
}

A more shiny related suggestion is to look into reactivity and reactives, which might help with the preprocessing steps that you take, for example the column selection and the filtering of missing data. Reactives are executed only when the input changes, and than you can use them in multiple places.

GabriellaS-K commented 1 year ago

Hi,

Thank you! This works really nicely. I've updated the code to reflect this, and will read into your suggestion on reactivity and reactives!