# 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")))