library(shiny) library(shinyjs) library(bslib) library(dplyr) library(ggplot2) library(tm) library(SnowballC) library(plotly) library(text2vec) library(tokenizers) library(dplyr) library(tidyr) library(igraph) library(ggraph) library(topicmodels) library(wordcloud) library(wordcloud2) library(reshape2) library(SnowballC) library(RColorBrewer) library(syuzhet) library(cluster) library(tidytext) library(word2vec) library(Rtsne) library(umap) library(MASS) library(koRpus) library(openxlsx) library(tools) library(shinyWidgets) library(reticulate) library(keras) library(tensorflow) library(neuralnet) library(rsample) library(tfdatasets) library(statnet) library(UserNetR) library(visNetwork) library(networkD3) library(ergm) library(ergm.count) library(network) library(tidyverse) options(width = 150) options(digits = 4, scipen = 1000000000) options(shiny.maxRequestSize=30*1024^2) ui <- fluidPage( theme = bs_theme(version = 5, bootswatch = "spacelab"), useShinyjs(), # Initialize shinyjs titlePanel("PtteM Data Science"), tags$head(tags$link(rel = "stylesheet", href="https://fonts.googleapis.com/css?family=Montserrat:100,300,400,700&display=swap"), tags$style(HTML(" body, h1, h2, h3, h4, h5, h6, .nav, p, a, .shiny-input-container { font-family: 'Montserrat'; /* Font type for the title attribute */ font-weight: 385; color: #007c9e !important; } * { font-family: 'Montserrat', sans-serif; font-weight: 385; color: #195576; /* Blue color */ } body { background-color: #f7f7f7; /* Light gray background */ } .icon-btn { border: 1px solid #0d6efd; /* Example border: solid, 2 pixels, #555 color */ border-radius: 15%; /* Circular border */ color: #00969e; /* Icon color */ font-family: 'Montserrat'; /* Font type for the title attribute */ font-weight: 385; background-color: #f7f7f7; padding: 125px; /* Space around the icon */ margin: 25px; /* Space around the button */ font-size: 24px; /* Icon size */ box-shadow: 0 2px 4px rgba(0,0,0,0.2); } .icon-btn:hover { color: #00969e; /* Icon color on hover */ border-color: #007c9e; background-color: #ebfbfd;/* Border color on hover */ } /* Add custom styles here */ .shiny-input-container { margin-bottom: 15px; } .box { border: 1px solid #ddd; padding: 20px; border-radius: 50px; margin-bottom: 200px; gap: 200px; align-items: center; } #statsTable_wrapper { margin: 0 auto; } .shiny-output-error { border: 1px solid #FF0000; /* Red border on error */ } /* If you want to change the font size of the tooltip, you can add custom CSS for the 'title' attribute's default styling. */ "))), tags$head( # Include JavaScript to reload the page tags$script(HTML(" document.addEventListener('DOMContentLoaded', function() { document.getElementById('myElement').style.color = '#0d6efd'; // Change to your desired color }); ")) ), tags$head( tags$script(HTML(" function reloadPage() { window.location.reload(); } ")) ), # Refresh button that calls the JavaScript function actionButton("refresh", "Refresh Analysis", onclick = "reloadPage();"), # Help Text or Information for the user helpText("Bu uygulama ile pekiştirmeli öğrenme başlığı altındaki veri bilimi fonksiyonlarına erişebilirsiniz."), #Reinforcement Learning h2("Reinforcement Learning Section"), tabsetPanel( tabPanel("Multi-Armed Bandit (MAB) Algorithms", tabsetPanel( tabPanel("Epsilon-Greedy", sidebarLayout( sidebarPanel( fileInput('epsgreefile', 'Upload Data File', accept = c(".csv", ".xlsx", ".xls")), uiOutput('selectActionColumn'), uiOutput('selectRewardColumn'), numericInput("epsilon", "Epsilon (Exploration Rate)", value = 0.1, min = 0, max = 1, step = 0.01), actionButton("runMAB", "Run MAB Decision"), ), mainPanel( tableOutput("actionResults"), textOutput("selectedAction"), ) ) ), tabPanel("Upper Confidence Bound (UCB)", sidebarLayout( sidebarPanel( fileInput("ucbfile", "Upload Data File", accept = c(".csv", ".xlsx", ".xls")), uiOutput("selectStrategyColucb"), uiOutput("selectRewardColucb"), numericInput("epsilon", "Epsilon (Exploration Rate)", value = 0.1, min = 0, max = 1, step = 0.01), actionButton("runMABucb", "Run MAB Decision") ), mainPanel( tabsetPanel( tabPanel("Results", tableOutput("actionResultsucb")), tabPanel("Reward History", plotlyOutput("rewardHistoryucb", width = "100%", height = "750px")) ) ) ) ), tabPanel("Thompson Sampling", sidebarLayout( sidebarPanel( fileInput("tsfile", "Upload Data File", accept = c(".csv", ".xlsx", ".xls")), uiOutput("selectStrategyColumnTS"), # This should be in the UI part uiOutput("selectRewardColumnTS"), textInput("successConditionsTS", "Enter success conditions (comma separated)"), actionButton("runMABTS", "Run MAB Thompson Sampling") ), mainPanel( tabsetPanel( tabPanel("Best Display", verbatimTextOutput("bestStrategyDisplayTS")), tabPanel("Reward History TS", plotlyOutput("rewardHistoryTS", width = "100%", height = "750px")), tabPanel("Strategy Frequency", plotlyOutput("strFrePlotTS", width = "100%", height = "750px")) ) ) ) ), ) ), tabPanel("Contextual Bandits", tabsetPanel( tabPanel("Linear Regression UCB", sidebarLayout( sidebarPanel( fileInput("lrucbfile", "Upload your dataset", accept = c(".csv", ".xlsx", ".xls")), actionButton("loadlrucb", "Load Data"), selectInput("targetlrucb", "Select Target Column", choices = NULL), selectizeInput("independentVarlrucb", "Select Independent Variable", choices = NULL, multiple = FALSE), actionButton("runModellrucb", "Run Linear Regression UCB") ), mainPanel( tabsetPanel( tabPanel("Model Summary", verbatimTextOutput("modelSummarylrucb")), tabPanel("Diagnostics", plotlyOutput("resFitlrucbPlot"), plotlyOutput("qqPlotlrucb"), plotlyOutput("scaleLoclrucbPlot"), plotlyOutput("resLevlrucbPlot") ), tabPanel("Regression Plot", plotlyOutput("regressionPlot", width = "100%", height = "750px")) ) ) ) ) ) ) ) ) server <- function(input, output, session) { ##Reinforcement Learning ###Multi-Armed Bandit (MAB) Algorithms ####Epsilon-Greedy # Reactive to store uploaded data uploadedepsgree <- reactive({ req(input$epsgreefile) ext <- tools::file_ext(input$epsgreefile$name) switch(ext, csv = read.csv(input$epsgreefile$datapath, stringsAsFactors = FALSE), xlsx = readxl::read_excel(input$epsgreefile$datapath), stop("Unsupported file type")) }) # Dynamic UI to select the action and reward columns output$selectActionColumn <- renderUI({ df <- uploadedepsgree() req(df) selectInput("actionColumn", "Select Action Column", choices = names(df)) }) output$selectRewardColumn <- renderUI({ df <- uploadedepsgree() req(df) selectInput("rewardColumn", "Select Reward Column", choices = names(df)) }) # Reactive values to store actions and performance metrics actionRewards <- reactiveValues(actions = NULL, rewards = NULL, counts = NULL) observeEvent(input$runMAB, { req(input$epsilon, input$actionColumn, input$rewardColumn) df <- uploadedepsgree() df <- df %>% dplyr::select(all_of(input$actionColumn), all_of(input$rewardColumn)) %>% na.omit() uniqueActions <- unique(df[[input$actionColumn]]) summedRewards <- tapply(df[[input$rewardColumn]], df[[input$actionColumn]], sum, na.rm = TRUE) # Store processed data in actionRewards actionRewards$actions <- uniqueActions actionRewards$rewards <- summedRewards actionRewards$counts <- rep(10, length(uniqueActions)) # Initialize with some counts # Run epsilon-greedy algorithm if (runif(1) < input$epsilon) { # Exploration selectedActionIndex <- sample(length(uniqueActions), 1) } else { # Exploitation averages <- summedRewards / actionRewards$counts selectedActionIndex <- which.max(averages) } selectedAction <- uniqueActions[selectedActionIndex] simulatedReward <- runif(1, min = 0, max = max(summedRewards)) # Simulate a reward actionRewards$rewards[selectedActionIndex] <- actionRewards$rewards[selectedActionIndex] + simulatedReward actionRewards$counts[selectedActionIndex] <- actionRewards$counts[selectedActionIndex] + 1 # Update UI output$selectedAction <- renderText({ selectedAction }) output$actionResults <- renderTable({ data.frame(Action = actionRewards$actions, Reward = actionRewards$rewards, Count = actionRewards$counts) }) }) ####Upper Confidence Bound (UCB) # Reactive values to store actions and performance metrics actionData <- reactiveValues( actions = NULL, counts = NULL, rewards = NULL, history = list() # Initialize the history list here ) uploadeducb <- reactive({ req(input$ucbfile) ext <- tools::file_ext(input$ucbfile$name) switch(ext, csv = read.csv(input$ucbfile$datapath, stringsAsFactors = FALSE), xlsx = readxl::read_excel(input$ucbfile$datapath), xls = readxl::read_excel(input$ucbfile$datapath), stop("Unsupported file type") ) }) output$selectStrategyColucb <- renderUI({ req(input$ucbfile) data <- uploadeducb() selectInput("strategyColumn", "Select Strategy Column", choices = names(data), selected = names(data)[1]) }) output$selectRewardColucb <- renderUI({ req(input$ucbfile) data <- uploadeducb() selectInput("rewardColumn", "Select Reward Column", choices = names(data), selected = names(data)[2]) }) observeEvent(input$runMABucb, { data <- uploadeducb() strategyColumn <- input$strategyColumn rewardColumn <- input$rewardColumn # Ensure numerical data for computation data[[rewardColumn]] <- as.numeric(as.character(data[[rewardColumn]])) # Unique strategies strategies <- unique(data[[strategyColumn]]) n <- nrow(data) # Total number of events if (is.null(actionData$actions)) { actionData$actions <- strategies actionData$counts <- rep(0, length(strategies)) actionData$rewards <- rep(0, length(strategies)) # Initialize history for each strategy actionData$history <- setNames(vector("list", length(strategies)), strategies) for(strategy in strategies) { actionData$history[[strategy]] <- data.frame(Time = integer(), Reward = numeric()) } } # Loop over strategies to compute UCB and update history for (strategy in strategies) { strategy_data <- data[data[[strategyColumn]] == strategy, ] ni <- nrow(strategy_data) # Number of times this strategy was tried avg_reward <- mean(strategy_data[[rewardColumn]], na.rm = TRUE) if (ni > 0) { ucb_value <- avg_reward + sqrt((2 * log(n)) / ni) } else { ucb_value <- Inf # Encourage exploration of untried strategies } # Define strategy_index here, inside the loop, before you use it strategy_index <- which(actionData$actions == strategy) actionData$counts[strategy_index] <- actionData$counts[strategy_index] + ni actionData$rewards[strategy_index] <- ucb_value actionData$history[[strategy]] <- rbind( actionData$history[[strategy]], data.frame(Time = as.integer(actionData$counts[strategy_index]), Reward = ucb_value) ) } # After the loop, the history should be updated }) output$actionResultsucb <- renderTable({ req(actionData$actions) # Ensure the action data is not NULL data.frame( Strategy = actionData$actions, Counts = actionData$counts, Rewards = actionData$rewards ) }) # Create the plot in a separate reactive context, responding to changes in actionData output$rewardHistoryucb <- renderPlotly({ req(actionData$history) # Ensure that the history is not NULL or empty # Create plot_data for plotting plot_data <- do.call(rbind, Filter(Negate(is.null), lapply(names(actionData$history), function(strategy) { history_data <- actionData$history[[strategy]] if(nrow(history_data) > 0) { return(data.frame(Strategy = strategy, Time = history_data$Time, Reward = history_data$Reward)) } }))) # Only attempt to create a plot if plot_data is a data frame and not NULL if(!is.null(plot_data) && is.data.frame(plot_data)) { # Create a custom color palette with as many colors as there are strategies colors <- grDevices::colorRampPalette(RColorBrewer::brewer.pal(8, "Set2"))(length(unique(plot_data$Strategy))) plot_ly(plot_data, x = ~Time, y = ~Reward, color = ~Strategy, colors = colors, type = 'scatter', mode = 'lines+markers') %>% layout(title = "Reward History Over Time", xaxis = list(title = "Time"), yaxis = list(title = "Reward")) } else { plot_ly() %>% add_annotations(text = "No data available for plot", x = 0.5, y = 0.5, showarrow = FALSE, xref = "paper", yref = "paper") } }) ####Thompson Sampling uploadedTS <- reactive({ req(input$tsfile) ext <- tools::file_ext(input$tsfile$name) switch(ext, csv = read.csv(input$tsfile$datapath, stringsAsFactors = FALSE), xlsx = readxl::read_excel(input$tsfile$datapath), xls = readxl::read_excel(input$tsfile$datapath), stop("Unsupported file type: ", ext) ) }) # Initialize actionDataTS similar to actionData for UCB actionDataTS <- reactiveValues( actions = NULL, successes = NULL, failures = NULL, theta = NULL ) output$selectStrategyColumnTS <- renderUI({ req(input$tsfile) data <- uploadedTS() selectInput("selectStrategyColumnTS", "Select Strategy Column", choices = names(data), selected = names(data)[1]) }) output$selectRewardColumnTS <- renderUI({ req(input$tsfile) data <- uploadedTS() selectInput("selectRewardColumnTS", "Select Reward Column", choices = names(data), selected = names(data)[2]) }) observeEvent(input$runMABTS, { req(input$tsfile) data <- uploadedTS() strategyColumn <- input$selectStrategyColumnTS rewardColumn <- input$selectRewardColumnTS success_conditions <- unlist(strsplit(input$successConditionsTS, ",")) # split the input string into a vector if (is.null(actionDataTS$actions)) { actionDataTS$actions <- unique(data[[strategyColumn]]) actionDataTS$successes <- rep(1, length(actionDataTS$actions)) # Start with 1 success for beta distribution actionDataTS$failures <- rep(1, length(actionDataTS$actions)) # Start with 1 failure for beta distribution actionDataTS$theta <- rep(0, length(actionDataTS$actions)) actionDataTS$history <- list() # Initialize history for plotting actionDataTS$selectionCount <- rep(0, length(actionDataTS$actions)) # Initialize with zeros } for (strategy in actionDataTS$actions) { strategy_index <- which(actionDataTS$actions == strategy) # Define strategy_data and num_successes/num_failures after defining strategy_index strategy_data <- data[data[[strategyColumn]] == strategy, ] strategy_data[[rewardColumn]] <- as.numeric(strategy_data[[rewardColumn]] %in% success_conditions) # Convert to binary num_successes <- sum(strategy_data[[rewardColumn]], na.rm = TRUE) num_failures <- nrow(strategy_data) - num_successes actionDataTS$successes[strategy_index] <- actionDataTS$successes[strategy_index] + num_successes actionDataTS$failures[strategy_index] <- actionDataTS$failures[strategy_index] + num_failures actionDataTS$theta[strategy_index] <- rbeta(1, actionDataTS$successes[strategy_index], actionDataTS$failures[strategy_index]) actionDataTS$selectionCount[strategy_index] <- actionDataTS$selectionCount[strategy_index] + 1 # Update the history with the new data for plotting if (is.null(actionDataTS$history[[strategy]])) { actionDataTS$history[[strategy]] <- data.frame(Time = seq_along(strategy_data[[rewardColumn]]), Reward = strategy_data[[rewardColumn]]) } else { actionDataTS$history[[strategy]] <- rbind(actionDataTS$history[[strategy]], data.frame(Time = seq_along(strategy_data[[rewardColumn]]) + nrow(actionDataTS$history[[strategy]]), Reward = strategy_data[[rewardColumn]])) } } }) output$bestStrategyDisplayTS <- renderPrint({ req(actionDataTS$theta) # Ensure theta values have been calculated best_strategy_index <- which.max(actionDataTS$theta) best_strategy <- actionDataTS$actions[best_strategy_index] # Ensure this variable matches throughout your code theta_value <- actionDataTS$theta[best_strategy_index] cat("The best strategy is:", best_strategy, "\n", "Estimated success probability (theta):", theta_value) }) createPlotData <- function(history) { if (is.list(history) && length(history) > 0 && all(sapply(history, is.data.frame))) { # Combine all strategy data frames into one, adding a 'Strategy' column plot_data <- do.call(rbind, lapply(names(history), function(strategy) { strategy_data <- history[[strategy]] if ("Time" %in% names(strategy_data) && "Reward" %in% names(strategy_data)) { strategy_data$Strategy <- strategy # Add the 'Strategy' column return(strategy_data) } else { # Return a data frame with missing values if required columns are not present return(data.frame(Time = NA, Reward = NA, Strategy = strategy)) } })) # Remove rows with missing values plot_data <- na.omit(plot_data) return(plot_data) } else { # If history is not as expected, return an empty data frame with the correct columns return(data.frame(Time = integer(0), Reward = numeric(0), Strategy = factor())) } } output$rewardHistoryTS <- renderPlotly({ req(actionDataTS$history) # Ensure that the history is not NULL or empty # Create the required plot_data using the createPlotData function plot_data <- createPlotData(actionDataTS$history) # Check if the plot_data has rows to plot if (!is.null(plot_data) && nrow(plot_data) > 0) { # Create the plot with plotly plot_ly( data = plot_data, x = ~Time, y = ~Reward, color = ~Strategy, type = 'scatter', # Use 'scatter' for line and point plots mode = 'markers', # Combine line and marker styles marker = list(color = 'rgba(74, 93, 191, 0.7))', line = list(color = 'rgba(55, 128, 191, 1.0)', width = 2)) # Optionally, adjust marker size ) %>% layout( title = "Reward History Over Time", xaxis = list(title = "Time"), yaxis = list(title = "Reward"), hovermode = 'closest' # Improves tooltip behavior ) } else { # Provide a message or an empty plot if plot_data is empty return(plotly_empty()) } }) output$strFrePlotTS <- renderPlotly({ req(input$tsfile) # Make sure a file is uploaded data <- uploadedTS() # Get the uploaded data # Calculate the frequency of each strategy strategy_freq <- table(data[[input$selectStrategyColumnTS]]) # Convert to a data frame for plotting strategy_freq_df <- as.data.frame(strategy_freq) # Create the plot with plotly plot_ly( data = strategy_freq_df, x = ~Var1, # The strategy names y = ~Freq, # The frequency counts type = 'scatter', # Use a scatter chart mode = 'markers', marker = list(color = 'rgba(74, 93, 191, 0.7)', line = list(color = 'rgba(55, 128, 191, 1.0)', width = 2)) ) %>% layout( title = "Strategy Frequency", xaxis = list(title = "Strategy"), yaxis = list(title = "Frequency"), hovermode = 'closest' ) }) ####Linear Regression UCB # Reactive value to store preprocessed data datalrucb <- reactiveVal(NULL) modelsList <- reactiveVal(list()) # Function to read and clean data read_data <- function(filepath) { ext <- tools::file_ext(filepath) if (ext == "csv") { read.csv(filepath, stringsAsFactors = FALSE) } else if (ext == "xlsx") { readxl::read_excel(filepath) } else { stop("Invalid file format. Please select a CSV or XLSX file.") } } clean_column_names <- function(dataframe) { colnames(dataframe) <- gsub("[^[:alnum:]_]", "", make.names(colnames(dataframe), unique = TRUE)) return(dataframe) } # Observe load data button click observeEvent(input$loadlrucb, { file <- input$lrucbfile req(file) data_lrucb <- read_data(file$datapath) data_lrucb <- clean_column_names(data_lrucb) datalrucb(data_lrucb) # Store the data updateSelectInput(session, "targetlrucb", choices = colnames(data_lrucb)) updateSelectizeInput(session, "independentVarlrucb", choices = setdiff(colnames(data_lrucb), input$targetlrucb)) }) # Function to perform safe regression safe_regression <- function(data, responseVar, var) { tryCatch({ # Print types to debug #print(paste("Type of response variable:", class(data[[responseVar]]))) #print(paste("Type of independent variable:", class(data[[var]]))) #if(!is.numeric(data[[var]])) { #stop(paste(var, "is not numeric and will be skipped.")) #} #if(!is.numeric(data[[responseVar]])) { #stop(paste(responseVar, "is not numeric and will be skipped.")) #} # Check for missing or infinite values #if(any(!is.finite(data[[var]])) || any(!is.finite(data[[responseVar]]))) { #stop("Non-finite values detected in variables.") #} # Assuming 'data' is your data frame and 'responseVar' and 'var' are column names data <- na.omit(data[c(responseVar, var)]) formula <- as.formula(paste(responseVar, "~", var)) model <- lm(formula, data = data) summary_model <- summary(model) ucb_estimate <- summary_model$coefficients[var, "Estimate"] # Store the entire model object along with variable names return(list(variable = var, model = model, ucb_estimate = ucb_estimate)) }, error = function(e) { message("Error in regression with variable: ", var, "; Error: ", e$message) return(NULL) # Skip this variable }) } # Perform regression on Run Model button click observeEvent(input$runModellrucb, { data <- req(datalrucb()) responseVar <- req(input$targetlrucb) var <- req(input$independentVarlrucb) if(!responseVar %in% names(data)) { stop("Selected response variable is not in the dataset.") } results <- safe_regression(data, responseVar, var) modelsList(list(results)) # Append new result to the list output$modelSummarylrucb <- renderPrint({ req(results) if (is.null(results$ucb_estimate)) { paste("Regression could not be performed for variable:", var) } else { paste("UCB estimate for variable", var, "is", results$ucb_estimate) } }) }) # Server function to create diagnostic plots output$resFitlrucbPlot <- renderPlotly({ req(modelsList(), datalrucb()) models <- req(modelsList()) # Use the first model for the plot as an example if (length(models) >= 1 && !is.null(models[[1]]$model)) { fitted_model <- models[[1]]$model p <- ggplot(fitted_model, aes(.fitted, .resid)) + geom_point(color = "darkorange") + geom_smooth(method = "lm", se = FALSE, color = "dodgerblue") + labs(title = "Residuals vs Fitted", x = "Fitted Values", y = "Residuals") ggplotly(p) } else { return(NULL) # No models to plot } }) output$qqPlotlrucb <- renderPlotly({ req(modelsList(), datalrucb()) models <- req(modelsList()) # Use the first model for the plot as an example if (length(models) >= 1 && !is.null(models[[1]]$model)) { fitted_model <- models[[1]]$model p <- ggplot(fitted_model, aes(sample = .stdresid)) + stat_qq(color = "darkorange") + stat_qq_line(color = "dodgerblue") + labs(title = "Normal Q-Q") ggplotly(p) } else { return(NULL) # No models to plot } }) output$scaleLoclrucbPlot <- renderPlotly({ req(modelsList(), datalrucb()) models <- req(modelsList()) # Use the first model for the plot as an example if (length(models) >= 1 && !is.null(models[[1]]$model)) { fitted_model <- models[[1]]$model p <- ggplot(fitted_model, aes(.fitted, sqrt(abs(.resid)))) + geom_point(color = "darkorange") + geom_smooth(method = "lm", se = FALSE, color = "dodgerblue") + labs(title = "Scale-Location", x = "Fitted Values", y = "Sqrt(|Residuals|)") ggplotly(p) } else { return(NULL) # No models to plot } }) output$resLevlrucbPlot <- renderPlotly({ req(modelsList(), datalrucb()) models <- req(modelsList()) # Use the first model for the plot as an example if (length(models) >= 1 && !is.null(models[[1]]$model)) { fitted_model <- models[[1]]$model p <- ggplot(fitted_model, aes(.hat, .stdresid)) + geom_point(aes(size = .cooksd), shape = 1, color = "darkorange") + geom_smooth(method = "lm", se = FALSE, color = "dodgerblue") + labs(title = "Residuals vs Leverage", x = "Leverage", y = "Standardized Residuals") ggplotly(p) } else { return(NULL) # No models to plot } }) output$regressionPlot <- renderPlotly({ req(modelsList(), datalrucb()) fitted_model <- modelsList() data_for_plot <- datalrucb() # Ensure the target and independent variables are provided target_col <- input$targetlrucb independent_var <- input$independentVarlrucb if (is.null(data_for_plot[[target_col]]) || is.null(data_for_plot[[independent_var]])) { return("Target or independent variable not found in the data.") } # Creating the plot with added color p <- ggplot(data_for_plot, aes_string(x = independent_var, y = target_col)) + geom_point(color = "darkorange") + # Change color of points geom_smooth(method = "lm", se = FALSE, color = "dodgerblue") + # Change color of regression line ggtitle("Regression Line Plot") + xlab(independent_var) + ylab(target_col) + theme_minimal() + # Adding a minimal theme for a cleaner look theme(legend.position = "none") # Remove legend if not needed # Convert ggplot object to Plotly for an interactive plot ggplotly(p) }) } shinyApp(ui, server)