Strains_longevity_data <- read.csv(file = "Summary_of_data_MASTER.csv", header = TRUE, skip = 2) library(shiny) library(ggplot2) library(dplyr) library(plotly) library(shinydashboard) library(shinydashboardPlus) library(shinyjs) # Added library for JavaScript interaction. # Data preprocessing: Convert columns to appropriate data types Strains_longevity_data$lab <- as.factor(Strains_longevity_data$Where.mice.maintained) Strains_longevity_data$Sex <- as.factor(Strains_longevity_data$Sex) Strains_longevity_data$First.author <- as.factor(Strains_longevity_data$First.Author) Strains_longevity_data$mean <- as.numeric(Strains_longevity_data$mean) Strains_longevity_data$SE <- as.numeric(Strains_longevity_data$SE) Strains_longevity_data$Maximum.lifespan <- as.numeric(Strains_longevity_data$Maximum.lifespan) Strains_longevity_data$median <- as.numeric(Strains_longevity_data$median) Strains_longevity_data$years <- as.numeric(Strains_longevity_data$Year.paper.published) Strains_longevity_data$SD <- as.numeric(Strains_longevity_data$SD) Strains_longevity_data$CV <- as.numeric(Strains_longevity_data$CV) Strains_longevity_data$Age.started..estimated.approx..days. <- as.numeric(Strains_longevity_data$Age.started..estimated.approx..days.) Strains_longevity_data$Year.paper.published <- as.numeric(Strains_longevity_data$Year.paper.published) Strains_longevity_data <- Strains_longevity_data[order(Strains_longevity_data$Strain),] # Define the Shiny UI layout ui <- fluidPage( titlePanel("Mouse Longevity Data Explorer"), fluidRow( column(width = 2, selectInput("x_axis", "X-Axis Column:", choices = colnames(Strains_longevity_data), selected = "Strain") ), column(width = 2, selectInput("y_axis", "Y-Axis Column:", choices = colnames(Strains_longevity_data), selected = "mean") ), column(width = 2, selectInput("error_bar", "Error Bar Column:", choices = c("SE", "SD", "CV")) ), column(width = 2, selectInput("plot_type", "Plot Type:", choices = c("scatter", "boxplot")) ), column(width = 2, selectInput("filter_column", "Filter Column:", choices = colnames(Strains_longevity_data), selected = "Where.mice.maintained") ), column(width = 2, selectInput("filter_value", "Filter Value:", choices = NULL) ), column(width = 2, selectInput("color_by", "Color By:", choices = colnames(Strains_longevity_data), selected = "Sex") ), column(width = 2, checkboxInput("filter_checkbox", "Turn Filtering On", value = FALSE) ), column(width = 2, selectInput("intervention_filter_column", "Intervention Filter Column:", choices = colnames(Strains_longevity_data), selected = "Intervention") ), column(width = 2, selectInput("intervention_filter_value", "Intervention Filter Value:", choices = NULL, selected = "Control") ), column(width = 2, checkboxInput("intervention_filter_checkbox", "Turn Intervention Filtering On", value = TRUE) ), column(width = 2, selectInput("shape_by", "Shape By:", choices = colnames(Strains_longevity_data), selected = "Where.mice.maintained") ), column(width = 2, actionButton("reset_button", "Reset to Defaults") ), column(width = 2, textInput("search_term", "Search Term:") ), # Add a checkbox to enable/disable the Age Cutoff (Days) filter column(width = 2, checkboxInput("age_cutoff_checkbox", "Use Age Cutoff Filter", value = FALSE) ), # Add a numeric input for age cutoff with a default value of 3650 column(width = 2, numericInput("age_cutoff", "Max age at start cutoff (Approx. Days):", value = 3650, min = 0, step = 1) ), column(width = 2, sliderInput("year_range", "Select Year Range:", min = min(Strains_longevity_data$Year.paper.published, na.rm = TRUE), max = max(Strains_longevity_data$Year.paper.published, na.rm = TRUE), value = c(min(Strains_longevity_data$Year.paper.published, na.rm = TRUE), max(Strains_longevity_data$Year.paper.published, na.rm = TRUE)), step = 1, sep = "") ) ), fluidRow( column(width = 12, textOutput("doi_url_display"), # Move this line above the plot plotlyOutput("plot") ) )) # Define the Shiny server logic # Define the Shiny server logic server <- function(input, output, session) { # Define default values for input fields default_values <- reactive({ list( x_axis = "Strain", y_axis = "mean", error_bar = "SE", plot_type = "scatter", filter_column = "Where.mice.maintained", filter_value = NULL, color_by = "Sex", filter_checkbox = FALSE, intervention_filter_column = "Intervention", intervention_filter_value = "Control", intervention_filter_checkbox = TRUE, shape_by = "Where.mice.maintained" ) }) # Reset button click event observeEvent(input$reset_button, { default <- default_values() updateSelectInput(session, "x_axis", selected = default$x_axis) updateSelectInput(session, "y_axis", selected = default$y_axis) updateSelectInput(session, "error_bar", selected = default$error_bar) updateSelectInput(session, "plot_type", selected = default$plot_type) updateSelectInput(session, "filter_column", selected = default$filter_column) updateSelectInput(session, "filter_value", selected = default$filter_value) updateSelectInput(session, "color_by", selected = default$color_by) updateCheckboxInput(session, "filter_checkbox", value = default$filter_checkbox) updateSelectInput(session, "intervention_filter_column", selected = default$intervention_filter_column) updateSelectInput(session, "intervention_filter_value", selected = default$intervention_filter_value) updateCheckboxInput(session, "intervention_filter_checkbox", value = default$intervention_filter_checkbox) updateSelectInput(session, "shape_by", selected = default$shape_by) updateNumericInput(session, "age_cutoff", value = NULL) }) observe({ # Update filter values based on selected filter column filter_column_values <- unique(Strains_longevity_data[, input$filter_column]) updateSelectInput(session, "filter_value", choices = filter_column_values) }) observe({ # Update intervention filter values based on selected intervention filter column intervention_filter_column_values <- unique(Strains_longevity_data[, input$intervention_filter_column]) updateSelectInput(session, "intervention_filter_value", choices = intervention_filter_column_values) }) filtered_data <- reactive({ filtered_data <- Strains_longevity_data if (input$filter_checkbox) { if (!is.null(input$filter_column) && input$filter_column != "" && !is.null(input$filter_value) && input$filter_value != "") { filtered_data <- filtered_data %>% filter(!!sym(input$filter_column) == input$filter_value) } } # Apply intervention filter if the checkbox is checked if (input$intervention_filter_checkbox) { if (!is.null(input$intervention_filter_column) && input$intervention_filter_column != "" && !is.null(input$intervention_filter_value) && input$intervention_filter_value != "") { filtered_data <- filtered_data %>% filter(!!sym(input$intervention_filter_column) == input$intervention_filter_value) } } # Filter the data to exclude rows with NA or blank values in x and y axes filtered_data <- filtered_data %>% filter(!is.na(!!sym(input$x_axis)) & !is.na(!!sym(input$y_axis)) & !!sym(input$x_axis) != "" & !!sym(input$y_axis) != "") filtered_data <- filtered_data %>% filter(Year.paper.published >= input$year_range[1] & Year.paper.published <= input$year_range[2]) # Add the search term filter search_term <- input$search_term if (!is.null(search_term) && search_term != "") { filtered_data <- filtered_data %>% filter(grepl(search_term, Strain) | grepl(search_term, Maternal.strain) | grepl(search_term, Paternal.strain)) } # Add the age cutoff filter if the checkbox is checked and a value is entered if (input$age_cutoff_checkbox) { age_cutoff <- input$age_cutoff if (!is.null(age_cutoff)) { filtered_data <- filtered_data %>% filter(!!sym("Age.started..estimated.approx..days.") <= age_cutoff) } } # Check the order of columns in filtered_data print(names(filtered_data)) # Select columns in the desired order select_cols <- c("DOI", colnames(filtered_data)[-which(names(filtered_data) %in% c("DOI"))]) filtered_data <- filtered_data %>% select(select_cols) filtered_data }) output$plot <- renderPlotly({ x_axis_col <- input$x_axis y_axis_col <- input$y_axis error_bar_col <- input$error_bar color_by <- input$color_by shape_by <- input$shape_by p <- ggplot(filtered_data(), aes_string(x = x_axis_col, y = y_axis_col, color = color_by, shape = shape_by)) if (input$plot_type == "scatter") { p <- p + geom_point() } else if (input$plot_type == "boxplot") { p <- p + geom_boxplot() } if (error_bar_col == "SE") { p <- p + geom_errorbar(aes_string(ymin = paste0(y_axis_col, " - SE"), ymax = paste0(y_axis_col, " + SE"))) } else if (error_bar_col == "SD") { p <- p + geom_errorbar(aes_string(ymin = paste0(y_axis_col, " - SD"), ymax = paste0(y_axis_col, " + SD"))) } else if (error_bar_col == "CV") { p <- p + geom_errorbar(aes_string(ymin = paste0(y_axis_col, " - CV"), ymax = paste0(y_axis_col, " + CV"))) } p <- p + labs(x = x_axis_col, y = y_axis_col) p <- p + theme(axis.text.x = element_text(angle = 45, hjust = 1)) unique_shapes <- seq_len(length(unique(filtered_data()[[shape_by]]))) p <- p + scale_shape_manual(values = unique_shapes) p <- ggplotly(p, key = ~DOI) # Use the DOI column as the key # Observe the plotly click event and register the event once the plot is rendered observe({ observeEvent(input$plot_click, { event_register("plot", "plotly_click") }) }) p <- p %>% layout( legend = list( orientation = "h", x = 0.5, y = -0.5, traceorder = "normal", title = list(text = "Legend"), xanchor = "center", yanchor = "top", itemsizing = "constant" ) ) p <- p %>% layout(height = 800) p }) observe({ event_data <- event_data("plotly_click") if (!is.null(event_data)) { # Extract selected values from the event data selected_strain <- event_data$x selected_mean <- event_data$y # Tolerance level for matching means tolerance <- 0.0000001 # Adjust this value based on your preference # Check if selected_strain is a valid index if (!is.na(selected_strain) && selected_strain >= 1 && selected_strain <= length(unique(filtered_data()$Strain))) { # Extract the selected strain based on the index selected_strain_value <- sort(unique(filtered_data()$Strain))[selected_strain] # Filter the data based on the selected values clicked_point_data <- Strains_longevity_data %>% filter(Strain == selected_strain_value) # Find the row with the closest mean value to the clicked point closest_rows <- clicked_point_data[which.min(abs(clicked_point_data$mean - selected_mean)), ] # If there are multiple rows with the same closest mean, prioritize based on "Sex" and "Where.mice.maintained." if (nrow(closest_rows) > 1) { # Prioritize based on "Sex" closest_rows <- closest_rows[which.min(abs(closest_rows$Sex - unique(closest_rows$Sex))), ] # If there are still multiple rows with the same "Sex," prioritize based on "Where.mice.maintained." if (nrow(closest_rows) > 1) { closest_rows <- closest_rows[which.min(abs(closest_rows$Where.mice.maintained - unique(closest_rows$Where.mice.maintained))), ] } } # Extract the DOI from the closest row doi <- closest_rows$DOI # Check if DOI is non-empty before rendering if (!is.null(doi) && length(doi) > 0) { # Return the DOI as text to be rendered output$doi_url_display <- renderText({ paste("Clicked point link:\n", doi) }) } else { output$doi_url_display <- renderText({ "DOI not found for the clicked point." }) } } else { # Use the first index as a fallback selected_strain_value <- sort(unique(filtered_data()$Strain))[1] clicked_point_data <- Strains_longevity_data %>% filter(Strain == selected_strain_value) # Find the row with the closest mean value to the clicked point closest_rows <- clicked_point_data[which.min(abs(clicked_point_data$mean - selected_mean)), ] # If there are multiple rows with the same closest mean, prioritize based on "Sex" and "Where.mice.maintained." if (nrow(closest_rows) > 1) { # Prioritize based on "Sex" closest_rows <- closest_rows[which.min(abs(closest_rows$Sex - unique(closest_rows$Sex))), ] # If there are still multiple rows with the same "Sex," prioritize based on "Where.mice.maintained." if (nrow(closest_rows) > 1) { closest_rows <- closest_rows[which.min(abs(closest_rows$Where.mice.maintained - unique(closest_rows$Where.mice.maintained))), ] } } # Extract the DOI from the closest row doi <- closest_rows$DOI # Check if DOI is non-empty before rendering if (!is.null(doi) && length(doi) > 0) { # Return the DOI as text to be rendered output$doi_url_display <- renderText({ paste("Clicked point link:\n", doi) }) } else { output$doi_url_display <- renderText({ "DOI not found for the clicked point." }) } } } }) } body = dashboardBody(ui) footer = dashboardFooter( left = a( href = "https://git.genenetwork.org/mouse-longevity-app/", target = "_blank", "source code" ), right = "2024 | David Ashbrook" ) # Run the Shiny app shinyApp( ui = dashboardPage( title = "Mouse Longevity Explorer", header = dashboardHeader(disable=TRUE), sidebar = dashboardSidebar(disable=TRUE), body = body, footer = footer, skin = "black", ), server = server )