# R 4.4+ Production-Ready Code Examples

## Complete Shiny Application

### Project Structure

```
shiny_app/
├── app.R                    # Single-file app
├── R/
│   ├── data_processing.R
│   ├── visualizations.R
│   └── modules/
│       ├── filter_module.R
│       └── plot_module.R
├── data/
│   └── sample_data.csv
├── www/
│   ├── styles.css
│   └── logo.png
├── tests/
│   ├── testthat.R
│   └── testthat/
│       ├── test-data.R
│       └── test-modules.R
├── renv.lock
├── DESCRIPTION
└── README.md
```

### Main Application Entry

```r
# app.R
library(shiny)
library(bslib)
library(tidyverse)
library(plotly)

# Source modules
source("R/modules/filter_module.R")
source("R/modules/plot_module.R")

# Load data
data <- read_csv("data/sample_data.csv", show_col_types = FALSE)

# Define theme
app_theme <- bs_theme(
  version = 5,
  bootswatch = "cosmo",
  primary = "#007bff",
  base_font = font_google("Roboto")
)

# UI
ui <- page_navbar(
  title = "Analytics Dashboard",
  theme = app_theme,

  nav_panel(
    "Overview",
    layout_sidebar(
      sidebar = sidebar(
        width = 300,
        dataFilterUI("main_filter")
      ),
      card(
        card_header("Key Metrics"),
        layout_column_wrap(
          width = 1/3,
          value_box(
            title = "Total Revenue",
            value = textOutput("total_revenue"),
            theme = "primary"
          ),
          value_box(
            title = "Growth Rate",
            value = textOutput("growth_rate"),
            theme = "success"
          ),
          value_box(
            title = "Active Users",
            value = textOutput("active_users"),
            theme = "info"
          )
        )
      ),
      card(
        card_header("Trend Analysis"),
        plotlyOutput("trend_plot", height = "400px")
      )
    )
  ),

  nav_panel(
    "Details",
    dataTableOutput("data_table")
  ),

  nav_spacer(),
  nav_item(downloadButton("download_data", "Download"))
)

# Server
server <- function(input, output, session) {
  # Filter module
  filtered_data <- dataFilterServer("main_filter", reactive(data))

  # Reactive metrics
  metrics <- reactive({
    filtered_data() |>
      summarise(
        total_revenue = sum(revenue, na.rm = TRUE),
        growth_rate = mean(growth, na.rm = TRUE),
        active_users = n_distinct(user_id)
      )
  })

  # Value boxes
  output$total_revenue <- renderText({
    scales::dollar(metrics()$total_revenue)
  })

  output$growth_rate <- renderText({
    scales::percent(metrics()$growth_rate, accuracy = 0.1)
  })

  output$active_users <- renderText({
    scales::comma(metrics()$active_users)
  })

  # Trend plot
  output$trend_plot <- renderPlotly({
    p <- filtered_data() |>
      group_by(date) |>
      summarise(revenue = sum(revenue, na.rm = TRUE)) |>
      ggplot(aes(x = date, y = revenue)) +
      geom_line(color = "#007bff", linewidth = 1) +
      geom_point(size = 2) +
      scale_y_continuous(labels = scales::dollar) +
      labs(x = "Date", y = "Revenue") +
      theme_minimal()

    ggplotly(p)
  })

  # Data table
  output$data_table <- renderDataTable({
    filtered_data()
  })

  # Download handler
  output$download_data <- downloadHandler(
    filename = function() {
      paste0("data-", Sys.Date(), ".csv")
    },
    content = function(file) {
      write_csv(filtered_data(), file)
    }
  )
}

# Run app
shinyApp(ui, server)
```

### Shiny Module Pattern

```r
# R/modules/filter_module.R
library(shiny)

dataFilterUI <- function(id) {
  ns <- NS(id)
  tagList(
    h4("Filters"),
    selectInput(
      ns("category"),
      "Category:",
      choices = NULL,
      multiple = TRUE
    ),
    dateRangeInput(
      ns("date_range"),
      "Date Range:",
      start = Sys.Date() - 30,
      end = Sys.Date()
    ),
    sliderInput(
      ns("revenue_range"),
      "Revenue Range:",
      min = 0,
      max = 100000,
      value = c(0, 100000),
      pre = "$"
    ),
    actionButton(ns("reset"), "Reset Filters", class = "btn-secondary")
  )
}

dataFilterServer <- function(id, data) {
  moduleServer(id, function(input, output, session) {
    # Initialize category choices
    observe({
      categories <- unique(data()$category)
      updateSelectInput(
        session,
        "category",
        choices = categories,
        selected = categories
      )
    })

    # Reset button
    observeEvent(input$reset, {
      updateSelectInput(session, "category", selected = unique(data()$category))
      updateDateRangeInput(
        session,
        "date_range",
        start = min(data()$date),
        end = max(data()$date)
      )
    })

    # Return filtered data
    reactive({
      req(input$category)

      data() |>
        filter(
          category %in% input$category,
          date >= input$date_range[1],
          date <= input$date_range[2],
          revenue >= input$revenue_range[1],
          revenue <= input$revenue_range[2]
        )
    })
  })
}
```

### Advanced Reactivity Patterns

```r
# Reactive values for state management
server <- function(input, output, session) {
  # Reactive value
  state <- reactiveValues(
    counter = 0,
    selected_items = character(),
    last_update = NULL
  )

  # Reactive expression (cached)
  processed_data <- reactive({
    raw_data() |>
      filter(year == input$year) |>
      mutate(normalized = scale(value)[,1])
  })

  # Event reactive (triggered by specific input)
  analysis_result <- eventReactive(input$run_analysis, {
    processed_data() |>
      perform_expensive_computation()
  })

  # Observe (side effects only)
  observe({
    state$last_update <- Sys.time()
  })

  # ObserveEvent (trigger on specific input)
  observeEvent(input$increment, {
    state$counter <- state$counter + 1
  })

  # Debounce for rapid inputs
  search_debounced <- reactive({
    input$search
  }) |> debounce(500)

  # Throttle for continuous inputs
  slider_throttled <- reactive({
    input$slider
  }) |> throttle(1000)

  # Reactive poll for external data
  external_data <- reactivePoll(
    intervalMillis = 5000,
    session = session,
    checkFunc = function() {
      file.info("data/external.csv")$mtime
    },
    valueFunc = function() {
      read_csv("data/external.csv")
    }
  )
}
```

---

## Complete tidyverse Data Analysis

### Data Import and Cleaning

```r
library(tidyverse)
library(lubridate)
library(janitor)

# Import with readr
raw_data <- read_csv(
  "data/sales.csv",
  col_types = cols(
    date = col_date(format = "%Y-%m-%d"),
    revenue = col_double(),
    category = col_factor(),
    region = col_factor()
  ),
  na = c("", "NA", "NULL")
)

# Clean column names
clean_data <- raw_data |>
  clean_names() |>
  remove_empty(which = c("rows", "cols"))

# Data cleaning pipeline
processed <- clean_data |>
  # Handle missing values
  mutate(
    revenue = if_else(is.na(revenue), median(revenue, na.rm = TRUE), revenue),
    category = fct_explicit_na(category, na_level = "Unknown")
  ) |>
  # Date operations
  mutate(
    year = year(date),
    month = month(date, label = TRUE),
    quarter = quarter(date),
    week = week(date),
    day_of_week = wday(date, label = TRUE)
  ) |>
  # Text cleaning
  mutate(
    across(where(is.character), str_trim),
    across(where(is.character), str_to_lower)
  ) |>
  # Remove duplicates
  distinct(date, category, region, .keep_all = TRUE)
```

### Advanced dplyr Operations

```r
# Complex grouping and summarization
summary_stats <- processed |>
  group_by(category, region) |>
  summarise(
    n = n(),
    total_revenue = sum(revenue, na.rm = TRUE),
    avg_revenue = mean(revenue, na.rm = TRUE),
    median_revenue = median(revenue, na.rm = TRUE),
    sd_revenue = sd(revenue, na.rm = TRUE),
    min_revenue = min(revenue, na.rm = TRUE),
    max_revenue = max(revenue, na.rm = TRUE),
    q25 = quantile(revenue, 0.25, na.rm = TRUE),
    q75 = quantile(revenue, 0.75, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(
    cv = sd_revenue / avg_revenue,
    iqr = q75 - q25
  )

# Window functions
ranked_data <- processed |>
  group_by(category) |>
  mutate(
    rank = row_number(desc(revenue)),
    percentile = percent_rank(revenue),
    cumulative_revenue = cumsum(revenue),
    moving_avg = slider::slide_dbl(
      revenue,
      mean,
      .before = 2,
      .after = 2
    )
  ) |>
  ungroup()

# Lag and lead for time series
time_series <- processed |>
  arrange(date) |>
  group_by(category) |>
  mutate(
    prev_revenue = lag(revenue),
    next_revenue = lead(revenue),
    growth = (revenue - prev_revenue) / prev_revenue,
    yoy_growth = (revenue - lag(revenue, 12)) / lag(revenue, 12)
  ) |>
  ungroup()

# across() for multiple columns
normalized <- processed |>
  mutate(
    across(
      where(is.numeric),
      list(
        scaled = \(x) scale(x)[,1],
        log = \(x) log1p(x),
        sqrt = \(x) sqrt(abs(x))
      ),
      .names = "{.col}_{.fn}"
    )
  )

# if_any() and if_all() for filtering
high_performers <- processed |>
  filter(
    if_any(
      c(revenue, profit, growth),
      \(x) x > quantile(x, 0.9, na.rm = TRUE)
    )
  )

low_performers <- processed |>
  filter(
    if_all(
      c(revenue, profit, growth),
      \(x) x < quantile(x, 0.1, na.rm = TRUE)
    )
  )
```

### tidyr Reshaping Patterns

```r
# Pivot longer with multiple value columns
wide_to_long <- wide_data |>
  pivot_longer(
    cols = matches("^(revenue|profit)_\\d{4}$"),
    names_to = c(".value", "year"),
    names_pattern = "(.+)_(\\d{4})"
  )

# Pivot wider with multiple id columns
long_to_wide <- long_data |>
  pivot_wider(
    id_cols = c(category, region),
    names_from = c(year, quarter),
    names_sep = "_Q",
    values_from = c(revenue, profit),
    values_fill = 0
  )

# Separate column into multiple
separated <- data |>
  separate_wider_delim(
    location,
    delim = ", ",
    names = c("city", "state", "country")
  )

# Unite multiple columns
united <- data |>
  unite(
    "full_location",
    city, state, country,
    sep = ", ",
    remove = FALSE
  )

# Nest data for grouped operations
nested <- processed |>
  group_by(category) |>
  nest() |>
  mutate(
    model = map(data, \(df) lm(revenue ~ date, data = df)),
    predictions = map2(model, data, \(m, df) predict(m, df)),
    r_squared = map_dbl(model, \(m) summary(m)$r.squared)
  )
```

### purrr Functional Programming

```r
library(purrr)

# Read multiple files
file_list <- list.files("data/", pattern = "\\.csv$", full.names = TRUE)
all_data <- file_list |>
  set_names(basename) |>
  map_dfr(read_csv, .id = "source")

# Map variants
numbers <- list(1:5, 6:10, 11:15)
map(numbers, mean)            # Returns list
map_dbl(numbers, mean)        # Returns numeric vector
map_chr(numbers, \(x) paste(x, collapse = "-"))  # Returns character vector
map_lgl(numbers, \(x) all(x > 5))  # Returns logical vector

# Map with multiple inputs
map2(list1, list2, \(x, y) x + y)
pmap(list(x = 1:3, y = 4:6, z = 7:9), sum)

# Error handling
safe_divide <- safely(\(x, y) x / y)
results <- map2(c(10, 20, 30), c(2, 0, 5), safe_divide)
successes <- map(results, "result") |> compact()
errors <- map(results, "error") |> compact()

# Possibly for default values
possibly_divide <- possibly(\(x, y) x / y, otherwise = NA)
map2_dbl(c(10, 20, 30), c(2, 0, 5), possibly_divide)

# Walk for side effects
walk(file_list, \(f) message("Processing: ", f))
walk2(names, data_frames, \(name, df) write_csv(df, paste0(name, ".csv")))

# Reduce for accumulation
reduce(list(df1, df2, df3), bind_rows)
reduce(1:10, `+`)  # Sum
accumulate(1:10, `+`)  # Cumulative sum

# Keep and discard
numbers <- list(1, "a", 2, "b", 3)
keep(numbers, is.numeric)
discard(numbers, is.numeric)
```

---

## ggplot2 Advanced Visualizations

### Publication-Ready Plots

```r
library(ggplot2)
library(scales)
library(ggtext)
library(patchwork)

# Complete publication plot
p <- data |>
  ggplot(aes(x = date, y = revenue, color = category)) +
  # Geometries
  geom_line(linewidth = 1, alpha = 0.8) +
  geom_point(size = 2, alpha = 0.6) +
  geom_smooth(method = "loess", se = TRUE, alpha = 0.2) +

  # Scales
  scale_x_date(
    date_breaks = "1 month",
    date_labels = "%b %Y",
    expand = expansion(mult = c(0.02, 0.02))
  ) +
  scale_y_continuous(
    labels = dollar,
    breaks = breaks_extended(n = 8),
    expand = expansion(mult = c(0, 0.05))
  ) +
  scale_color_viridis_d(
    option = "D",
    begin = 0.2,
    end = 0.8
  ) +

  # Faceting
  facet_wrap(
    ~ region,
    scales = "free_y",
    ncol = 2
  ) +

  # Labels with markdown
  labs(
    title = "**Revenue Trends** by Category and Region",
    subtitle = "Monthly data from 2020-2024 with LOESS smoothing",
    x = "Date",
    y = "Revenue (USD)",
    color = "Category",
    caption = "Data source: Internal sales database"
  ) +

  # Theme
  theme_minimal(base_size = 12, base_family = "Roboto") +
  theme(
    plot.title = element_markdown(face = "bold", size = 16),
    plot.subtitle = element_text(color = "gray40"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    legend.position = "bottom",
    panel.grid.minor = element_blank(),
    strip.text = element_text(face = "bold", size = 11),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

# Save with high resolution
ggsave(
  "figures/revenue_trends.png",
  plot = p,
  width = 12,
  height = 8,
  dpi = 300,
  bg = "white"
)
```

### Custom Themes and Scales

```r
# Custom theme
theme_custom <- function(base_size = 12, base_family = "sans") {
  theme_minimal(base_size = base_size, base_family = base_family) +
    theme(
      plot.title = element_text(face = "bold", size = rel(1.4)),
      plot.subtitle = element_text(color = "gray40", size = rel(1.1)),
      plot.caption = element_text(hjust = 0, face = "italic", color = "gray50"),
      panel.grid.minor = element_blank(),
      panel.border = element_rect(fill = NA, color = "gray80"),
      legend.position = "bottom",
      legend.title = element_text(face = "bold"),
      strip.text = element_text(face = "bold", hjust = 0),
      strip.background = element_rect(fill = "gray90", color = NA)
    )
}

# Custom color palette
brand_colors <- c(
  primary = "#007bff",
  secondary = "#6c757d",
  success = "#28a745",
  danger = "#dc3545",
  warning = "#ffc107",
  info = "#17a2b8"
)

scale_color_brand <- function(...) {
  scale_color_manual(values = brand_colors, ...)
}

scale_fill_brand <- function(...) {
  scale_fill_manual(values = brand_colors, ...)
}

# Usage
ggplot(data, aes(x = category, y = value, fill = status)) +
  geom_col() +
  scale_fill_brand() +
  theme_custom()
```

### Multiple Plots with patchwork

```r
library(patchwork)

# Create individual plots
p1 <- ggplot(data, aes(x = x)) +
  geom_histogram(bins = 30, fill = "#007bff") +
  labs(title = "Distribution of X") +
  theme_minimal()

p2 <- ggplot(data, aes(x = x, y = y)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm", color = "#dc3545") +
  labs(title = "X vs Y Relationship") +
  theme_minimal()

p3 <- ggplot(data, aes(x = category, y = value)) +
  geom_boxplot(fill = "#28a745") +
  labs(title = "Value by Category") +
  theme_minimal()

p4 <- ggplot(data, aes(x = date, y = value)) +
  geom_line(color = "#ffc107") +
  labs(title = "Time Series") +
  theme_minimal()

# Layout options
combined <- (p1 | p2) / (p3 | p4) +
  plot_annotation(
    title = "Comprehensive Data Analysis",
    subtitle = "Multiple perspectives on the dataset",
    caption = "Created with ggplot2 + patchwork",
    tag_levels = "A",
    theme = theme(
      plot.title = element_text(face = "bold", size = 18),
      plot.subtitle = element_text(color = "gray40")
    )
  )

# Alternative layouts
(p1 + p2 + p3) / p4
p1 / (p2 | p3 | p4)
wrap_plots(p1, p2, p3, p4, ncol = 2, guides = "collect")
```

---

## R Package Development

### Package Structure

```
mypackage/
├── DESCRIPTION
├── NAMESPACE
├── LICENSE
├── README.md
├── NEWS.md
├── R/
│   ├── package.R
│   ├── data_processing.R
│   └── utils.R
├── man/
│   ├── mypackage-package.Rd
│   └── process_data.Rd
├── tests/
│   ├── testthat.R
│   └── testthat/
│       ├── test-processing.R
│       └── test-utils.R
├── vignettes/
│   └── introduction.Rmd
└── data-raw/
    └── prepare_data.R
```

### DESCRIPTION File

```
Package: mypackage
Title: Advanced Data Processing Tools
Version: 0.1.0
Authors@R:
    person("First", "Last", email = "first.last@example.com",
           role = c("aut", "cre"),
           comment = c(ORCID = "0000-0000-0000-0000"))
Description: Provides efficient tools for processing and analyzing large datasets
    with tidyverse integration and performance optimization.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.0
Imports:
    dplyr (>= 1.1.0),
    tidyr (>= 1.3.0),
    rlang (>= 1.1.0),
    cli (>= 3.6.0)
Suggests:
    testthat (>= 3.2.0),
    knitr,
    rmarkdown
VignetteBuilder: knitr
URL: https://github.com/username/mypackage
BugReports: https://github.com/username/mypackage/issues
```

### roxygen2 Documentation

```r
# R/data_processing.R

#' Process and clean data
#'
#' @description
#' Performs comprehensive data cleaning and processing with automatic
#' type inference and missing value handling.
#'
#' @param data A data frame to process
#' @param remove_na Logical. Should rows with missing values be removed?
#'   Default is `FALSE`.
#' @param normalize Logical. Should numeric columns be normalized?
#'   Default is `FALSE`.
#'
#' @return A processed data frame with the same structure as input
#'
#' @examples
#' library(mypackage)
#'
#' # Basic usage
#' clean_data <- process_data(mtcars)
#'
#' # With options
#' clean_data <- process_data(
#'   mtcars,
#'   remove_na = TRUE,
#'   normalize = TRUE
#' )
#'
#' @export
#' @importFrom dplyr mutate across where
#' @importFrom tidyr drop_na
process_data <- function(data, remove_na = FALSE, normalize = FALSE) {
  # Input validation
  if (!is.data.frame(data)) {
    cli::cli_abort("{.arg data} must be a data frame")
  }

  # Processing
  result <- data

  if (remove_na) {
    result <- tidyr::drop_na(result)
  }

  if (normalize) {
    result <- dplyr::mutate(
      result,
      dplyr::across(
        dplyr::where(is.numeric),
        \(x) scale(x)[,1]
      )
    )
  }

  result
}

#' @keywords internal
"_PACKAGE"
```

### Package Development Workflow

```r
# Setup
usethis::create_package("mypackage")
usethis::use_git()
usethis::use_github()
usethis::use_mit_license()

# Development
usethis::use_r("data_processing")
usethis::use_test("processing")
usethis::use_vignette("introduction")

# Dependencies
usethis::use_package("dplyr")
usethis::use_package("testthat", type = "Suggests")

# Documentation
devtools::document()
devtools::build_readme()

# Testing and checking
devtools::test()
devtools::check()
goodpractice::gp()

# Build and install
devtools::build()
devtools::install()
```

---

## testthat Testing Suite

### Test Structure

```r
# tests/testthat/test-processing.R
library(testthat)
library(mypackage)

test_that("process_data handles basic input", {
  input <- data.frame(x = 1:5, y = 6:10)
  result <- process_data(input)

  expect_s3_class(result, "data.frame")
  expect_equal(nrow(result), 5)
  expect_equal(ncol(result), 2)
})

test_that("process_data removes NA values when requested", {
  input <- data.frame(x = c(1, NA, 3), y = c(4, 5, NA))
  result <- process_data(input, remove_na = TRUE)

  expect_equal(nrow(result), 1)
  expect_false(anyNA(result))
})

test_that("process_data normalizes numeric columns", {
  input <- data.frame(x = 1:100, y = 101:200)
  result <- process_data(input, normalize = TRUE)

  expect_equal(mean(result$x), 0, tolerance = 1e-10)
  expect_equal(sd(result$x), 1, tolerance = 1e-10)
})

test_that("process_data errors on invalid input", {
  expect_error(
    process_data("not a dataframe"),
    "must be a data frame"
  )

  expect_error(
    process_data(NULL),
    "must be a data frame"
  )
})

# Snapshot testing
test_that("process_data output matches snapshot", {
  input <- mtcars[1:5, 1:3]
  result <- process_data(input, normalize = TRUE)
  expect_snapshot_value(result, style = "json2")
})

# Mocking external dependencies
test_that("function handles API errors gracefully", {
  local_mocked_bindings(
    fetch_external_data = function(...) stop("API error")
  )

  expect_error(
    process_external(),
    "API error"
  )
})
```

### Setup and Teardown

```r
# tests/testthat/setup.R (runs once before all tests)
temp_dir <- tempdir()
test_data <- data.frame(x = 1:100, y = rnorm(100))

# tests/testthat/teardown.R (runs once after all tests)
unlink(temp_dir, recursive = TRUE)

# Per-test setup
test_that("example with local setup", {
  withr::local_tempdir()  # Automatically cleaned up

  # Test code here
})
```

---

## R Markdown and Quarto

### R Markdown Document

````markdown
---
title: "Comprehensive Data Analysis"
author: "Data Scientist"
date: "`r Sys.Date()`"
output:
  html_document:
    toc: true
    toc_float: true
    code_folding: hide
    theme: cosmo
    highlight: tango
  pdf_document:
    toc: true
params:
  data_file: "data/sales.csv"
  year: 2024
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  echo = TRUE,
  message = FALSE,
  warning = FALSE,
  fig.width = 10,
  fig.height = 6,
  dpi = 300
)

library(tidyverse)
library(scales)
library(knitr)
library(kableExtra)
```

## Executive Summary

This report analyzes sales data for `r params$year`.

```{r load-data}
data <- read_csv(params$data_file)
```

## Data Overview

```{r summary-table}
data |>
  summary() |>
  kable(caption = "Data Summary") |>
  kable_styling(bootstrap_options = c("striped", "hover"))
```

## Visualizations

```{r revenue-plot, fig.cap="Revenue trends over time"}
data |>
  ggplot(aes(x = date, y = revenue, color = category)) +
  geom_line(linewidth = 1) +
  scale_y_continuous(labels = dollar) +
  theme_minimal() +
  labs(title = "Revenue Trends", x = "Date", y = "Revenue")
```

## Inline Code

The total revenue for `r params$year` was
`r dollar(sum(data$revenue, na.rm = TRUE))`.
````

### Quarto Document

````markdown
---
title: "Advanced Analysis with Quarto"
format:
  html:
    code-fold: true
    toc: true
    theme: cosmo
  pdf:
    documentclass: article
execute:
  echo: true
  warning: false
jupyter: python3
---

## Introduction

Quarto supports multiple languages.

```{r}
library(tidyverse)
data <- mtcars
summary(data)
```

```{python}
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.randn(100)})
df.describe()
```

## Cross-references {#sec-intro}

See @sec-analysis for details.

## Analysis {#sec-analysis}

Results shown in @fig-plot.

```{r}
#| label: fig-plot
#| fig-cap: "Data distribution"
ggplot(mtcars, aes(x = mpg)) +
  geom_histogram(bins = 20)
```
````

---

## Statistical Modeling

### Linear Models

```r
# Simple linear regression
model <- lm(mpg ~ wt + hp, data = mtcars)
summary(model)

# Broom for tidy output
library(broom)
tidy(model, conf.int = TRUE)
glance(model)
augment(model)

# Diagnostics
par(mfrow = c(2, 2))
plot(model)

# Model comparison
model1 <- lm(mpg ~ wt, data = mtcars)
model2 <- lm(mpg ~ wt + hp, data = mtcars)
model3 <- lm(mpg ~ wt + hp + qsec, data = mtcars)

anova(model1, model2, model3)
AIC(model1, model2, model3)
```

### tidymodels Workflow

```r
library(tidymodels)

# Split data
set.seed(123)
data_split <- initial_split(mtcars, prop = 0.8)
train_data <- training(data_split)
test_data <- testing(data_split)

# Recipe
recipe <- recipe(mpg ~ ., data = train_data) |>
  step_normalize(all_numeric_predictors()) |>
  step_dummy(all_nominal_predictors())

# Model specification
rf_spec <- rand_forest(
  mtry = tune(),
  trees = 1000,
  min_n = tune()
) |>
  set_engine("ranger") |>
  set_mode("regression")

# Workflow
workflow <- workflow() |>
  add_recipe(recipe) |>
  add_model(rf_spec)

# Tuning
set.seed(234)
folds <- vfold_cv(train_data, v = 5)

tune_results <- workflow |>
  tune_grid(
    resamples = folds,
    grid = 10
  )

# Best model
best_params <- select_best(tune_results, metric = "rmse")
final_workflow <- finalize_workflow(workflow, best_params)
final_fit <- fit(final_workflow, train_data)

# Predictions
predictions <- predict(final_fit, test_data)
metrics <- metric_set(rmse, rsq, mae)
bind_cols(test_data, predictions) |>
  metrics(truth = mpg, estimate = .pred)
```

---

Last Updated: 2026-01-10
Version: 1.0.0
