Required Libraries

library(dplyr)
library(plotly)
library(htmlwidgets)
library(htmltools)
library(DT)

Process the mtcars dataset

mtcars_processed <- mtcars %>%
  mutate(
    car_name = rownames(.),
    # Convert binary variables to factors
    vs = factor(vs, levels = c(0, 1), labels = c("V-shape", "Straight")),
    am = factor(am, levels = c(0, 1), labels = c("Automatic", "Manual")),
    # Convert categorical variables to factors
    cyl = factor(cyl),
    gear = factor(gear),
    carb = factor(carb)
  )

# Get numeric variables
numeric_vars <- names(mtcars_processed)[sapply(mtcars_processed, is.numeric)]

# Create variable labels
var_labels <- list(
  mpg = "Miles per Gallon",
  disp = "Displacement",
  hp = "Horsepower",
  drat = "Rear Axle Ratio",
  wt = "Weight",
  qsec = "Quarter Mile Time"
)

Interactive plot

  1. Implemented using Plotly and Javascript.
  2. Select different variables for X and Y axes using the dropdown menus
  3. Hover over points to see car details, Click and drag to zoom, Double-click to reset the view, Pan across the plot
# Create selection UI using htmltools
div(
  style = "margin-bottom: 20px;",
  tags$label("X Variable: ", `for` = "xvar"),
  tags$select(
    id = "xvar",
    onchange = "updatePlot()",
    style = "margin-right: 15px;",
    lapply(numeric_vars, function(var) {
      tags$option(
        value = var,
        selected = if(var == "wt") "selected" else NULL,
        var_labels[[var]]
      )
    })
  ),
  tags$label("Y Variable: ", `for` = "yvar", style = "margin-left: 15px;"),
  tags$select(
    id = "yvar",
    onchange = "updatePlot()",
    lapply(numeric_vars, function(var) {
      tags$option(
        value = var,
        selected = if(var == "mpg") "selected" else NULL,
        var_labels[[var]]
      )
    })
  )
)
# Create initial plot
plot_ly(mtcars_processed, height = 500) %>%
  add_trace(
    x = ~wt,
    y = ~mpg,
    type = 'scatter',
    mode = 'markers',
    text = ~car_name,
    hoverinfo = 'text+x+y',
    marker = list(
      size = 10,
      color = '#1f77b4'
    )
  ) %>%
  layout(
    title = paste(var_labels[["wt"]], "vs", var_labels[["mpg"]]),
    xaxis = list(
      title = var_labels[["wt"]],
      gridcolor = '#F5F5F5',
      zerolinecolor = '#E1E1E1'
    ),
    yaxis = list(
      title = var_labels[["mpg"]],
      gridcolor = '#F5F5F5',
      zerolinecolor = '#E1E1E1'
    ),
    plot_bgcolor = '#FFFFFF',
    paper_bgcolor = '#FFFFFF',
    showlegend = FALSE
  ) %>%
  # Add JavaScript event handlers and data
  onRender(sprintf("
    function(el, x) {
      // Store the data globally
      window.mtcars = %s;
      
      // Function to update the plot
      window.updatePlot = function() {
        var xvar = document.getElementById('xvar').value;
        var yvar = document.getElementById('yvar').value;
        
        // Get variable labels
        var xlabel = document.getElementById('xvar').options[document.getElementById('xvar').selectedIndex].text;
        var ylabel = document.getElementById('yvar').options[document.getElementById('yvar').selectedIndex].text;
        
        // Create scatter plot trace
        var trace1 = {
          x: window.mtcars[xvar],
          y: window.mtcars[yvar],
          mode: 'markers',
          type: 'scatter',
          text: window.mtcars['car_name'],
          hoverinfo: 'text+x+y',
          marker: {size: 10, color: '#1f77b4'},
          name: 'Data points'
        };
        
        // Calculate smooth line
        var x_vals = window.mtcars[xvar];
        var y_vals = window.mtcars[yvar];
        
        // Sort x and y values for the smooth line
        var xy_pairs = x_vals.map((x, i) => ({x: x, y: y_vals[i]}));
        xy_pairs.sort((a, b) => a.x - b.x);
        
        // Create evenly spaced x values for the smooth line
        var smooth_x = [];
        var n_points = 100;
        var x_min = Math.min(...x_vals);
        var x_max = Math.max(...x_vals);
        var x_step = (x_max - x_min) / (n_points - 1);
        for(var i = 0; i < n_points; i++) {
          smooth_x.push(x_min + i * x_step);
        }
        
        // Calculate LOESS-like smoothing
        var smooth_y = smooth_x.map(x => {
          var weights = xy_pairs.map(point => {
            var diff = (point.x - x) / (x_max - x_min);
            return Math.exp(-Math.abs(diff) * 5);
          });
          var weight_sum = weights.reduce((a, b) => a + b, 0);
          return weights.reduce((sum, w, i) => sum + w * xy_pairs[i].y, 0) / weight_sum;
        });
        
        // Create smooth line trace
        var trace2 = {
          x: smooth_x,
          y: smooth_y,
          mode: 'lines',
          type: 'scatter',
          line: {color: '#ff7f0e', width: 2},
          name: 'Trend line'
        };
        
        // Update layout
        var layout = {
          title: xlabel + ' vs ' + ylabel,
          xaxis: {
            title: xlabel,
            gridcolor: '#F5F5F5',
            zerolinecolor: '#E1E1E1'
          },
          yaxis: {
            title: ylabel,
            gridcolor: '#F5F5F5',
            zerolinecolor: '#E1E1E1'
          },
          plot_bgcolor: '#FFFFFF',
          paper_bgcolor: '#FFFFFF',
          showlegend: true,
          legend: {
            x: 1,
            xanchor: 'right',
            y: 1
          }
        };
        
        // Update the plot with both traces
        Plotly.react(el, [trace1, trace2], layout);
      }
    }", jsonlite::toJSON(mtcars_processed, dataframe = "columns")))

Variable Information

# Create data frame of variable information
numeric_info <- data.frame(
  Variable = numeric_vars,
  Name = sapply(numeric_vars, function(x) var_labels[[x]]),
  Description = c(
    "Fuel efficiency measurement",
    "Engine displacement in cubic inches",
    "Gross horsepower",
    "Rear axle ratio",
    "Weight (1000 lbs)",
    "1/4 mile time"
  )
)

DT::datatable(numeric_info, 
              options = list(dom = 't', 
                           ordering = FALSE),
              caption = "Available Numeric Variables")