// Reject null, "", and the "." sentinel used in the source CSVs.
// (+null === 0 in JS, so unary + is unsafe on parsed nulls.)
parseNum = (v) => {
if (v === null || v === undefined) return null;
const s = String(v).trim();
if (s === "" || s === ".") return null;
const n = Number(s);
return Number.isFinite(n) ? n : null;
}Adjusted Lifetime Prevalence — Documentation
This file holds the data processing and component definitions for the Adjusted Lifetime Prevalence dashboard. index.qmd imports and assembles these components. Source: Chapter 3 Tables & Figures (2025 edition), Figures 114–119.
Missing-value helper
Load data
data = rawData.map(d => ({
drug: d.drug,
drug_label: d.drug_label,
figure: d.figure,
age: d.age,
lifetime: parseNum(d.lifetime),
lifetime_adjusted: parseNum(d.lifetime_adjusted)
}))Drug dropdown (with URL-hash persistence)
// Inlined persist-input: keeps the selected drug in the URL hash so a
// shared link reopens on the same drug. (Mirrors @john-guerra/persist-input.)
PersistInput = function PersistInput(key, input) {
const params = new URLSearchParams(location.hash.slice(1));
const saved = params.get(key);
if (saved !== null) {
try { input.value = saved; input.dispatchEvent(new Event("input", { bubbles: true })); }
catch (e) { /* value not in option set; ignore */ }
}
input.addEventListener("input", () => {
const p = new URLSearchParams(location.hash.slice(1));
p.set(key, input.value);
history.replaceState(null, "", "#" + p.toString());
});
return input;
}viewof select = PersistInput("drug", Inputs.select(
new Map(drugOptions.map(o => [o.label, o.value])),
{ label: "Select Drug:", value: "mj" }
))Bar chart
plots = {
const width = 820;
const marginBottom = 46;
const plot = Plot.plot({
width,
height: 460,
marginTop: 16,
marginRight: 20,
marginBottom,
marginLeft: 46,
x: {
label: "Age Group",
domain: ageOrder.filter(a => current.some(d => d.age === a)),
tickSize: 0
},
y: {
label: "Percent",
domain: [0, 100],
grid: true,
ticks: 5
},
style: {
fontFamily: "Arial, Helvetica, sans-serif",
fontSize: "11px",
color: "#000066"
},
marks: [
Plot.barY(current, {
x: "age",
y: "lifetime_adjusted",
fill: barColor
}),
Plot.ruleY([0], { stroke: "#000066" }),
Plot.text(current, {
x: "age",
y: "lifetime_adjusted",
text: d => d.lifetime_adjusted.toFixed(1),
dy: -6,
fontSize: 9,
fill: "#000066"
})
]
});
return plot;
}Plot title
Estimates table (dropdown accordion)
tableRows = current.map(d => ({
age: d.age,
lifetime: d.lifetime,
lifetime_adjusted: d.lifetime_adjusted
}))estimatesTable = Inputs.table(tableRows, {
columns: ["age", "lifetime_adjusted"],
header: {
age: "Age Group",
lifetime_adjusted: "Lifetime, Adjusted"
},
format: {
age: d => d,
lifetime: fmtPct,
lifetime_adjusted: fmtPct
}
})accordionButtons =
html`
<div style="width: 700px; max-width: 100%; border-top: 1px solid var(--bs-border-color); border-bottom: 1px solid var(--bs-border-color);" class="accordion accordion-flush" id="accordionFlushAlp">
<div class="accordion-item">
<h2 class="accordion-header" id="flush-headingAlp">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseAlp" aria-expanded="false" aria-controls="flush-collapseAlp">
${currentLabel}: Adjusted Lifetime Prevalence by Age Group
</button>
</h2>
<div id="flush-collapseAlp" class="accordion-collapse collapse" aria-labelledby="flush-headingAlp" data-bs-parent="#accordionFlushAlp">
<div class="accordion-body">
<div class="center"><div>${estimatesTable}</div></div>
</div>
</div>
</div>
</div>`Download data button
downloadButton = (data, filename) => {
let downloadData;
downloadData = new Blob([d3.csvFormat(data)], { type: "text/csv" });
const size = (downloadData.size / 1024).toFixed(0);
const button = DOM.download(
downloadData,
filename,
`Download ${filename} Dataset (~${size} KB)`
);
return button;
}
name = `${currentLabel} - Adjusted Lifetime Prevalence`
downloadWide = current.map(d => ({
"Age Group": d.age,
"Lifetime, Adjusted": d.lifetime_adjusted == null ? "." : d.lifetime_adjusted
}))
downloadData = downloadButton(downloadWide, name)Citation
citation = html`<div style="max-width: 750px;"><p style="font-size: small;">Suggested citation: Patrick, M. E., Miech, R. A., O'Malley, P. M., Jager, J. O., & Jang, J. B. (2026). Monitoring the Future Longitudinal Panel Study annual report: National data on substance use among adults ages 19 to 65, 1976–2025. Monitoring the Future Monograph Series. Ann Arbor, MI: Institute for Social Research, University of Michigan. <a href="https://monitoringthefuture.org/wp-content/uploads/2026/07/mtfpanel2026.pdf" target="_blank" rel="noopener noreferrer">MTF Panel Study Annual Report</a></p></div>`