small updates and corrections

This commit is contained in:
Jeremy Kidwell 2024-02-13 10:59:42 +00:00
parent f0c39da8b9
commit d7eb7814d5
14 changed files with 484 additions and 131 deletions

View file

@ -6,8 +6,13 @@ You can view a live demo of the book here: [https://kidwellj.github.io/hacking_r
The course here has been compiled with [quarto](https://quarto.org/), and so the live instance of the course is compiled from openly accessible resources located in this repository. If you're interested in doing something similar, there are a number of other options, some of which have lamentably turned commercial, including: [bookdown](https://github.com/rstudio/bookdown), [gitbook](https://docs.gitbook.com/), [mkdocs](https://www.mkdocs.org/), [readthedocs](https://readthedocs.org) which technically uses [Sphinx](http://www.sphinx-doc.org/en/master/) or [daux](https://daux.io/).
Directory structure includes:
Top level files and directories in this repository are:
* `README.md` this README file displayed on Github
* `hacking_religion` a folder containing the quarto code and accompanying files which are used to render the book in html and .pdf formats
* `docs` a rendered sample version of the most recent version of the book (used for )
Directory structure includes:
* `_book` a folder containing the compiled book in html and .pdf formats
* `references.bib` a bibliography of items used for the course in [BibTeX format](http://www.bibtex.org/Format/)
* `preface.qmd` Preface
@ -22,8 +27,13 @@ Directory structure includes:
1. clone this repository using `git clone https://github.com/kidwellj/hacking_religion_textbook.git` ([install git](https://git-scm.com/downloads) if you haven't already)
2. [install quarto](https://quarto.org/docs/get-started/)
3. change to the `hacking_religion` subdirectory and run `quarto preview`
4. alternatively you can render a copy of the book using `quarto render`
3. install the necessary pre-requisite R libraries, including `knitr` and `rmarkdown`. This can be done by running the following commands in an RStudio console:
```{r}
install.packages("knitr")
install.packages("rmarkdown")
```
4. change to the `hacking_religion` subdirectory and run `quarto preview` to get a temporary preview of the book as a website running on your local PC.
5. alternatively you can render a copy of the book using `quarto render`.
# Cookbook

View file

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.3.450">
<meta name="generator" content="quarto-1.4.549">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
@ -48,7 +48,13 @@ ul.task-list li input[type="checkbox"] {
"collapse-after": 3,
"panel-placement": "start",
"type": "textbox",
"limit": 20,
"limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
@ -57,6 +63,7 @@ ul.task-list li input[type="checkbox"] {
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit",
"search-label": "Search"
@ -72,12 +79,12 @@ ul.task-list li input[type="checkbox"] {
<header id="quarto-header" class="headroom fixed-top">
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./index.html">Introduction: Hacking Religion</a></li></ol></nav>
<a class="flex-grow-1" role="button" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./index.html">Introduction: Hacking Religion</a></li></ol></nav>
<a class="flex-grow-1" role="button" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
<button type="button" class="btn quarto-search-button" aria-label="" onclick="window.quartoOpenSearch();">
<i class="bi bi-search"></i>
</button>
@ -87,7 +94,7 @@ ul.task-list li input[type="checkbox"] {
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal sidebar-navigation floating overflow-auto">
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation floating overflow-auto">
<div class="pt-lg-2 mt-2 text-left sidebar-header">
<div class="sidebar-title mb-0 py-0">
<a href="./">Hacking Religion: TRS &amp; Data Science in Action</a>
@ -151,7 +158,7 @@ ul.task-list li input[type="checkbox"] {
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass"></div>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
@ -198,8 +205,10 @@ ul.task-list li input[type="checkbox"] {
</div>
</header>
<section id="introduction-hacking-religion" class="level1 unnumbered">
<h1 class="unnumbered">Introduction: Hacking Religion</h1>
<section id="why-this-book" class="level2">
@ -318,10 +327,9 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
// clear code selection
e.clearSelection();
});
function tippyHover(el, contentFn) {
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = {
allowHTML: true,
content: contentFn,
maxWidth: 500,
delay: 100,
arrow: false,
@ -331,8 +339,17 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start'
placement: 'bottom-start',
};
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
@ -346,6 +363,125 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
const note = window.document.getElementById(id);
return note.innerHTML;
});
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
// TODO in 1.5, we should make sure this works without a callout special case
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
@ -388,6 +524,7 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
@ -413,6 +550,32 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
});
selectedAnnoteEl = undefined;
};
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
@ -476,7 +639,7 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
<div class="nav-page nav-page-previous">
</div>
<div class="nav-page nav-page-next">
<a href="./chapter_1.html" class="pagination-link">
<a href="./chapter_1.html" class="pagination-link" aria-label="<span class='chapter-number'>1</span>&nbsp; <span class='chapter-title'>The 2021 UK Census</span>">
<span class="nav-page-text"><span class="chapter-number">1</span>&nbsp; <span class="chapter-title">The 2021 UK Census</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
@ -485,4 +648,5 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
</body></html>

View file

@ -1,8 +1,14 @@
/*!
* Bootstrap Icons v1.11.1 (https://icons.getbootstrap.com/)
* Copyright 2019-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
*/
@font-face {
font-display: block;
font-family: "bootstrap-icons";
src:
url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
url("./bootstrap-icons.woff?2820a3852bdb9a5832199cc61cec4e65") format("woff");
}
.bi::before,
@ -441,7 +447,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-cloud-fog2::before { content: "\f2a2"; }
.bi-cloud-hail-fill::before { content: "\f2a3"; }
.bi-cloud-hail::before { content: "\f2a4"; }
.bi-cloud-haze-1::before { content: "\f2a5"; }
.bi-cloud-haze-fill::before { content: "\f2a6"; }
.bi-cloud-haze::before { content: "\f2a7"; }
.bi-cloud-haze2-fill::before { content: "\f2a8"; }
@ -1437,21 +1442,16 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-dpad::before { content: "\f687"; }
.bi-ear-fill::before { content: "\f688"; }
.bi-ear::before { content: "\f689"; }
.bi-envelope-check-1::before { content: "\f68a"; }
.bi-envelope-check-fill::before { content: "\f68b"; }
.bi-envelope-check::before { content: "\f68c"; }
.bi-envelope-dash-1::before { content: "\f68d"; }
.bi-envelope-dash-fill::before { content: "\f68e"; }
.bi-envelope-dash::before { content: "\f68f"; }
.bi-envelope-exclamation-1::before { content: "\f690"; }
.bi-envelope-exclamation-fill::before { content: "\f691"; }
.bi-envelope-exclamation::before { content: "\f692"; }
.bi-envelope-plus-fill::before { content: "\f693"; }
.bi-envelope-plus::before { content: "\f694"; }
.bi-envelope-slash-1::before { content: "\f695"; }
.bi-envelope-slash-fill::before { content: "\f696"; }
.bi-envelope-slash::before { content: "\f697"; }
.bi-envelope-x-1::before { content: "\f698"; }
.bi-envelope-x-fill::before { content: "\f699"; }
.bi-envelope-x::before { content: "\f69a"; }
.bi-explicit-fill::before { content: "\f69b"; }
@ -1461,8 +1461,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-list-columns-reverse::before { content: "\f69f"; }
.bi-list-columns::before { content: "\f6a0"; }
.bi-meta::before { content: "\f6a1"; }
.bi-mortorboard-fill::before { content: "\f6a2"; }
.bi-mortorboard::before { content: "\f6a3"; }
.bi-nintendo-switch::before { content: "\f6a4"; }
.bi-pc-display-horizontal::before { content: "\f6a5"; }
.bi-pc-display::before { content: "\f6a6"; }
@ -1481,7 +1479,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-send-check::before { content: "\f6b3"; }
.bi-send-dash-fill::before { content: "\f6b4"; }
.bi-send-dash::before { content: "\f6b5"; }
.bi-send-exclamation-1::before { content: "\f6b6"; }
.bi-send-exclamation-fill::before { content: "\f6b7"; }
.bi-send-exclamation::before { content: "\f6b8"; }
.bi-send-fill::before { content: "\f6b9"; }
@ -1493,7 +1490,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-send-x::before { content: "\f6bf"; }
.bi-send::before { content: "\f6c0"; }
.bi-steam::before { content: "\f6c1"; }
.bi-terminal-dash-1::before { content: "\f6c2"; }
.bi-terminal-dash::before { content: "\f6c3"; }
.bi-terminal-plus::before { content: "\f6c4"; }
.bi-terminal-split::before { content: "\f6c5"; }
@ -1523,7 +1519,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-usb-symbol::before { content: "\f6dd"; }
.bi-usb::before { content: "\f6de"; }
.bi-boombox-fill::before { content: "\f6df"; }
.bi-displayport-1::before { content: "\f6e0"; }
.bi-displayport::before { content: "\f6e1"; }
.bi-gpu-card::before { content: "\f6e2"; }
.bi-memory::before { content: "\f6e3"; }
@ -1536,8 +1531,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-pci-card::before { content: "\f6ea"; }
.bi-router-fill::before { content: "\f6eb"; }
.bi-router::before { content: "\f6ec"; }
.bi-ssd-fill::before { content: "\f6ed"; }
.bi-ssd::before { content: "\f6ee"; }
.bi-thunderbolt-fill::before { content: "\f6ef"; }
.bi-thunderbolt::before { content: "\f6f0"; }
.bi-usb-drive-fill::before { content: "\f6f1"; }
@ -1644,7 +1637,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-pdf::before { content: "\f756"; }
.bi-filetype-php::before { content: "\f757"; }
.bi-filetype-png::before { content: "\f758"; }
.bi-filetype-ppt-1::before { content: "\f759"; }
.bi-filetype-ppt::before { content: "\f75a"; }
.bi-filetype-psd::before { content: "\f75b"; }
.bi-filetype-py::before { content: "\f75c"; }
@ -1660,7 +1652,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-txt::before { content: "\f766"; }
.bi-filetype-wav::before { content: "\f767"; }
.bi-filetype-woff::before { content: "\f768"; }
.bi-filetype-xls-1::before { content: "\f769"; }
.bi-filetype-xls::before { content: "\f76a"; }
.bi-filetype-xml::before { content: "\f76b"; }
.bi-filetype-yml::before { content: "\f76c"; }
@ -1703,56 +1694,38 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-json::before { content: "\f791"; }
.bi-filetype-pptx::before { content: "\f792"; }
.bi-filetype-xlsx::before { content: "\f793"; }
.bi-1-circle-1::before { content: "\f794"; }
.bi-1-circle-fill-1::before { content: "\f795"; }
.bi-1-circle-fill::before { content: "\f796"; }
.bi-1-circle::before { content: "\f797"; }
.bi-1-square-fill::before { content: "\f798"; }
.bi-1-square::before { content: "\f799"; }
.bi-2-circle-1::before { content: "\f79a"; }
.bi-2-circle-fill-1::before { content: "\f79b"; }
.bi-2-circle-fill::before { content: "\f79c"; }
.bi-2-circle::before { content: "\f79d"; }
.bi-2-square-fill::before { content: "\f79e"; }
.bi-2-square::before { content: "\f79f"; }
.bi-3-circle-1::before { content: "\f7a0"; }
.bi-3-circle-fill-1::before { content: "\f7a1"; }
.bi-3-circle-fill::before { content: "\f7a2"; }
.bi-3-circle::before { content: "\f7a3"; }
.bi-3-square-fill::before { content: "\f7a4"; }
.bi-3-square::before { content: "\f7a5"; }
.bi-4-circle-1::before { content: "\f7a6"; }
.bi-4-circle-fill-1::before { content: "\f7a7"; }
.bi-4-circle-fill::before { content: "\f7a8"; }
.bi-4-circle::before { content: "\f7a9"; }
.bi-4-square-fill::before { content: "\f7aa"; }
.bi-4-square::before { content: "\f7ab"; }
.bi-5-circle-1::before { content: "\f7ac"; }
.bi-5-circle-fill-1::before { content: "\f7ad"; }
.bi-5-circle-fill::before { content: "\f7ae"; }
.bi-5-circle::before { content: "\f7af"; }
.bi-5-square-fill::before { content: "\f7b0"; }
.bi-5-square::before { content: "\f7b1"; }
.bi-6-circle-1::before { content: "\f7b2"; }
.bi-6-circle-fill-1::before { content: "\f7b3"; }
.bi-6-circle-fill::before { content: "\f7b4"; }
.bi-6-circle::before { content: "\f7b5"; }
.bi-6-square-fill::before { content: "\f7b6"; }
.bi-6-square::before { content: "\f7b7"; }
.bi-7-circle-1::before { content: "\f7b8"; }
.bi-7-circle-fill-1::before { content: "\f7b9"; }
.bi-7-circle-fill::before { content: "\f7ba"; }
.bi-7-circle::before { content: "\f7bb"; }
.bi-7-square-fill::before { content: "\f7bc"; }
.bi-7-square::before { content: "\f7bd"; }
.bi-8-circle-1::before { content: "\f7be"; }
.bi-8-circle-fill-1::before { content: "\f7bf"; }
.bi-8-circle-fill::before { content: "\f7c0"; }
.bi-8-circle::before { content: "\f7c1"; }
.bi-8-square-fill::before { content: "\f7c2"; }
.bi-8-square::before { content: "\f7c3"; }
.bi-9-circle-1::before { content: "\f7c4"; }
.bi-9-circle-fill-1::before { content: "\f7c5"; }
.bi-9-circle-fill::before { content: "\f7c6"; }
.bi-9-circle::before { content: "\f7c7"; }
.bi-9-square-fill::before { content: "\f7c8"; }
@ -1771,8 +1744,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-browser-edge::before { content: "\f7d5"; }
.bi-browser-firefox::before { content: "\f7d6"; }
.bi-browser-safari::before { content: "\f7d7"; }
.bi-c-circle-1::before { content: "\f7d8"; }
.bi-c-circle-fill-1::before { content: "\f7d9"; }
.bi-c-circle-fill::before { content: "\f7da"; }
.bi-c-circle::before { content: "\f7db"; }
.bi-c-square-fill::before { content: "\f7dc"; }
@ -1783,8 +1754,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-car-front::before { content: "\f7e1"; }
.bi-cassette-fill::before { content: "\f7e2"; }
.bi-cassette::before { content: "\f7e3"; }
.bi-cc-circle-1::before { content: "\f7e4"; }
.bi-cc-circle-fill-1::before { content: "\f7e5"; }
.bi-cc-circle-fill::before { content: "\f7e6"; }
.bi-cc-circle::before { content: "\f7e7"; }
.bi-cc-square-fill::before { content: "\f7e8"; }
@ -1803,8 +1772,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-sql::before { content: "\f7f5"; }
.bi-fire::before { content: "\f7f6"; }
.bi-google-play::before { content: "\f7f7"; }
.bi-h-circle-1::before { content: "\f7f8"; }
.bi-h-circle-fill-1::before { content: "\f7f9"; }
.bi-h-circle-fill::before { content: "\f7fa"; }
.bi-h-circle::before { content: "\f7fb"; }
.bi-h-square-fill::before { content: "\f7fc"; }
@ -1813,8 +1780,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-lungs-fill::before { content: "\f7ff"; }
.bi-lungs::before { content: "\f800"; }
.bi-microsoft-teams::before { content: "\f801"; }
.bi-p-circle-1::before { content: "\f802"; }
.bi-p-circle-fill-1::before { content: "\f803"; }
.bi-p-circle-fill::before { content: "\f804"; }
.bi-p-circle::before { content: "\f805"; }
.bi-p-square-fill::before { content: "\f806"; }
@ -1823,8 +1788,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-pass::before { content: "\f809"; }
.bi-prescription::before { content: "\f80a"; }
.bi-prescription2::before { content: "\f80b"; }
.bi-r-circle-1::before { content: "\f80c"; }
.bi-r-circle-fill-1::before { content: "\f80d"; }
.bi-r-circle-fill::before { content: "\f80e"; }
.bi-r-circle::before { content: "\f80f"; }
.bi-r-square-fill::before { content: "\f810"; }
@ -2016,3 +1979,100 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-sina-weibo::before { content: "\f8ca"; }
.bi-tencent-qq::before { content: "\f8cb"; }
.bi-wikipedia::before { content: "\f8cc"; }
.bi-alphabet-uppercase::before { content: "\f2a5"; }
.bi-alphabet::before { content: "\f68a"; }
.bi-amazon::before { content: "\f68d"; }
.bi-arrows-collapse-vertical::before { content: "\f690"; }
.bi-arrows-expand-vertical::before { content: "\f695"; }
.bi-arrows-vertical::before { content: "\f698"; }
.bi-arrows::before { content: "\f6a2"; }
.bi-ban-fill::before { content: "\f6a3"; }
.bi-ban::before { content: "\f6b6"; }
.bi-bing::before { content: "\f6c2"; }
.bi-cake::before { content: "\f6e0"; }
.bi-cake2::before { content: "\f6ed"; }
.bi-cookie::before { content: "\f6ee"; }
.bi-copy::before { content: "\f759"; }
.bi-crosshair::before { content: "\f769"; }
.bi-crosshair2::before { content: "\f794"; }
.bi-emoji-astonished-fill::before { content: "\f795"; }
.bi-emoji-astonished::before { content: "\f79a"; }
.bi-emoji-grimace-fill::before { content: "\f79b"; }
.bi-emoji-grimace::before { content: "\f7a0"; }
.bi-emoji-grin-fill::before { content: "\f7a1"; }
.bi-emoji-grin::before { content: "\f7a6"; }
.bi-emoji-surprise-fill::before { content: "\f7a7"; }
.bi-emoji-surprise::before { content: "\f7ac"; }
.bi-emoji-tear-fill::before { content: "\f7ad"; }
.bi-emoji-tear::before { content: "\f7b2"; }
.bi-envelope-arrow-down-fill::before { content: "\f7b3"; }
.bi-envelope-arrow-down::before { content: "\f7b8"; }
.bi-envelope-arrow-up-fill::before { content: "\f7b9"; }
.bi-envelope-arrow-up::before { content: "\f7be"; }
.bi-feather::before { content: "\f7bf"; }
.bi-feather2::before { content: "\f7c4"; }
.bi-floppy-fill::before { content: "\f7c5"; }
.bi-floppy::before { content: "\f7d8"; }
.bi-floppy2-fill::before { content: "\f7d9"; }
.bi-floppy2::before { content: "\f7e4"; }
.bi-gitlab::before { content: "\f7e5"; }
.bi-highlighter::before { content: "\f7f8"; }
.bi-marker-tip::before { content: "\f802"; }
.bi-nvme-fill::before { content: "\f803"; }
.bi-nvme::before { content: "\f80c"; }
.bi-opencollective::before { content: "\f80d"; }
.bi-pci-card-network::before { content: "\f8cd"; }
.bi-pci-card-sound::before { content: "\f8ce"; }
.bi-radar::before { content: "\f8cf"; }
.bi-send-arrow-down-fill::before { content: "\f8d0"; }
.bi-send-arrow-down::before { content: "\f8d1"; }
.bi-send-arrow-up-fill::before { content: "\f8d2"; }
.bi-send-arrow-up::before { content: "\f8d3"; }
.bi-sim-slash-fill::before { content: "\f8d4"; }
.bi-sim-slash::before { content: "\f8d5"; }
.bi-sourceforge::before { content: "\f8d6"; }
.bi-substack::before { content: "\f8d7"; }
.bi-threads-fill::before { content: "\f8d8"; }
.bi-threads::before { content: "\f8d9"; }
.bi-transparency::before { content: "\f8da"; }
.bi-twitter-x::before { content: "\f8db"; }
.bi-type-h4::before { content: "\f8dc"; }
.bi-type-h5::before { content: "\f8dd"; }
.bi-type-h6::before { content: "\f8de"; }
.bi-backpack-fill::before { content: "\f8df"; }
.bi-backpack::before { content: "\f8e0"; }
.bi-backpack2-fill::before { content: "\f8e1"; }
.bi-backpack2::before { content: "\f8e2"; }
.bi-backpack3-fill::before { content: "\f8e3"; }
.bi-backpack3::before { content: "\f8e4"; }
.bi-backpack4-fill::before { content: "\f8e5"; }
.bi-backpack4::before { content: "\f8e6"; }
.bi-brilliance::before { content: "\f8e7"; }
.bi-cake-fill::before { content: "\f8e8"; }
.bi-cake2-fill::before { content: "\f8e9"; }
.bi-duffle-fill::before { content: "\f8ea"; }
.bi-duffle::before { content: "\f8eb"; }
.bi-exposure::before { content: "\f8ec"; }
.bi-gender-neuter::before { content: "\f8ed"; }
.bi-highlights::before { content: "\f8ee"; }
.bi-luggage-fill::before { content: "\f8ef"; }
.bi-luggage::before { content: "\f8f0"; }
.bi-mailbox-flag::before { content: "\f8f1"; }
.bi-mailbox2-flag::before { content: "\f8f2"; }
.bi-noise-reduction::before { content: "\f8f3"; }
.bi-passport-fill::before { content: "\f8f4"; }
.bi-passport::before { content: "\f8f5"; }
.bi-person-arms-up::before { content: "\f8f6"; }
.bi-person-raised-hand::before { content: "\f8f7"; }
.bi-person-standing-dress::before { content: "\f8f8"; }
.bi-person-standing::before { content: "\f8f9"; }
.bi-person-walking::before { content: "\f8fa"; }
.bi-person-wheelchair::before { content: "\f8fb"; }
.bi-shadows::before { content: "\f8fc"; }
.bi-suitcase-fill::before { content: "\f8fd"; }
.bi-suitcase-lg-fill::before { content: "\f8fe"; }
.bi-suitcase-lg::before { content: "\f8ff"; }
.bi-suitcase::before { content: "\f900"; }
.bi-suitcase2-fill::before { content: "\f901"; }
.bi-suitcase2::before { content: "\f902"; }
.bi-vignette::before { content: "\f903"; }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,7 +9,7 @@ const layoutMarginEls = () => {
// Find any conflicting margin elements and add margins to the
// top to prevent overlap
const marginChildren = window.document.querySelectorAll(
".column-margin.column-container > * "
".column-margin.column-container > *, .margin-caption, .aside"
);
let lastBottom = 0;
@ -18,25 +18,14 @@ const layoutMarginEls = () => {
// clear the top margin so we recompute it
marginChild.style.marginTop = null;
const top = marginChild.getBoundingClientRect().top + window.scrollY;
console.log({
childtop: marginChild.getBoundingClientRect().top,
scroll: window.scrollY,
top,
lastBottom,
});
if (top < lastBottom) {
const margin = lastBottom - top;
const marginChildStyle = window.getComputedStyle(marginChild);
const marginBottom = parseFloat(marginChildStyle["marginBottom"]);
const margin = lastBottom - top + marginBottom;
marginChild.style.marginTop = `${margin}px`;
}
const styles = window.getComputedStyle(marginChild);
const marginTop = parseFloat(styles["marginTop"]);
console.log({
top,
height: marginChild.getBoundingClientRect().height,
marginTop,
total: top + marginChild.getBoundingClientRect().height + marginTop,
});
lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
}
}
@ -46,7 +35,15 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
// Recompute the position of margin elements anytime the body size changes
if (window.ResizeObserver) {
const resizeObserver = new window.ResizeObserver(
throttle(layoutMarginEls, 50)
throttle(() => {
layoutMarginEls();
if (
window.document.body.getBoundingClientRect().width < 990 &&
isReaderMode()
) {
quartoToggleReader();
}
}, 50)
);
resizeObserver.observe(window.document.body);
}

View file

@ -85,6 +85,17 @@ window.document.addEventListener("DOMContentLoaded", function () {
}
}
function dashboardOffset() {
const dashboardNavEl = window.document.getElementById(
"quarto-dashboard-header"
);
if (dashboardNavEl !== null) {
return dashboardNavEl.clientHeight;
} else {
return 0;
}
}
function updateDocumentOffsetWithoutAnimation() {
updateDocumentOffset(false);
}
@ -92,7 +103,7 @@ window.document.addEventListener("DOMContentLoaded", function () {
function updateDocumentOffset(animated) {
// set body offset
const topOffset = headerOffset();
const bodyOffset = topOffset + footerOffset();
const bodyOffset = topOffset + footerOffset() + dashboardOffset();
const bodyEl = window.document.body;
bodyEl.setAttribute("data-bs-offset", topOffset);
bodyEl.style.paddingTop = topOffset + "px";
@ -205,9 +216,9 @@ window.document.addEventListener("DOMContentLoaded", function () {
// Observe size changed for the header
const headerEl = window.document.querySelector("header.fixed-top");
if (headerEl && window.ResizeObserver) {
const observer = new window.ResizeObserver(
updateDocumentOffsetWithoutAnimation
);
const observer = new window.ResizeObserver(() => {
setTimeout(updateDocumentOffsetWithoutAnimation, 0);
});
observer.observe(headerEl, {
attributes: true,
childList: true,
@ -233,7 +244,7 @@ window.document.addEventListener("DOMContentLoaded", function () {
// Fixup any sharing links that require urls
// Append url to any sharing urls
const sharingLinks = window.document.querySelectorAll(
"a.sidebar-tools-main-item"
"a.sidebar-tools-main-item, a.quarto-navigation-tool, a.quarto-navbar-tools, a.quarto-navbar-tools-item"
);
for (let i = 0; i < sharingLinks.length; i++) {
const sharingLink = sharingLinks[i];

File diff suppressed because one or more lines are too long

View file

@ -43,7 +43,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
const mainEl = window.document.querySelector("main");
// highlight matches on the page
if (query !== null && mainEl) {
if (query && mainEl) {
// perform any highlighting
highlight(escapeRegExp(query), mainEl);
@ -57,7 +57,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
// (e.g. if the user edits the query or clears it)
let highlighting = true;
const resetHighlighting = (searchTerm) => {
if (mainEl && highlighting && query !== null && searchTerm !== query) {
if (mainEl && highlighting && query && searchTerm !== query) {
clearHighlight(query, mainEl);
highlighting = false;
}
@ -98,6 +98,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
classNames: {
form: "d-flex",
},
placeholder: language["search-text-placeholder"],
translations: {
clearButtonTitle: language["search-clear-button-title"],
detachedCancelButtonText: language["search-detached-cancel-button-title"],
@ -110,6 +111,8 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
return item.href;
},
onStateChange({ state }) {
// If this is a file URL, note that
// Perhaps reset highlighting
resetHighlighting(state.query);
@ -359,7 +362,8 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
state,
setActiveItemId,
setContext,
refresh
refresh,
quartoSearchOptions
);
},
},
@ -374,6 +378,32 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
focusSearchInput();
};
document.addEventListener("keyup", (event) => {
const { key } = event;
const kbds = quartoSearchOptions["keyboard-shortcut"];
const focusedEl = document.activeElement;
const isFormElFocused = [
"input",
"select",
"textarea",
"button",
"option",
].find((tag) => {
return focusedEl.tagName.toLowerCase() === tag;
});
if (
kbds &&
kbds.includes(key) &&
!isFormElFocused &&
!document.activeElement.isContentEditable
) {
event.preventDefault();
window.quartoOpenSearch();
}
});
// Remove the labeleledby attribute since it is pointing
// to a non-existent label
if (quartoSearchOptions.type === "overlay") {
@ -385,11 +415,30 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
}
}
function throttle(func, wait) {
let waiting = false;
return function () {
if (!waiting) {
func.apply(this, arguments);
waiting = true;
setTimeout(function () {
waiting = false;
}, wait);
}
};
}
// If the main document scrolls dismiss the search results
// (otherwise, since they're floating in the document they can scroll with the document)
window.document.body.onscroll = () => {
setIsOpen(false);
};
window.document.body.onscroll = throttle(() => {
// Only do this if we're not detached
// Bug #7117
// This will happen when the keyboard is shown on ios (resulting in a scroll)
// which then closed the search UI
if (!window.matchMedia(detachedMediaQuery).matches) {
setIsOpen(false);
}
}, 50);
if (showSearchResults) {
setIsOpen(true);
@ -429,15 +478,27 @@ function configurePlugins(quartoSearchOptions) {
const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({
insightsClient: window.aa,
onItemsChange({ insights, insightsEvents }) {
const events = insightsEvents.map((event) => {
const maxEvents = event.objectIDs.slice(0, 20);
return {
...event,
objectIDs: maxEvents,
};
const events = insightsEvents.flatMap((event) => {
// This API limits the number of items per event to 20
const chunkSize = 20;
const itemChunks = [];
const eventItems = event.items;
for (let i = 0; i < eventItems.length; i += chunkSize) {
itemChunks.push(eventItems.slice(i, i + chunkSize));
}
// Split the items into multiple events that can be sent
const events = itemChunks.map((items) => {
return {
...event,
items,
};
});
return events;
});
insights.viewedObjectIDs(...events);
for (const event of events) {
insights.viewedObjectIDs(event);
}
},
});
return algoliaInsightsPlugin;
@ -613,9 +674,17 @@ function showCopyLink(query, options) {
/* Search Index Handling */
// create the index
var fuseIndex = undefined;
var shownWarning = false;
async function readSearchData() {
// Initialize the search index on demand
if (fuseIndex === undefined) {
if (window.location.protocol === "file:" && !shownWarning) {
window.alert(
"Search requires JavaScript features disabled when running in file://... URLs. In order to use search, please run this document in a web server."
);
shownWarning = true;
return;
}
// create fuse index
const options = {
keys: [
@ -646,6 +715,7 @@ async function readSearchData() {
);
}
}
return fuseIndex;
}
@ -674,7 +744,8 @@ function renderItem(
state,
setActiveItemId,
setContext,
refresh
refresh,
quartoSearchOptions
) {
switch (item.type) {
case kItemTypeDoc:
@ -684,7 +755,9 @@ function renderItem(
item.title,
item.section,
item.text,
item.href
item.href,
item.crumbs,
quartoSearchOptions
);
case kItemTypeMore:
return createMoreCard(
@ -709,15 +782,46 @@ function renderItem(
}
}
function createDocumentCard(createElement, icon, title, section, text, href) {
function createDocumentCard(
createElement,
icon,
title,
section,
text,
href,
crumbs,
quartoSearchOptions
) {
const iconEl = createElement("i", {
class: `bi bi-${icon} search-result-icon`,
});
const titleEl = createElement("p", { class: "search-result-title" }, title);
const titleContents = [iconEl, titleEl];
const showParent = quartoSearchOptions["show-item-context"];
if (crumbs && showParent) {
let crumbsOut = undefined;
const crumbClz = ["search-result-crumbs"];
if (showParent === "root") {
crumbsOut = crumbs.length > 1 ? crumbs[0] : undefined;
} else if (showParent === "parent") {
crumbsOut = crumbs.length > 1 ? crumbs[crumbs.length - 2] : undefined;
} else {
crumbsOut = crumbs.length > 1 ? crumbs.join(" > ") : undefined;
crumbClz.push("search-result-crumbs-wrap");
}
const crumbEl = createElement(
"p",
{ class: crumbClz.join(" ") },
crumbsOut
);
titleContents.push(crumbEl);
}
const titleContainerEl = createElement(
"div",
{ class: "search-result-title-container" },
[iconEl, titleEl]
titleContents
);
const textEls = [];
@ -1099,17 +1203,19 @@ function algoliaSearch(query, limit, algoliaOptions) {
const remappedHits = response.hits.map((hit) => {
return hit.map((item) => {
const newItem = { ...item };
["href", "section", "title", "text"].forEach((keyName) => {
const mappedName = indexFields[keyName];
if (
mappedName &&
item[mappedName] !== undefined &&
mappedName !== keyName
) {
newItem[keyName] = item[mappedName];
delete newItem[mappedName];
["href", "section", "title", "text", "crumbs"].forEach(
(keyName) => {
const mappedName = indexFields[keyName];
if (
mappedName &&
item[mappedName] !== undefined &&
mappedName !== keyName
) {
newItem[keyName] = item[mappedName];
delete newItem[mappedName];
}
}
});
);
newItem.text = highlightMatch(query, newItem.text);
return newItem;
});
@ -1135,6 +1241,7 @@ function fuseSearch(query, fuse, fuseOptions) {
section: result.item.section,
href: addParam(result.item.href, kQueryArg, query),
text: highlightMatch(query, result.item.text),
crumbs: result.item.crumbs,
};
});
}

View file

@ -208,17 +208,17 @@ One element of R data analysis that can get really interesting is working with m
For the UK, census data is made available for programmatic research like this via an organisation called NOMIS. Luckily for us, there is an R library you can use to access nomis directly which greatly simplifies the process of pulling data down from the platform. It's worth noting that if you're not in the UK, there are similar options for other countries. Nearly every R textbook I've ever seen works with USA census data, so you'll find plenty of documentation available on the tools you can use for US Census data. Similarly for the EU, Canada, Austrailia etc.
If you want to draw some data from the nomis platform yourself in R, have a look at the [companion cookbook repository](https://github.com/kidwellj/hacking_religion_cookbook/blob/main/nomis.R).
If you want to draw some data from the nomis platform yourself in R, have a look at the nomis script in our [companion cookbook repository](https://github.com/kidwellj/hacking_religion_cookbook/blob/main/nomis.R). For now, we'll provide some data extracts for you to use.
:::
```{r}
# Get table of Census 2011 religion data from nomis
z <- readRDS(file = (here("example_data", "z.rds")))
nomis_extract_census2011a <- readRDS(file = (here("example_data", "nomis_extract_census2011a.rds")))
# Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2011_religion <- filter(z, GEOGRAPHY_NAME=="England and Wales" & RURAL_URBAN_NAME=="Total" & C_RELPUK11_NAME != "All categories: Religion")
uk_census_2011_religion <- filter(nomis_extract_census2011a, GEOGRAPHY_NAME=="England and Wales" & RURAL_URBAN_NAME=="Total" & C_RELPUK11_NAME != "All categories: Religion")
# Drop unnecessary columns
uk_census_2011_religion <- select(uk_census_2011_religion, C_RELPUK11_NAME, OBS_VALUE)
# Plot results
@ -226,30 +226,30 @@ plot1 <- ggplot(uk_census_2011_religion, aes(x = C_RELPUK11_NAME, y = OBS_VALUE)
# ggsave(filename = "plot.png", plot = plot1)
# grab daata from nomis for 2001 census religion / ethnicity
z0 <- readRDS(file = (here("example_data", "z0.rds")))
nomis_extract_census2001 <- readRDS(file = (here("example_data", "nomis_extract_census2001.rds")))
# select relevant columns
uk_census_2001_religion_ethnicity <- select(z0, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHHUK11_NAME, OBS_VALUE)
uk_census_2001_religion_ethnicity <- select(nomis_extract_census2001, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHHUK11_NAME, OBS_VALUE)
# Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2001_religion_ethnicity <- filter(uk_census_2001_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C_RELPUK11_NAME != "All categories: Religion")
# Simplify data to only include general totals and omit subcategories
uk_census_2001_religion_ethnicity <- uk_census_2001_religion_ethnicity %>% filter(grepl('Total', C_ETHHUK11_NAME))
# grab data from nomis for 2011 census religion / ethnicity table
z1 <- readRDS(file = (here("example_data", "z1.rds")))
nomis_extract_census2011b <- readRDS(file = (here("example_data", "nomis_extract_census2011b.rds")))
# select relevant columns
uk_census_2011_religion_ethnicity <- select(z1, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHPUK11_NAME, OBS_VALUE)
uk_census_2011_religion_ethnicity <- select(nomis_extract_census2011b, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHPUK11_NAME, OBS_VALUE)
# Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2011_religion_ethnicity <- filter(uk_census_2011_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C_RELPUK11_NAME != "All categories: Religion" & C_ETHPUK11_NAME != "All categories: Ethnic group")
# Simplify data to only include general totals and omit subcategories
uk_census_2011_religion_ethnicity <- uk_census_2011_religion_ethnicity %>% filter(grepl('Total', C_ETHPUK11_NAME))
# grab data from nomis for 2021 census religion / ethnicity table
z2 <- readRDS(file = (here("example_data", "z2.rds")))
nomis_extract_census2021 <- readRDS(file = (here("example_data", "nomis_extract_census2021.rds")))
# select relevant columns
uk_census_2021_religion_ethnicity <- select(z2, GEOGRAPHY_NAME, C2021_RELIGION_10_NAME, C2021_ETH_8_NAME, OBS_VALUE)
uk_census_2021_religion_ethnicity <- select(nomis_extract_census2021, GEOGRAPHY_NAME, C2021_RELIGION_10_NAME, C2021_ETH_8_NAME, OBS_VALUE)
# Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2021_religion_ethnicity <- filter(uk_census_2021_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C2021_RELIGION_10_NAME != "Total" & C2021_ETH_8_NAME != "Total")
# 2021 census includes white sub-groups so we need to omit those so we just have totals:

View file

@ -210,6 +210,8 @@ ggplot(religiosity_sums, aes(x = response, y = n, color=response)) + geom_col(co
Or, you might try the well-crafted typgraphy from `hbrthemes` in the `theme_ipsum_pub` theme:
Note: this library will expect your system to have certain fonts installed and available for RStudio to use. You may want to run the following command to import your system fonts to R so they are available: `extrafont::font_import()`. This will take a bit of time to run. The package will also save fonts to a folder on your PC so you can install them if you don't already have them, you can run `import_public_sans()` to get the path for these files and install in R.
```{r}
library(hrbrthemes) |> suppressPackageStartupMessages()
ggplot(religiosity_sums, aes(x = response, y = n, color=response)) + geom_col(colour = "white", aes(fill = response)) + coord_flip() + theme(plot.margin = margin(rep(15, 4))) + labs(caption = caption) +