library(tidyverse)
Quarto & Plotly
R: Prepare data
<- palmerpenguins::penguins %>%
data mutate(across(where(is.factor), as.character)) %>%
filter(!is.na(sex))
%>%
data ggplot(aes(x = sex, group = island, fill = island)) +
geom_bar(position = position_dodge())
head(data)
# A tibble: 6 × 8
species island bill_length_mm bill_depth_mm flipper_l…¹ body_…² sex year
<chr> <chr> <dbl> <dbl> <int> <int> <chr> <int>
1 Adelie Torgersen 39.1 18.7 181 3750 male 2007
2 Adelie Torgersen 39.5 17.4 186 3800 fema… 2007
3 Adelie Torgersen 40.3 18 195 3250 fema… 2007
4 Adelie Torgersen 36.7 19.3 193 3450 fema… 2007
5 Adelie Torgersen 39.3 20.6 190 3650 male 2007
6 Adelie Torgersen 38.9 17.8 181 3625 fema… 2007
# … with abbreviated variable names ¹flipper_length_mm, ²body_mass_g
OJS: Expose data for OJS
ojs_define(data = transpose(data))
d3 is needed for grouping / aggregation
= require("d3-array") d3
= require("https://cdn.plot.ly/plotly-2.16.1.min.js") Plotly
.table(data) Inputs
Aggregate (with JS / D3)
Goal: reduce data to group counts with JavaScript
This way we preserve runtime reactivity and don’t need to rely on static R inputs
Group by Species & Sex, Count Totals (length)
= d3
data_aggregated .flatRollup(
,
data=> facet.length, // index: 2
(facet) => row.island, // index: 0
(row) => row.sex // index: 1
(row)
)// abstraction / parametarisation for Plotly (x,y, name) here
// .map((entry) => ({ island: entry[0], sex: entry[1], n: entry[2] }))
.map((entry) => ({ name: entry[0], x: entry[1], y: entry[2] }))
.sort((a,b) => a.name.localeCompare(b.name)) // to put facet names in A-Z order
= d3.group(data_aggregated, (d) => d.name) data_grouped
Facet Result Template
Create a template for an object for a single facet (~ trace)
= ["x", "y", "name", "type"] toVars
= ({}) resultObject
.forEach((variable) => (resultObject[variable] = [])) toVars
// the manual mapping is only necessary if you don't abstract during aggregation
= [
fromVars from: "sex", to: "x"},
{from: "n", to: "y" }
{ ]
Aggregate Facets
= [...data_grouped.entries()].map((trace) => {
result const traceObj = JSON.parse(JSON.stringify(resultObject)); // copy the Object template
.name = trace[0]; // facet's name / Map's key (here: island)
traceObj.type = "bar"; // TODO: make global param
traceObj.text = trace[1].map((entry) => `n: ${entry.y}`); // TODO: make n a param
traceObj1].forEach((entry) => {
trace[// fromVars.forEach((mapping) => {
// traceObj[mapping.to].push(entry[mapping.from]);
"x", "y"].forEach((mapping) => {
[.push(entry[mapping]);
traceObj[mapping];
});
})return traceObj;
})
Plot data with Plotly
Plot Options
= ({
options title: "Count of Penguins by Gender & Island",
barmode: doStack ? "stack" : "group" // "group", "stack", "relative"
})
Reactive Stack Toogle
Plotly Plot
.newPlot("plot-canvas", result, options) Plotly