Calculator: Simple linear regression

Author

Tom Coleman

Summary
A calculator to provide simple linear regression for your data.

Simple linear regression calculator

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 1400

library(shiny)
library(bslib)
library(ggplot2)

ui <- page_fillable(
  title = "Simple linear regression calculator",
  
  card(
    card_header("Variable names"),
    height = "175px",
    layout_columns(
      col_widths = c(6, 6),
      textInput("x_name", "X Variable Name:", value = "X Variable", placeholder = "Enter X variable name"),
      textInput("y_name", "Y Variable Name:", value = "Y Variable", placeholder = "Enter Y variable name")
    )
  ),
  
  card(
    card_header("Input data"),
    height = "375px",
    p("Enter x and y values (one pair per line, separated by space, comma, or tab):"),
    textAreaInput(
      "data_input",
      NULL,
      placeholder = "for example,\n1 2\n2 4\n3 5\n4 4\n5 5",
      height = "150px",
      width = "100%"
    ),
    actionButton("calculate", "Calculate simple linear regression", class = "btn-primary")
  ),
  
  card(
    card_header("Regression results"),
    height = "350px",
    verbatimTextOutput("regression_summary")
  ),
  
  card(
    card_header("Regression plot"),
    height = "500px",
    plotOutput("regression_plot", height = "400px")
  )
)

server <- function(input, output, session) {
  
  # Reactive expression to parse and validate data
  parsed_data <- reactive({
    req(input$calculate)
    
    # Get input text
    text <- input$data_input
    
    if (text == "" || is.null(text)) {
      return(NULL)
    }
    
    # Split by newlines
    lines <- strsplit(text, "\n")[[1]]
    lines <- lines[lines != ""]  # Remove empty lines
    
    # Parse each line (split by space, comma, or tab)
    data_list <- lapply(lines, function(line) {
      # Split by space, comma, or tab
      parts <- strsplit(line, "[,\\s\t]+")[[1]]
      parts <- parts[parts != ""]  # Remove empty strings
      
      if (length(parts) >= 2) {
        return(c(as.numeric(parts[1]), as.numeric(parts[2])))
      } else {
        return(NULL)
      }
    })
    
    # Remove NULL entries
    data_list <- data_list[!sapply(data_list, is.null)]
    
    if (length(data_list) < 2) {
      return(NULL)
    }
    
    # Convert to data frame
    df <- data.frame(
      x = sapply(data_list, `[`, 1),
      y = sapply(data_list, `[`, 2)
    )
    
    # Remove rows with NA values
    df <- na.omit(df)
    
    if (nrow(df) < 2) {
      return(NULL)
    }
    
    return(df)
  })
  
  # Reactive expression to fit linear model
  regression_model <- reactive({
    df <- parsed_data()
    req(df)
    
    lm(y ~ x, data = df)
  })
  
  # Output: Regression summary
  output$regression_summary <- renderPrint({
    model <- regression_model()
    req(model)
    
    coefs <- coef(model)
    r_squared <- summary(model)$r.squared
    x_name <- input$x_name
    y_name <- input$y_name
    
    cat("Simple linear regression results\n")
    cat("=========================\n\n")
    cat(sprintf("Equation: %s = %.4f + %.4f * %s\n\n", y_name, coefs[1], coefs[2], x_name))
    cat(sprintf("Intercept (alpha): %.4f\n", coefs[1]))
    cat(sprintf("Gradient (beta): %.4f\n", coefs[2]))
    cat(sprintf("R-squared: %.4f\n\n", r_squared))
    cat("Sample size:", nrow(parsed_data()), "\n")
  })
  
  # Output: Regression plot
  output$regression_plot <- renderPlot({
    df <- parsed_data()
    model <- regression_model()
    req(df, model)
    
    x_name <- input$x_name
    y_name <- input$y_name
    
    # Get predictions for the regression line
    df$predicted <- predict(model)
    df$se <- predict(model, se.fit = TRUE)$se.fit
    
    ggplot(df, aes(x = x, y = y)) +
      geom_point(size = 3, color = "#3f68b6") +
      geom_line(aes(y = predicted), color = "#db4315", linewidth = 1) +
      geom_ribbon(aes(ymin = predicted - 1.96 * se, ymax = predicted + 1.96 * se), 
                  fill = "#db4315", alpha = 0.2) +
      labs(
        title = "Simple linear regression",
        x = x_name,
        y = y_name
      ) +
      theme_minimal(base_size = 14) +
      theme(
        plot.title = element_text(hjust = 0.5, face = "bold"),
        panel.grid.minor = element_blank()
      )
  })
}

shinyApp(ui, server)

Version history

v1.0: created by tdhc 03/26.

This work is licensed under CC BY-NC-SA 4.0.

Feedback

Your feedback is appreciated and useful. Feel free to leave a comment here,
but please be specific with any issues you encounter so we can help to resolve them
(for example, what page it occured on, what you tried, and so on).