Filter data by searching. Uses spaces between terms.
Select individual rows with floating checkboxes next to the first column. Sort the table by clicking column heads.
Filter data by searching. Uses spaces between terms.
Select individual rows with floating checkboxes next to the first column. Sort the table by clicking column heads.
Plot.plot({
facet: {
data: filtered,
x: scatterFacetX,
y: scatterFacetY,
marginRight: 80
},
grid: false,
symbol: ((scatterFill !== "") && (fillType === 'string')) ?
{
legend: true,
tickFormat: formatLabel,
label: formatLabel(scatterFill),
} : [],
color: ((scatterFill !== "") && (fillType === 'number')) ?
{
legend: true,
tickFormat: formatLabel,
label: formatLabel(scatterFill),
} : [],
marginTop: 36, // more room for facets
marginBottom: 48,
marks: [
Plot.axisX({ // Axis with just the ticks in the default fontSize
label: null,
// ticks: (scatterFacetX !== "") ? 4 : 8,
}),
Plot.axisX({ // Axis with just the label in custom fontSize
label: formatLabel(scatterX),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(scatterFacetX !== "") ? Plot.axisFx({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(scatterFacetX !== "") ? Plot.axisFx({ // Facet axis with just the label
label: formatLabel(scatterFacetX),
fontSize: largeFontSize,
ticks: [ ],
}) : [],
Plot.axisY({ // Axis with just the ticks in the default fontSize
label: null,
// ticks: (scatterFacetX !== "") ? 4 : 8,
}),
Plot.axisY({ // Axis with just the label in the custom fontSize
label: formatLabel(scatterY),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(scatterFacetY !== "") ? Plot.axisFy({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(scatterFacetY !== "") ? Plot.axisFy({ // Facet axis with just the label
label: formatLabel(scatterFacetY),
labelAnchor: "top",
fontSize: largeFontSize,
ticks: [ ],
}) : [],
(scatterRegr=="Line")?Plot.linearRegressionY(
filtered, {
x: scatterX,
y: scatterY,
stroke: scatterRegrFill,
ci: 0}):[],
(scatterRegr=="± 95% CI")?Plot.linearRegressionY(
filtered, {
x: scatterX,
y: scatterY,
stroke: scatterRegrFill,
ci: 0.95}):[],
(scatterGrpRegr=="Line")?Plot.linearRegressionY(
filtered, {
x: scatterX,
y: scatterY,
stroke: scatterFill,
ci: 0}):[],
(scatterGrpRegr=="± 95% CI")?Plot.linearRegressionY(
filtered, {
x: scatterX,
y: scatterY,
stroke: scatterFill,
ci: 0.95}):[],
Plot.dot(
filtered, {
x: scatterX,
y: scatterY,
fill: (scatterFill !== "") ? scatterFill : "steelblue",
opacity: scatterOpacity,
symbol: ((scatterFill !== "") && (fillType === 'string')) ? scatterFill : "steelblue"}),
(scatterFacetX !== "" || scatterFacetY !== "") ?
Plot.frame() : null,
],
height: scatterHeight,
width: scatterWidth,
marginLeft: 60
});html
`<div id="scatterEditor" autocorrect="off" spellcheck="false">
<p>Figure #. <b>Title.</b></p><p>Caption.</p>
</div>`myHisto = Plot.plot({
height: histoHeight,
width: histoWidth,
y: {grid: true},
// figure: true,
facet: {
data: filtered,
x: histFacetX,
y: histFacetY,
marginRight: 80
},
color: {
legend: true,
label: formatLabel(histoFill),
tickFormat: formatLabel,
},
marginTop: 36, // more room for facets
marginBottom: 48,
marks: [
Plot.axisX({ // Axis with just the ticks in the default fontSize
label: null,
}),
Plot.axisX({ // Axis with just the label in custom fontSize
label: formatLabel(histoX),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(histFacetX !== "") ? Plot.axisFx({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(histFacetX !== "") ? Plot.axisFx({ // Facet axis with just the label
label: formatLabel(histFacetX),
fontSize: largeFontSize,
ticks: [ ],
}) : [],
Plot.axisY({ // Axis with just the ticks in the default fontSize
label: null,
}),
Plot.axisY({ // Axis with just the label in the custom fontSize
label: formatLabel(histoY),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(histFacetY !== "") ? Plot.axisFy({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(histFacetY !== "") ? Plot.axisFy({ // Facet axis with just the label
label: formatLabel(histFacetY),
labelAnchor: "top",
fontSize: largeFontSize,
ticks: [ ],
}) : [],
Plot.rectY(filtered,
Plot.binX({y: histoY},
{x: histoX,
fill: (histoFill !== "") ? histoFill : "steelblue",
opacity: histoOpacity,
thresholds: 20}
)
),
// Mean and SE marks
( (histoStats == "± SE") || (histoStats == "± 95% CI") )?
Plot.dot(filtered,
Plot.groupY(
{x: "mean"},
{x: histoX,
y: 0,
fill: (histoFill !== "") ? histoFill : "black",
r: 5}
)) : [],
(histoStats == "± SE") ?
Plot.link(filtered,
Plot.groupY({
x1: (filtered) => d3.mean(filtered)
- d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
x2: (filtered) => d3.mean(filtered)
+ d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{x: histoX,
y: 0,
stroke: (histoFill !== "") ? histoFill : "black",
strokeWidth: 3}
)) : [],
(histoStats == "± 95% CI") ?
Plot.link(filtered,
Plot.groupY({
x1: (filtered) => d3.mean(filtered)
- 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
x2: (filtered) => d3.mean(filtered)
+ 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{x: histoX,
y: 0,
stroke: (histoFill !== "") ? histoFill : "black",
strokeWidth: 3}
)) : [],
// Plot.ruleY([0])
]
});
html
`<div id="histoEditor" autocorrect="off" spellcheck="false">
<p>Figure #. <b>Title.</b></p><p>Caption.</p>
</div>`( ((stripOrient == "Vertical ┋") && (stripX == stripDotFill)) ||
((stripOrient == "Horizontal ┉") && (stripY == stripDotFill)) ) ?
// Big IF stripDotFill is the same as the main x or y variable
// No subplots needed
Plot.plot({
axis: null, // Will specify these in detail with axis marks
symbol:
{
legend: true,
tickFormat: formatLabel,
label: formatLabel(stripDotFill),
},
facet: {
marginRight: (stripOrient == "Horizontal ┉") ? 90 : [],
},
height: stripHeight,
insetTop: ((stripOrient == "Horizontal ┉") &&
(dotsJitterOrDodge == "Jitter")) ?
10 : [],
insetBottom: ((stripOrient == "Horizontal ┉") &&
(dotsJitterOrDodge == "Jitter")) ?
10 : [],
insetLeft: ((stripOrient == "Vertical ┋") &&
(dotsJitterOrDodge == "Jitter")) ?
10 : [],
insetRight: ((stripOrient == "Vertical ┋") &&
(dotsJitterOrDodge == "Jitter")) ?
10 : [],
// marginTop: 36, // more room for facets
marginBottom: 42,
marks: [
// Axis marks
(stripOrient == "Vertical ┋") ? // Main axis w/o label
Plot.axisY({
label: null,
}) :
Plot.axisX({
label: null,
}),
(stripOrient == "Vertical ┋") ? // Main axis label, large font
Plot.axisY({
label: formatLabel(stripY),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}) :
Plot.axisX({
label: formatLabel(stripX),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(stripOrient == "Vertical ┋") ? // Facet axis w/o label
Plot.axisFx({
anchor: "bottom",
label: null,
tickFormat: formatLabel,
}) :
Plot.axisFy({
label: null,
tickFormat: formatLabel,
}),
(stripOrient == "Vertical ┋") ? // Facet axis label, large font
Plot.axisFx({
anchor: "bottom",
label: formatLabel(stripX),
fontSize: largeFontSize,
ticks: [],
}) :
Plot.axisFy({
label: formatLabel(stripY),
labelAnchor: "top",
fontSize: largeFontSize,
ticks: [],
}),
// Grid marks
(stripOrient == "Vertical ┋") ?
Plot.gridY({}) :
Plot.gridX({}),
// Box marks
((stripOrient == "Vertical ┋") && (stripStats == "BoxPlot")) ?
Plot.boxY(filtered,
{fx: stripX,
y: stripY,
fill: "gray",
fillOpacity: 0.35,
stroke: "gray"}) : [],
((stripOrient == "Horizontal ┉") && (stripStats == "BoxPlot")) ?
Plot.boxX(filtered,
{fy: stripY,
x: stripX,
fill: "gray",
fillOpacity: 0.35,
stroke: "gray"}) : [],
// Jitter marks
((stripOrient == "Vertical ┋") && (dotsJitterOrDodge == "Jitter")) ?
Plot.dot(filtered, {
fx: stripX,
x: Math.random,
y: stripY,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,
}) : [],
((stripOrient == "Horizontal ┉") && (dotsJitterOrDodge == "Jitter")) ?
Plot.dot(filtered, {
axis: null,
x: stripX,
fy: stripY,
y: Math.random,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,
}) : [],
// Dodge marks
((stripOrient == "Vertical ┋") && (dotsJitterOrDodge == "Dodge")) ?
Plot.dot(filtered,
Plot.dodgeX("middle", {
fx: stripX,
y: stripY,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,})) : [],
((stripOrient == "Horizontal ┉") && (dotsJitterOrDodge == "Dodge")) ?
Plot.dot(filtered, // Horizontal
Plot.dodgeY("middle", {
fy: stripY,
x: stripX,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,})) : [],
// Mean and SE marks
((stripOrient == "Vertical ┋") && (stripStats == "Mean ± SE")) ?
Plot.dot(
filtered,
Plot.groupX(
{y: "mean"},
{y: stripY,
fx: stripX,
x: 0.5,
fill: "black",
r: 5}
)) : [],
((stripOrient == "Vertical ┋") && (stripStats == "Mean ± SE")) ?
Plot.link(filtered,
Plot.groupX({
y1: (filtered) => d3.mean(filtered)
- d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
y2: (filtered) => d3.mean(filtered)
+ d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{y: stripY,
fx: stripX,
x: 0.5,
stroke: "black",
strokeWidth: 2}
)) : [],
((stripOrient == "Horizontal ┉") && (stripStats == "Mean ± SE")) ?
Plot.dot(filtered,
Plot.groupY(
{x: "mean"},
{x: stripX,
fy: stripY,
y: 0.5,
fill: "black",
r: 5}
)) : [],
((stripOrient == "Horizontal ┉") && (stripStats == "Mean ± SE")) ?
Plot.link(filtered,
Plot.groupY({
x1: (filtered) => d3.mean(filtered)
- d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
x2: (filtered) => d3.mean(filtered)
+ d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{x: stripX,
fy: stripY,
y: 0.5,
stroke: "black",
strokeWidth: 3}
)) : [],
// Mean and confidence interval marks
((stripOrient == "Vertical ┋") && (stripStats == "Mean ± 95% CI")) ?
Plot.dot(
filtered,
Plot.groupX(
{y: "mean"},
{y: stripY,
fx: stripX,
x: 0.5,
fill: "black",
r: 5}
)) : [],
((stripOrient == "Vertical ┋") && (stripStats == "Mean ± 95% CI")) ?
Plot.link(filtered,
Plot.groupX({
y1: (filtered) => d3.mean(filtered)
- 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
y2: (filtered) => d3.mean(filtered)
+ 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{y: stripY,
fx: stripX,
x: 0.5,
stroke: "black",
strokeWidth: 2}
)) : [],
((stripOrient == "Horizontal ┉") && (stripStats == "Mean ± 95% CI")) ?
Plot.dot(filtered,
Plot.groupY(
{x: "mean"},
{x: stripX,
fy: stripY,
y: 0.5,
fill: "black",
r: 5}
)) : [],
((stripOrient == "Horizontal ┉") && (stripStats == "Mean ± 95% CI")) ?
Plot.link(filtered,
Plot.groupY({
x1: (filtered) => d3.mean(filtered)
- 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
x2: (filtered) => d3.mean(filtered)
+ 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{x: stripX,
fy: stripY,
y: 0.5,
stroke: "black",
strokeWidth: 3}
)) : [],
],
width: stripWidth
})
// Big ELSE stripDotFill is the NOT same as the main x or y variable
// Subplots needed. Subplots implemented using undocumented render
// transform, as demonstrated by the extraordinary Fil:
// https://observablehq.com/@fil/subplots-1870
// see also: https://observablehq.com/@observablehq/plot-of-plots
:
Plot.plot({
height: stripHeight,
width: stripWidth,
marginLeft: (stripOrient == "Horizontal ┉") ? 80 : 40,
marginRight: (stripOrient == "Horizontal ┉") ? 80 : [],
marginBottom: 36,
marginTop: 36,
symbol:
{ legend: true,
tickFormat: formatLabel,
label: formatLabel(stripDotFill),
},
x: (stripOrient == "Horizontal ┉") ?
{ domain: [Math.min(...Plot.valueof(filtered, stripX)),
Math.max(...Plot.valueof(filtered, stripX))],
} : [],
y: (stripOrient == "Vertical ┋") ?
{ domain: [Math.min(...Plot.valueof(filtered, stripY)),
Math.max(...Plot.valueof(filtered, stripY))],
} : [],
fx: (stripOrient == "Horizontal ┉") ?
{ axis: null } : [], // Will set with axis mark
fy: (stripOrient == "Vertical ┋") ?
{ axis: null } : [], // Will set with axis mark
marks: [
// Grid and axis marks
(stripOrient == "Vertical ┋") ? Plot.gridY() : Plot.gridX(),
(stripOrient == "Horizontal ┉") ?
Plot.axisFy({
label: formatLabel(stripY),
fontSize: largeFontSize,
labelAnchor: "top",
ticks: [],
}) : [],
(stripOrient == "Vertical ┋") ?
Plot.axisFx({
label: formatLabel(stripX),
fontSize: largeFontSize,
ticks: [],
}) : [],
(stripOrient == "Horizontal ┉") ?
Plot.axisFy({
label: null,
tickFormat: formatLabel,
}) : [],
(stripOrient == "Vertical ┋") ?
Plot.axisFx({
label: null,
tickFormat: formatLabel,
}) : [],
(stripOrient == "Horizontal ┉") ?
Plot.axisX({
label: formatLabel(stripX),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}) : [],
(stripOrient == "Vertical ┋") ?
Plot.axisY({
label: formatLabel(stripY),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}) : [],
(stripOrient == "Horizontal ┉") ?
Plot.axisX({
label: null,
tickFormat: formatLabel,
}) : [],
(stripOrient == "Vertical ┋") ?
Plot.axisY({
label: null,
tickFormat: formatLabel,
}) : [],
// This Kluge puts symbols in the color legend
Plot.dot(filtered, {
fill: stripDotFill,
symbol: stripDotFill,
}),
// Text marks - Where the subplots are
(stripOrient == "Horizontal ┉") ?
Plot.text(getUniqueValsOfColumn(filtered, stripY), {
frameAnchor: "middle",
text: Plot.identity,
fy: Plot.identity, // facets vertically by stripY
render([i], { scales }, { channels }, dimensions) {
return svg`<g>${
Plot.plot({
...dimensions,
...scales,
x: { ...scales.x, axis: null },
y: { axis: null },
fy: { axis: null },
marks:[
// Axis marks
Plot.axisFy({
anchor: "left",
tickFormat: formatLabel,
label: formatLabel(stripDotFill),
}),
// Box marks
(stripStats == "BoxPlot") ?
Plot.boxX(filtered, {
filter: (d) => d[stripY] === channels.text.value[i],
fy: stripDotFill,
x: stripX,
// fill: stripStatsColor,
fill: "gray",
fillOpacity: 0.35,
stroke: "gray"}) : [],
// Dot marks -- Jitter
(dotsJitterOrDodge == "Jitter") ?
Plot.dot(filtered, {
filter: (d) => d[stripY] === channels.text.value[i],
fy: stripDotFill,
y: Math.random,
x: stripX,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,
}) : [],
// Dot marks -- Dodge
(dotsJitterOrDodge == "Dodge") ?
Plot.dot(filtered,
Plot.dodgeY("middle", {
filter: (d) => d[stripY] === channels.text.value[i],
fy: stripDotFill,
x: stripX,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,
})) : [],
// Mean marks
((stripStats == "Mean ± SE") || (stripStats == "Mean ± 95% CI")) ?
Plot.dot(
filtered,
Plot.groupY(
{x: "mean"},
{
filter: (d) => d[stripY] === channels.text.value[i],
x: stripX,
fy: stripDotFill,
y: 0.5,
fill: "black",
r: 5
}
)) : [],
// SE marks
(stripStats == "Mean ± SE") ?
Plot.link(filtered,
Plot.groupY({
x1: (filtered) => d3.mean(filtered)
- d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
x2: (filtered) => d3.mean(filtered)
+ d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{
filter: (d) => d[stripY] === channels.text.value[i],
x: stripX,
fy: stripDotFill,
y: 0.5,
stroke: "black",
strokeWidth: 2}
)) : [],
// 95% CI marks
(stripStats == "Mean ± 95% CI") ?
Plot.link(filtered,
Plot.groupY({
x1: (filtered) => d3.mean(filtered)
- 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
x2: (filtered) => d3.mean(filtered)
+ 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{
filter: (d) => d[stripY] === channels.text.value[i],
x: stripX,
fy: stripDotFill,
y: 0.5,
stroke: "black",
strokeWidth: 2}
)) : [],
]
})
}`;
}
}) : [],
// Text marks - Where the subplots are
(stripOrient == "Vertical ┋") ?
Plot.text(getUniqueValsOfColumn(filtered, stripX), {
frameAnchor: "middle",
text: Plot.identity,
fx: Plot.identity, // facets vertically by stripX
render([i], { scales }, { channels }, dimensions) {
return svg`<g>${
Plot.plot({
...dimensions,
...scales,
insetLeft: 4,
insetRight: 4,
y: { ...scales.y, axis: null },
x: { axis: null },
fx: { axis: null },
marks:[
// Axis marks
Plot.axisFx({
anchor: "bottom",
label: formatLabel(stripDotFill),
tickFormat: formatLabel,
}),
// Box marks
(stripStats == "BoxPlot") ?
Plot.boxY(filtered, {
filter: (d) => d[stripX] === channels.text.value[i],
fx: stripDotFill,
y: stripY,
fill: "gray",
fillOpacity: 0.35,
stroke: "gray"}) : [],
// Dot marks -- Jitter
(dotsJitterOrDodge == "Jitter") ?
Plot.dot(filtered, {
filter: (d) => d[stripX] === channels.text.value[i],
fx: stripDotFill,
x: Math.random,
y: stripY,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,
}) : [],
// Dot marks -- Dodge
(dotsJitterOrDodge == "Dodge") ?
Plot.dot(filtered,
Plot.dodgeX("middle", {
filter: (d) => d[stripX] === channels.text.value[i],
fx: stripDotFill,
y: stripY,
fill: stripDotFill,
opacity: dotsOpacity,
symbol: stripDotFill,
})) : [],
// Mean marks
((stripStats == "Mean ± SE") || (stripStats == "Mean ± 95% CI")) ?
Plot.dot(
filtered,
Plot.groupX(
{y: "mean"},
{
filter: (d) => d[stripX] === channels.text.value[i],
y: stripY,
fx: stripDotFill,
x: 0.5,
fill: "black",
r: 5
}
)) : [],
// SE marks
(stripStats == "Mean ± SE") ?
Plot.link(filtered,
Plot.groupX({
y1: (filtered) => d3.mean(filtered)
- d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
y2: (filtered) => d3.mean(filtered)
+ d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{
filter: (d) => d[stripX] === channels.text.value[i],
y: stripY,
fx: stripDotFill,
x: 0.5,
stroke: "black",
strokeWidth: 2}
)) : [],
// 95% CI marks
(stripStats == "Mean ± 95% CI") ?
Plot.link(filtered,
Plot.groupX({
y1: (filtered) => d3.mean(filtered)
- 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered)),
y2: (filtered) => d3.mean(filtered)
+ 1.96*d3.deviation(filtered)/Math.sqrt(d3.count(filtered))
},
{
filter: (d) => d[stripX] === channels.text.value[i],
y: stripY,
fx: stripDotFill,
x: 0.5,
stroke: "black",
strokeWidth: 2}
)) : [],
]
})
}`;
}
}) : [],
]
})
;html
`<div id="stripEditor" autocorrect="off" spellcheck="false">
<p>Figure #. <b>Title.</b></p><p>Caption.</p>
</div>`chart = {
const mData = getMarimekkoData(filtered, barXVar, barFill, barFacetX, barFacetY);
// From https://observablehq.com/@observablehq/plot-marimekko
const xy = (options) => marimekko({...options,
x: "mXVar",
y: "mYVar",
value: "mVal"});
// THE BIG IF ELSE
// Bar chart or marimekko
return (barCountsOrPercentages == "Bar") ? // THE BIG IF - Bar chart
Plot.plot({
// marginLeft: 60,
// x: {label: barXVar},
// y: {label: "Count"},
facet: {
data: filtered,
x: barFacetX,
y: barFacetY,
marginRight: 80,
// marginTop: 36,
},
fx: {
label: formatLabel(barFacetX),
},
fy: {
label: formatLabel(barFacetY),
},
color: {legend: true, tickFormat: formatLabel},
marginTop: 35, // more room for facets
marginBottom: 48,
marks: [
// Axis marks
Plot.axisX({ // Axis with just the ticks in the default fontSize
label: null,
tickFormat: formatLabel,
}),
Plot.axisX({ // Axis with just the label in custom fontSize
label: formatLabel(barXVar),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(barFacetX !== "") ? Plot.axisFx({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(barFacetX !== "") ? Plot.axisFx({ // Facet axis with just the label
label: formatLabel(barFacetX),
fontSize: largeFontSize,
ticks: [ ],
}) : [],
Plot.axisY({ // Axis with just the ticks in the default fontSize
label: null,
}),
Plot.axisY({ // Axis with just the label in the custom fontSize
label: "Count",
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(barFacetY !== "") ? Plot.axisFy({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(barFacetY !== "") ? Plot.axisFy({ // Facet axis with just the label
label: formatLabel(barFacetY),
labelAnchor: "top",
fontSize: largeFontSize,
ticks: [ ],
}) : [],
// Bars
Plot.barY(
filtered,
Plot.groupX({y: "count"},
{x: barXVar, fill: barFill, opacity: barOpacity}
)),
],
height: barChartHeight,
width: barChartWidth,
})
: // The big ELSE - marimekko
Plot.plot({
width: barChartWidth,
height: barChartHeight,
label: null,
facet: {data: mData,
x: "mFXVar",
y: "mFYVar",
marginRight: 80},
fx: {padding: 0.12},
fy: {padding: 0.12},
color: {legend: true, tickFormat: formatLabel},
marginTop: 35,
marginBottom: 48,
x: {percent: true},
y: {percent: true, ticks: 0, tickFormat: (d) => d === 100 ? `100%` : d},
marks: [
Plot.frame({opacity: 0.25}),
// Added these
Plot.axisX({ // Axis with just the ticks in the default fontSize
label: null,
ticks: (barFacetX == "") ? 10 : 5,
tickFormat: (d) => d === 100 ? `100%` : d
}),
Plot.axisX({ // Axis with just the label in custom fontSize
label: formatLabel(barXVar),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}),
(barFacetX !== "") ? Plot.axisFx({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(barFacetX !== "") ? Plot.axisFx({ // Facet axis with just the label
label: formatLabel(barFacetX),
fontSize: largeFontSize,
ticks: [ ],
}) : [],
(barXVar !== barFill) ?
Plot.axisY({ // Axis with just the ticks in the default fontSize
label: null,
ticks: (barFacetY == "") ? 10 : 5,
tickFormat: (d) => d === 100 ? `100%` : d
}) : [],
(barXVar !== barFill) ?
Plot.axisY({ // Axis with just the label in the custom fontSize
label: formatLabel(barFill),
fontSize: largeFontSize,
labelOffset: 36,
ticks: [],
}) : [],
(barFacetY !== "") ? Plot.axisFy({ // Facet axis with just the ticks
label: null,
tickFormat: formatLabel,
}) : [],
(barFacetY !== "") ? Plot.axisFy({ // Facet axis with just the label
label: formatLabel(barFacetY),
labelAnchor: "top",
fontSize: largeFontSize,
ticks: [ ],
}) : [],
Plot.rect(mData, xy({fill: "mYVar", fillOpacity: barOpacity})),
// Added 'formatLabel' ×3; made conditional on d.mVal > 0; conditional on barFill ≠ barXVar
Plot.text(mData, xy({text: d => (d.mVal > 0) ?
[formatLabel(d.mVal.toLocaleString("en")),
(barFill !== barXVar) ?
"y: " + formatLabel(d.mYVar) : formatLabel(d.mYVar),
(barFill !== barXVar) ?
"x: " + formatLabel(d.mXVar)
:
""
].join("\n") : "" })),
// Plot.text(mData, Plot.selectMaxX(xy({z: "mYVar", text: "mYVar", anchor: "right", textAnchor: "middle", lineAnchor: "bottom", rotate: 90, dx: 6}))),
// Made conditional on mData.mYVar !== mData.mXVar -- but it's redunant, because axis label is handled elsewhere.
// (mData.mYVar !== mData.mXVar) ? Plot.text(mData, Plot.selectMaxY(xy({z: "mXVar", text: "mXVar", anchor: "top", lineAnchor: "bottom", dy: -6}))) : [],
]
});
};
html
`<div id="barEditor" autocorrect="off" spellcheck="false">
<p>Figure #. <b>Title.</b></p><p>Caption.</p>
</div>`
I wrote ardeaPlot for my introductory biology students. The idea is to give them a chance to explore, with interesting datasets and as frictionlessly as possible, a few common types of graphs. My hope is that once students learn a bit about what graphical analyses can do for them, they’ll be motivated to master more general-purpose and complex software tools.
My work on ardeaPlot was inspired by SimBio’s GraphSmarts assessments, by the other participants in the GraphSmarts Faculty Mentoring Network, and by easyPlot. It was partially funded by Grappling with graphs: New Tools For Improving Graphing Practices of Undergraduate Biology Students (NSF# 2111150).
If you use ardeaPlot, I’d love to get an email about your experience.
Thanks,Jon C. Herron
ardeaPlot is made with
Quarto, including:
Observable js, especially:
Many thanks to the creators of these tools!
Sources for built-in datasets
Palmer Penguins — observations with missing data (NA) have been removed.
Elephants: Mduduzi Ndlovu et al. 2018
Primates and carnivores: Daniel L. Bowling et al. 2020
Hurricane lizards one: Colin M. Donihue et al. 2018
Hurricane lizards two: Colin M. Donihue et al. 2020
d3 = require("d3@7");
Quill = require("https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js");
// Global variables
largeFontSize = 13;
myColors = ["black", "gray", "lightgray", "steelblue", "darkseagreen"];
// Functions used across pages
function formatLabel(myString) {
if ((typeof myString !== "string") || (myString === "pH")){
return myString;
} else if ( (myString == null) || (myString.length === 0) ) {
return "";
} else {
myString = myString.replaceAll("_", " ");
return myString.charAt(0).toUpperCase() + myString.slice(1);
};
};
/* Old versions of getCategoricalColumns and getNumericalColumns
function getCategoricalColumns(data) {
const categoricalColumns = [];
if (data.length > 0) {
const firstRow = data[0];
for (const columnName in firstRow) {
if (typeof firstRow[columnName] === 'string') {
categoricalColumns.push(columnName);
}
}
}
return categoricalColumns;
};
function getNumericalColumns(data) {
if (!data || data.length === 0) {
return [];
}
const numericalColumns = [];
const firstRow = data[0];
for (const key in firstRow) {
if (typeof firstRow[key] === 'number') {
numericalColumns.push(key);
}
}
return numericalColumns;
};
*/
function getCategoricalColumns(data) {
if (!data || data.length === 0) {
return [];
};
const categoricalColumns = [];
const dataKeys = Object.keys(data[0]);
for (let i = 0; i < dataKeys.length; i++) {
let j = 0;
let myType = undefined;
while ( (j < data.length) && (myType !== 'string' ) && (myType !== 'number') ) {
myType = typeof data[j][dataKeys[i]];
j++;
};
if (myType === 'string') {
categoricalColumns.push(dataKeys[i]);
};
};
return categoricalColumns;
};
function getNumericalColumns(data) {
if (!data || data.length === 0) {
return [];
};
const numericalColumns = [];
const dataKeys = Object.keys(data[0]);
for (let i = 0; i < dataKeys.length; i++) {
let j = 0;
let myType = undefined;
while ( (j < data.length) && (myType !== 'string' ) && (myType !== 'number') ) {
myType = typeof data[j][dataKeys[i]];
j++;
};
if (myType === 'number') {
numericalColumns.push(dataKeys[i]);
};
};
return numericalColumns;
};
// May replicate Plot.valueof
function columnToArray(data, column) {
return data.map((row) => row[column]);
};
function getUniqueVals(array) {
return array.filter((value, index, self) => self.indexOf(value) === index);
};
function getUniqueValsOfColumn(data, column) {
return getUniqueVals(columnToArray(data, column));
};
function set(input, value) {
input.value = value;
input.dispatchEvent(new Event("input", {bubbles: true}));
};
populateWithOptions = function(myArray, defIndex) {
const returnArray = [];
for (let i = 0; i < myArray.length; i++) {
if (i == defIndex) {
returnArray.push("<option value='" + myArray[i] + "' selected>" + formatLabel(myArray[i]) + "</option>")
} else {
returnArray.push("<option value='" + myArray[i] + "'>" + formatLabel(myArray[i]) + "</option>")
};
};
return returnArray;
};
function quillDisplay(myBoolean, myElement) {
const elem = document.getElementById(myElement);
if (myBoolean == true) {
elem.style.display = "block";
} else {
elem.style.display = "none";
};
};
function quillResetText(myQuill) {
myQuill.setContents([
{ insert: 'Figure #. ' },
{ insert: 'Title', attributes: { bold: true } },
{ insert: '\n' },
{ insert: 'Caption.' },
{ insert: '\n' },
]);
};
function quillSetWidth(myNum, myElement) {
const elem = document.getElementById(myElement);
const myString = myNum.toString().concat("px");
elem.style.width = myString;
};