diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/_extension.yml b/docs/posts/quarto-webr/_extensions/coatless/webr/_extension.yml
new file mode 100644
index 0000000..e484b59
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/_extension.yml
@@ -0,0 +1,8 @@
+name: webr
+title: Embedded webr code cells
+author: James Joseph Balamuta
+version: 0.4.0-dev.1
+quarto-required: ">=1.2.198"
+contributes:
+ filters:
+ - webr.lua
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/monaco-editor-init.html b/docs/posts/quarto-webr/_extensions/coatless/webr/monaco-editor-init.html
new file mode 100644
index 0000000..a56edb0
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/monaco-editor-init.html
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/template.qmd b/docs/posts/quarto-webr/_extensions/coatless/webr/template.qmd
new file mode 100644
index 0000000..a74441f
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/template.qmd
@@ -0,0 +1,31 @@
+---
+title: "WebR-enabled code cell"
+format: html
+engine: knitr
+#webr:
+# show-startup-message: false # Display status of webR initialization
+# show-header-message: false # Check to see if COOP&COEP headers are set for speed.
+# packages: ['ggplot2', 'dplyr'] # Pre-install dependencies
+# home-dir: "/home/rstudio" # Customize where the working directory is
+# base-url: '' # Base URL used for downloading R WebAssembly binaries
+# service-worker-url: '' # URL from where to load JavaScript worker scripts when loading webR with the ServiceWorker communication channel.
+filters:
+- webr
+---
+
+## Demo
+
+This is a webr-enabled code cell in a Quarto HTML document.
+
+```{webr-r}
+1 + 1
+```
+
+```{webr-r}
+fit = lm(mpg ~ am, data = mtcars)
+summary(fit)
+```
+
+```{webr-r}
+plot(pressure)
+```
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-interactive.html b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-interactive.html
new file mode 100644
index 0000000..80bf7ee
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-interactive.html
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-output.html b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-output.html
new file mode 100644
index 0000000..ca9f96b
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-output.html
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-setup.html b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-setup.html
new file mode 100644
index 0000000..918337b
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-context-setup.html
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr-init.html b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-init.html
new file mode 100644
index 0000000..efed488
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-init.html
@@ -0,0 +1,292 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr-serviceworker.js b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-serviceworker.js
new file mode 100644
index 0000000..4022c54
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-serviceworker.js
@@ -0,0 +1 @@
+importScripts('https://webr.r-wasm.org/v0.2.2/webr-serviceworker.js');
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr-worker.js b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-worker.js
new file mode 100644
index 0000000..8aa663a
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr-worker.js
@@ -0,0 +1 @@
+importScripts('https://webr.r-wasm.org/v0.2.2/webr-worker.js');
diff --git a/docs/posts/quarto-webr/_extensions/coatless/webr/webr.lua b/docs/posts/quarto-webr/_extensions/coatless/webr/webr.lua
new file mode 100644
index 0000000..091b636
--- /dev/null
+++ b/docs/posts/quarto-webr/_extensions/coatless/webr/webr.lua
@@ -0,0 +1,493 @@
+----
+--- Setup variables for default initialization
+
+-- Define a variable to only include the initialization once
+local hasDoneWebRSetup = false
+
+--- Setup default initialization values
+-- Default values taken from:
+-- https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html
+
+-- Define a base compatibile version
+local baseVersionWebR = "0.2.2"
+
+-- Define where WebR can be found
+local baseUrl = "https://webr.r-wasm.org/v".. baseVersionWebR .."/"
+local serviceWorkerUrl = ""
+
+-- Define the webR communication protocol
+local channelType = "ChannelType.Automatic"
+
+-- Define a variable to suppress exporting service workers if not required.
+-- (e.g. skipped for PostMessage or SharedArrayBuffer)
+local hasServiceWorkerFiles = true
+
+-- Define user directory
+local homeDir = "/home/web_user"
+
+-- Define whether a startup message should be displayed
+local showStartUpMessage = "true"
+
+-- Define whether header type messages should be displayed
+local showHeaderMessage = "false"
+
+-- Define an empty string if no packages need to be installed.
+local installRPackagesList = "''"
+
+-- Define whether R packages should automatically be loaded
+local autoloadRPackages = "true"
+----
+
+--- Setup variables for tracking number of code cells
+
+-- Define a counter variable
+local counter = 0
+
+----
+--- Process initialization
+
+-- Check if variable is present and not just the empty string
+function is_variable_empty(s)
+ return s == nil or s == ''
+end
+
+-- Convert the communication channel meta option into a WebROptions.channelType option
+function convertMetaChannelTypeToWebROption(input)
+ -- Create a table of conditions
+ local conditions = {
+ ["automatic"] = "ChannelType.Automatic",
+ [0] = "ChannelType.Automatic",
+ ["shared-array-buffer"] = "ChannelType.SharedArrayBuffer",
+ [1] = "ChannelType.SharedArrayBuffer",
+ ["service-worker"] = "ChannelType.ServiceWorker",
+ [2] = "ChannelType.ServiceWorker",
+ ["post-message"] = "ChannelType.PostMessage",
+ [3] = "ChannelType.PostMessage",
+ }
+ -- Subset the table to obtain the communication channel.
+ -- If the option isn't found, return automatic.
+ return conditions[input] or "ChannelType.Automatic"
+end
+
+
+-- Parse the different webr options set in the YAML frontmatter, e.g.
+--
+-- ```yaml
+-- ----
+-- webr:
+-- base-url: https://webr.r-wasm.org/[version]
+-- service-worker-url: path/to/workers/{webr-serviceworker.js, webr-worker.js}
+-- ----
+-- ```
+--
+--
+function setWebRInitializationOptions(meta)
+
+ -- Let's explore the meta variable data!
+ -- quarto.log.output(meta)
+
+ -- Retrieve the webr options from meta
+ local webr = meta.webr
+
+ -- Does this exist? If not, just return meta as we'll just use the defaults.
+ if is_variable_empty(webr) then
+ return meta
+ end
+
+ -- The base URL used for downloading R WebAssembly binaries
+ -- https://webr.r-wasm.org/[version]/webr.mjs
+ -- Documentation:
+ -- https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#baseurl
+ if not is_variable_empty(webr["base-url"]) then
+ baseUrl = pandoc.utils.stringify(webr["base-url"])
+ end
+
+ -- The communication channel mode webR uses to connect R with the web browser
+ -- Default: "ChannelType.Automatic"
+ -- Documentation:
+ -- https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#channeltype
+ if not is_variable_empty(webr["channel-type"]) then
+ channelType = convertMetaChannelTypeToWebROption(pandoc.utils.stringify(webr["channel-type"]))
+
+ -- Starting from webR v0.2.2, service workers are only deployed when explicitly requested.
+ hasServiceWorkerFiles = (channelType == "ChannelType.ServiceWorker")
+ end
+
+ -- The base URL from where to load JavaScript worker scripts when loading webR
+ -- with the ServiceWorker communication channel mode.
+ -- Documentation:
+ -- https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#serviceworkerurl
+ if not is_variable_empty(webr["service-worker-url"]) then
+ serviceWorkerUrl = pandoc.utils.stringify(webr["service-worker-url"])
+ end
+
+ -- The WebAssembly user's home directory and initial working directory. Default: '/home/web_user'
+ -- Documentation:
+ -- https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#homedir
+ if not is_variable_empty(webr['home-dir']) then
+ homeDir = pandoc.utils.stringify(webr["home-dir"])
+ end
+
+ -- Display a startup message indicating the WebR state at the top of the document.
+ if not is_variable_empty(webr['show-startup-message']) then
+ showStartUpMessage = pandoc.utils.stringify(webr["show-startup-message"])
+ end
+
+ -- Display a startup message indicating the WebR state at the top of the document.
+ if not is_variable_empty(webr['show-header-message']) then
+ showHeaderMessage = pandoc.utils.stringify(webr["show-header-message"])
+ if showHeaderMessage == "true" then
+ showStartUpMessage = "true"
+ end
+ end
+
+
+ -- Attempt to install different packages.
+ if not is_variable_empty(webr["packages"]) then
+ -- Create a custom list
+ local package_list = {}
+
+ -- Iterate through each list item and enclose it in quotes
+ for _, package_name in pairs(webr["packages"]) do
+ table.insert(package_list, "'" .. pandoc.utils.stringify(package_name) .. "'")
+ end
+
+ installRPackagesList = table.concat(package_list, ", ")
+
+ if not is_variable_empty(webr['autoload-packages']) then
+ autoloadRPackages = pandoc.utils.stringify(webr["autoload-packages"])
+ end
+
+ end
+
+
+ return meta
+end
+
+
+-- Obtain a template file
+function readTemplateFile(template)
+ -- Establish a hardcoded path to where the .html partial resides
+ -- Note, this should be at the same level as the lua filter.
+ -- This is crazy fragile since Lua lacks a directory representation (!?!?)
+ -- https://quarto.org/docs/extensions/lua-api.html#includes
+ local path = quarto.utils.resolve_path(template)
+
+ -- Let's hopefully read the template file...
+
+ -- Open the webr editor
+ local file = io.open(path, "r")
+
+ -- Check if null pointer before grabbing content
+ if not file then
+ return nil
+ end
+
+ -- *a or *all reads the whole file
+ local content = file:read "*a"
+
+ -- Close the file
+ file:close()
+
+ -- Return contents
+ return content
+end
+
+-- Obtain the editor template file at webr-context-interactive.html
+function interactiveTemplateFile()
+ return readTemplateFile("webr-context-interactive.html")
+end
+
+-- Obtain the output template file at webr-context-output.html
+function outputTemplateFile()
+ return readTemplateFile("webr-context-output.html")
+end
+
+-- Obtain the setup template file at webr-context-setup.html
+function setupTemplateFile()
+ return readTemplateFile("webr-context-setup.html")
+end
+
+-- Obtain the initialization template file at webr-init.html
+function initializationTemplateFile()
+ return readTemplateFile("webr-init.html")
+end
+
+-- Cache a copy of each public-facing templates to avoid multiple read/writes.
+interactive_template = interactiveTemplateFile()
+
+output_template = outputTemplateFile()
+
+setup_template = setupTemplateFile()
+----
+
+-- Define a function that escape control sequence
+function escapeControlSequences(str)
+ -- Perform a global replacement on the control sequence character
+ return str:gsub("[\\%c]", function(c)
+ if c == "\\" then
+ -- Escape backslash
+ return "\\\\"
+ end
+ end)
+end
+
+-- Check if version is latest
+function isLatestVersion(str)
+ return str == "latest"
+end
+
+
+-- Verify the string is a valid version
+function isMajorMinorPatchFormat(version)
+ -- Create a regular expression pattern that matches:
+ -- major.minor.patch
+ local pattern = "^%d+%.%d+%.%d+$"
+
+ -- If the pattern matches, then we're set!
+ return string.match(version, pattern) ~= nil
+end
+
+function checkMajorMinorPatchVersionFormat(version_string)
+ -- Verify string matches a given format
+ if not isMajorMinorPatchFormat(version_string) then
+ error("Invalid version string: " .. version_string)
+ end
+ -- Empty return to use as enforcement
+ return false
+end
+
+-- Compare versions
+function compareMajorMinorPatchVersions(v1, v2)
+
+ -- Enforce a version string
+ checkMajorMinorPatchVersionFormat(v1)
+ checkMajorMinorPatchVersionFormat(v2)
+
+ -- Extract version details
+ local v1_major, v1_minor, v1_patch = v1:match("(%d+)%.(%d+)%.(%d+)")
+ local v2_major, v2_minor, v2_patch = v2:match("(%d+)%.(%d+)%.(%d+)")
+
+ -- Perform a comparison check on the dot releases, such that:
+ -- v1 > v2 returns 1
+ -- v2 > v1 returns -1
+ -- v1 == v2 returns 0
+ if tonumber(v1_major) > tonumber(v2_major) then
+ return 1
+ elseif tonumber(v2_major) > tonumber(v1_major) then
+ return -1
+ elseif tonumber(v1_minor) > tonumber(v2_minor) then
+ return 1
+ elseif tonumber(v2_minor) > tonumber(v1_minor) then
+ return -1
+ elseif tonumber(v1_patch) > tonumber(v2_patch) then
+ return 1
+ elseif tonumber(v2_patch) > tonumber(v1_patch) then
+ return -1
+ else
+ return 0
+ end
+end
+----
+
+function initializationWebR()
+
+ -- Setup different WebR specific initialization variables
+ local substitutions = {
+ ["SHOWSTARTUPMESSAGE"] = showStartUpMessage, -- tostring()
+ ["SHOWHEADERMESSAGE"] = showHeaderMessage,
+ ["BASEURL"] = baseUrl,
+ ["CHANNELTYPE"] = channelType,
+ ["SERVICEWORKERURL"] = serviceWorkerUrl,
+ ["HOMEDIR"] = homeDir,
+ ["INSTALLRPACKAGESLIST"] = installRPackagesList,
+ ["AUTOLOADRPACKAGES"] = autoloadRPackages
+ -- ["VERSION"] = baseVersionWebR
+ }
+
+ -- Make sure we perform a copy
+ local initializationTemplate = initializationTemplateFile()
+
+ -- Make the necessary substitutions
+ local initializedWebRConfiguration = substitute_in_file(initializationTemplate, substitutions)
+
+ return initializedWebRConfiguration
+end
+
+-- Setup WebR's pre-requisites once per document.
+function ensureWebRSetup()
+
+ -- If we've included the initialization, then bail.
+ if hasDoneWebRSetup then
+ return
+ end
+
+ -- Otherwise, let's include the initialization script _once_
+ hasDoneWebRSetup = true
+
+ local initializedConfigurationWebR = initializationWebR()
+
+ -- Insert the web initialization
+ -- https://quarto.org/docs/extensions/lua-api.html#includes
+ quarto.doc.include_text("in-header", initializedConfigurationWebR)
+
+ -- Insert the monaco editor initialization
+ quarto.doc.include_file("before-body", "monaco-editor-init.html")
+
+ -- If the ChannelType requires service workers, register and copy them into the
+ -- output directory.
+ if hasServiceWorkerFiles then
+ -- Copy the two web workers into the directory
+ -- https://quarto.org/docs/extensions/lua-api.html#dependencies
+ quarto.doc.add_html_dependency({
+ name = "webr-worker",
+ version = baseVersionWebR,
+ seviceworkers = {"webr-worker.js"}, -- Kept to avoid error text.
+ serviceworkers = {"webr-worker.js"}
+ })
+
+ quarto.doc.add_html_dependency({
+ name = "webr-serviceworker",
+ version = baseVersionWebR,
+ seviceworkers = {"webr-serviceworker.js"}, -- Kept to avoid error text.
+ serviceworkers = {"webr-serviceworker.js"}
+ })
+ end
+
+end
+
+-- Define a function to replace keywords given by {{ WORD }}
+-- Is there a better lua-approach?
+function substitute_in_file(contents, substitutions)
+
+ -- Substitute values in the contents of the file
+ contents = contents:gsub("{{%s*(.-)%s*}}", substitutions)
+
+ -- Return the contents of the file with substitutions
+ return contents
+end
+
+-- Extract Quarto code cell options from the block's text
+function extractCodeBlockOptions(block)
+
+ -- Access the text aspect of the code block
+ local code = block.text
+
+ -- Define two local tables:
+ -- the block's attributes
+ -- the block's code lines
+ local newAttributes = {}
+ local newCodeLines = {}
+
+ -- Iterate over each line in the code block
+ for line in code:gmatch("([^\r\n]*)[\r\n]?") do
+ -- Check if the line starts with "#|" and extract the key-value pairing
+ -- e.g. #| key: value goes to newAttributes[key] -> value
+ local key, value = line:match("^#|%s*(.-):%s*(.-)%s*$")
+
+ -- If a special comment is found, then add the key-value pairing to the newAttributes table
+ if key and value then
+ newAttributes[key] = value
+ else
+ -- Otherwise, it's not a special comment, keep the code line
+ table.insert(newCodeLines, line)
+ end
+ end
+
+ -- Set the new attributes for the code block
+ block.attributes = newAttributes
+
+ -- Set the codeblock text to exclude the special comments.
+ block.text = table.concat(newCodeLines, '\n')
+
+ -- Return the full block
+ return block
+end
+
+-- Replace the code cell with a webR editor
+function enableWebRCodeCell(el)
+
+ -- Let's see what's going on here:
+ -- quarto.log.output(el)
+
+ -- Should display the following elements:
+ -- https://pandoc.org/lua-filters.html#type-codeblock
+
+ -- Verify the element has attributes and the document type is HTML
+ -- not sure if this will work with an epub (may need html:js)
+ if el.attr and quarto.doc.is_format("html") then
+
+ -- Check to see if any form of the {webr} tag is present
+
+ -- Look for the original compute cell type `{webr}`
+ -- If the compute engine is:
+ -- - jupyter: this appears as `{webr}`
+ -- - knitr: this appears as `webr`
+ -- since the later dislikes custom engines
+ local originalEngine = el.attr.classes:includes("{webr}") or el.attr.classes:includes("webr")
+
+ -- Check for the new engine syntax that allows for the cell to be
+ -- evaluated in VS Code or RStudio editor views, c.f.
+ -- https://github.com/quarto-dev/quarto-cli/discussions/4761#discussioncomment-5336636
+ local newEngine = el.attr.classes:includes("{webr-r}")
+
+ if (originalEngine or newEngine) then
+
+ -- Make sure we've initialized the code block
+ ensureWebRSetup()
+
+ -- Modify the counter variable each time this is run to create
+ -- unique code cells
+ counter = counter + 1
+
+ -- Convert webr-specific option commands into attributes
+ el = extractCodeBlockOptions(el)
+
+ -- 7 is the default height and width for knitr. But, that doesn't translate to pixels.
+ -- So, we have 504 and 360 respectively.
+ -- Should we check the attributes for this value? Seems odd.
+ -- https://yihui.org/knitr/options/
+ local substitutions = {
+ ["WEBRCOUNTER"] = counter,
+ ["WIDTH"] = 504,
+ ["HEIGHT"] = 360,
+ ["WEBRCODE"] = escapeControlSequences(el.text)
+ }
+
+ -- Retrieve the newly defined attributes
+ local cell_context = el.attributes.context
+
+ -- Decide the correct template
+ -- Make sure we perform a copy of each template
+ local copied_code_template = nil
+ if is_variable_empty(cell_context) or cell_context == "interactive" then
+ copied_code_template = interactive_template
+ elseif cell_context == "setup" then
+ copied_code_template = setup_template
+ elseif cell_context == "output" then
+ copied_code_template = output_template
+ else
+ error("The `context` option must contain either: `interactive`, `setup`, or `output`. Not the value of `".. cell_context .."`")
+ end
+
+ -- Make the necessary substitutions into the template
+ local webr_enabled_code_cell = substitute_in_file(copied_code_template, substitutions)
+
+ -- Return the modified HTML template as a raw cell
+ return pandoc.RawInline('html', webr_enabled_code_cell)
+
+ end
+ end
+ -- Allow for a pass through in other languages
+ return el
+end
+
+return {
+ {
+ Meta = setWebRInitializationOptions
+ },
+ {
+ CodeBlock = enableWebRCodeCell
+ }
+}
+
diff --git a/docs/posts/quarto-webr/day1_results.csv.gz b/docs/posts/quarto-webr/day1_results.csv.gz
new file mode 100644
index 0000000..97edc9b
Binary files /dev/null and b/docs/posts/quarto-webr/day1_results.csv.gz differ
diff --git a/docs/posts/quarto-webr/day2_results.csv.gz b/docs/posts/quarto-webr/day2_results.csv.gz
new file mode 100644
index 0000000..5f49d04
Binary files /dev/null and b/docs/posts/quarto-webr/day2_results.csv.gz differ
diff --git a/docs/posts/quarto-webr/day3_results.csv.gz b/docs/posts/quarto-webr/day3_results.csv.gz
new file mode 100644
index 0000000..731aa50
Binary files /dev/null and b/docs/posts/quarto-webr/day3_results.csv.gz differ
diff --git a/docs/posts/quarto-webr/differential_expression_analysis.R b/docs/posts/quarto-webr/differential_expression_analysis.R
new file mode 100644
index 0000000..a6e4917
--- /dev/null
+++ b/docs/posts/quarto-webr/differential_expression_analysis.R
@@ -0,0 +1,89 @@
+library(edgeR)
+library(here)
+library(limma)
+library(readr)
+library(readxl)
+
+# retrieve and parse raw counts
+url <- paste0("https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE158152&",
+ "format=file&file=GSE158152%5Fdst150%5Fprocessed%2Exlsx")
+temp_file <- tempfile(fileext = ".xlsx")
+download.file(url, destfile = temp_file)
+raw_counts <- read_excel(temp_file, sheet = "raw_counts")
+
+# retrieve mapping between geo samples & the author's sample identifiers
+url <- paste0("https://tomsing1.github.io/blog/posts/",
+ "nextflow-core-quantseq-3-xia/GEO_sample_ids.csv")
+geo_ids <- read.csv(url)
+
+# retrieve sample annotations
+url <- paste0("https://tomsing1.github.io/blog/posts/",
+ "nextflow-core-quantseq-3-xia/sample_metadata.csv")
+sample_anno <- read.csv(url, row.names = "Experiment")
+colnames(sample_anno)<- tolower(colnames(sample_anno))
+colnames(sample_anno) <- sub(".", "_", colnames(sample_anno),
+ fixed = TRUE)
+sample_anno <- sample_anno[, c("sample_name", "animal_id", "genotype", "sex",
+ "batch")]
+sample_anno$genotype <- factor(sample_anno$genotype,
+ levels = c("WT", "Het", "Hom"))
+sample_anno$sample_title <- geo_ids[
+ match(sample_anno$sample_name, geo_ids$sample_name), "sample_id"]
+
+# create DGEList object
+count_data <- as.matrix(raw_counts[, grep("DRN-", colnames(raw_counts))])
+row.names(count_data) <- raw_counts$feature_id
+colnames(count_data) <- row.names(sample_anno)[
+ match(colnames(count_data), sample_anno$sample_title)
+]
+
+gene_data <- data.frame(
+ gene_id = raw_counts$feature_id,
+ gene_name = raw_counts$symbol,
+ row.names = raw_counts$feature_id
+)
+
+col_data <- data.frame(
+ sample_anno[colnames(count_data),
+ c("sample_title", "animal_id", "sex", "genotype", "batch")],
+ workflow = "geo"
+)
+
+dge <- DGEList(
+ counts = as.matrix(count_data),
+ samples = col_data[colnames(count_data), ],
+ genes = gene_data[row.names(count_data), ]
+)
+
+dge <- normLibSizes(dge, method = "TMM")
+
+# linear modeling
+dge$samples$group <- with(dge$samples, paste(genotype, batch, sep = "_"))
+design <- model.matrix(~ 0 + group + sex, data = dge$samples)
+colnames(design) <- sub("^group", "", colnames(design))
+keep <- filterByExpr(dge, design = design, min.count = 25)
+fit <- voomLmFit(
+ dge[keep, row.names(design)],
+ design = design,
+ block = dge$samples$animal_id,
+ sample.weights = TRUE,
+ plot = FALSE
+)
+contrasts <- makeContrasts(
+ day1 = "Hom_Day1-WT_Day1",
+ day2 = "Hom_Day2-WT_Day2",
+ day3 = "Hom_Day3-WT_Day3",
+ levels = design
+)
+fit <- contrasts.fit(fit, contrasts = contrasts)
+fit2 <- eBayes(fit, robust=TRUE)
+
+for (contr in colnames(fit2)) {
+ tt <- topTable(fit2, coef = contr, number = Inf)
+ tt$logFC <- signif(tt$logFC, 2)
+ tt$adj.P.Val <- signif(tt$adj.P.Val, 3)
+ write_csv(tt[, c("gene_name", "logFC", "adj.P.Val")],
+ here("posts", "quarto-webr", sprintf("%s_results.csv.gz", contr)))
+}
+
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/index.html b/docs/posts/quarto-webr/index.html
new file mode 100644
index 0000000..b30a2a6
--- /dev/null
+++ b/docs/posts/quarto-webr/index.html
@@ -0,0 +1,907 @@
+
+
+
+
+
+
+
+
+
+
+
+Thomas Sandmann’s blog - Embedding R into Quarto documents with quarto-webr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Embedding R into Quarto documents with quarto-webr
+
+
R
+
Quarto
+
TIL
+
+
+
+
+
+
+
+
+
Author
+
+
Thomas Sandmann
+
+
+
+
+
Published
+
+
November 18, 2023
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
tl;dr
+
+
+
Motivation
+
Since its first release in March 2023, the webR project has made incredible progress. It
+
+
makes it possible to run R code in the browser without the need for an R server to execute the code: the R interpreter runs directly on the user’s machine.
+
+
I routinely communicate the results of analysis to collaborators as HTML documents, e.g. websites created from Quarto markdown files that combine prose and the analysis code itself. While I incorporate interactive elements (e.g. tables or plots) as much as possible, the reports are static - e.g. they don’t allow users to change parameters on the fly.
+
That’s why I am excited to learn about James J Balamuta’s quarto-webr extension, which embeds webR into quarto documents.
+
Today I am making my first steps, with the goal of creating an HTML report that
+
+
Reads differential gene expression results from multiple experiments from CSV files.
+
Applies a user-specified false discovery (FDR) threshold to each experiment.
+
Creates a Venn diagram to visualize the overlap between the results.
+
Shows the results for the genes deemed significantly differentially expressed in all experiments.
+
+
Specifically, I would like users to be able to modify the FDR threshold themselves and to trigger a refresh of the plot & tables.
+
+
+
Installation & configuration
+
I already have quarto installed on my system1, and first add the quarto-webr extension:
+
quarto add coatless/quarto-webr
+
+
Enabling webr-r code cells
+
With the extension in place, I can now create webr-r code cells in my Quarto documents. To render them, I also need to include the corresponding filter in the YAML header of the file.2
+
Here is a minimal example of a Quarto document with the required header:
+
---
+title: webR - Hello world
+format: html
+engine: knitr
+filters:
+ - webr
+---
+
+The following code cell will be executed by `webR` within the user's browser.
+
+```{webr-r}
+print('Hello mtcars')
+```
+
+
+
Using additional R packages
+
The webR community has precompiled a large collection of R packages. The quarto-webr extension facilitates their installation: we can simply list required packages in the webr section of the YAML header. For example, I will use the ggvenn and huxtable packages to create the Venn diagram and result tables, respectively.
At the time of writing, the quarto-webr extension supports code cells with three different contexts3
+
+
interactive cells are executed when the user clicks the Run Code button and display both the code and the output.
+
setup cells are executed automatically and neither their code nor their output is shown.
+
output cells execute automatically but don’t show their code.
+
+
I will use setup contexts to download & parse the output of three differential expression analyses, and then use interactive contexts to trigger filtering, plotting and printing the result tables.
+
+
+
+
Reproducibility
+
+
+
+
+
+
+Session Information
+
+
+
+
+
+
+
sessioninfo::session_info()
+
+
─ Session info ───────────────────────────────────────────────────────────────
+ setting value
+ version R version 4.3.2 (2023-10-31)
+ os Debian GNU/Linux 12 (bookworm)
+ system x86_64, linux-gnu
+ ui X11
+ language (EN)
+ collate en_US.UTF-8
+ ctype en_US.UTF-8
+ tz America/Los_Angeles
+ date 2023-11-18
+ pandoc 3.1.1 @ /usr/lib/rstudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)
+
+─ Packages ───────────────────────────────────────────────────────────────────
+ ! package * version date (UTC) lib source
+ P BiocManager 1.30.22 2023-08-08 [?] RSPM (R 4.3.1)
+ R bspm 0.5.5 <NA> [?] <NA>
+ P cli 3.6.1 2023-03-23 [?] CRAN (R 4.3.1)
+ P digest 0.6.33 2023-07-07 [?] CRAN (R 4.3.1)
+ P evaluate 0.21 2023-05-05 [?] CRAN (R 4.3.1)
+ P fastmap 1.1.1 2023-02-24 [?] CRAN (R 4.3.1)
+ P htmltools 0.5.6 2023-08-10 [?] CRAN (R 4.3.1)
+ P htmlwidgets 1.6.2 2023-03-17 [?] CRAN (R 4.3.1)
+ P jsonlite 1.8.7 2023-06-29 [?] CRAN (R 4.3.1)
+ P knitr 1.43 2023-05-25 [?] CRAN (R 4.3.1)
+ renv 1.0.2 2023-08-15 [1] RSPM (R 4.3.0)
+ P rlang 1.1.1 2023-04-28 [?] CRAN (R 4.3.1)
+ P rmarkdown 2.24 2023-08-14 [?] CRAN (R 4.3.1)
+ P rstudioapi 0.15.0 2023-07-07 [?] CRAN (R 4.3.1)
+ P sessioninfo 1.2.2 2021-12-06 [?] CRAN (R 4.3.1)
+ P xfun 0.40 2023-08-09 [?] CRAN (R 4.3.1)
+ P yaml 2.3.7 2023-01-23 [?] CRAN (R 4.3.1)
+
+ [1] /home/sandmann/repositories/blog/renv/library/R-4.3/x86_64-pc-linux-gnu
+ [2] /home/sandmann/.cache/R/renv/sandbox/R-4.3/x86_64-pc-linux-gnu/9a444a72
+
+ P ── Loaded and on-disk path mismatch.
+ R ── Package was removed from disk.
+
+──────────────────────────────────────────────────────────────────────────────
---
+title: "Embedding R into Quarto documents with quarto-webr"
+author: "Thomas Sandmann"
+date: "2023-11-18"
+freeze: true
+categories: [R, Quarto, TIL]
+editor:
+ markdown:
+ wrap: 72
+format:
+ html:
+ toc: true
+ toc-depth: 4
+ code-tools:
+ source: true
+ toggle: false
+ caption: none
+editor_options:
+ chunk_output_type: console
+resources:
+ - day1_results.csv.gz
+ - day2_results.csv.gz
+ - day3_results.csv.gz
+ - quarto_webr_example.qmd
+ - differential_expression_analysis.R
+ - webr-serviceworker.js
+ - webr-worker.js
+---
+
+## tl;dr
+
+## Motivation
+
+Since its first release in March 2023, the
+[webR project](https://docs.r-wasm.org/webr/latest/)
+has made incredible progress. It
+
+> makes it possible to run R code in the browser without the need for an R
+server to execute the code: the R interpreter runs directly on the user’s
+machine.
+
+I routinely communicate the results of analysis to collaborators as HTML
+documents, e.g. websites created from
+[Quarto markdown](https://quarto.org/)
+files that combine prose and the analysis code itself. While I incorporate
+interactive elements (e.g. tables or plots) as much as possible, the reports
+are static - e.g. they don't allow users to change parameters on the fly.
+
+That's why I am excited to learn about James J Balamuta's
+[quarto-webr](https://github.com/coatless/quarto-webr)
+extension, which embeds webR into quarto documents.
+
+Today I am making my first steps, with the goal of creating an HTML report that
+
+1. Reads differential gene expression results from multiple experiments from
+ CSV files.
+2. Applies a user-specified false discovery (FDR) threshold to each experiment.
+3. Creates a Venn diagram to visualize the overlap between the results.
+4. Shows the results for the genes deemed significantly differentially expressed
+ in _all_ experiments.
+
+Specifically, I would like users to be able to modify the FDR threshold
+themselves and to trigger a refresh of the plot & tables.
+
+## Installation & configuration
+
+I already have quarto installed on my system[^1], and first add the
+`quarto-webr` extension:
+
+```bash
+quarto add coatless/quarto-webr
+```
+
+[^1]: You can download the version for your operation system
+[here](https://quarto.org/docs/get-started/index.html)
+
+### Enabling `webr-r` code cells
+
+With the extension in place, I can now create `webr-r` code cells in my
+Quarto documents. To render them, I also need to include the corresponding
+filter in the YAML header of the file.[^2]
+
+Here is a minimal example of a Quarto document with the required header:
+
+````
+---
+title: webR - Hello world
+format: html
+engine: knitr
+filters:
+ - webr
+---
+
+The following code cell will be executed by `webR` within the user's browser.
+
+```{webr-r}
+print('Hello mtcars')
+```
+````
+
+[^2]: Alternatively, you can also specify the options globally by
+[adding them to your `_quarto.yml` file](https://quarto-webr.thecoatlessprofessor.com/qwebr-meta-options.html#global-configuration)
+
+### Using additional R packages
+
+The webR community has precompiled a large
+[collection of R packages](https://docs.r-wasm.org/webr/latest/packages.html).
+The `quarto-webr` extension facilitates their installation: we can simply list
+required packages in the `webr` section of the YAML header. For example, I will
+use the
+[ggvenn](https://cran.r-project.org/package=ggvenn)
+and
+[huxtable](https://hughjonesd.github.io/huxtable/)
+packages to create the Venn diagram and result tables, respectively.
+
+```
+webr:
+ show-startup-mmessage: false
+ pacakges: ['ggvenn', 'huxtable']
+```
+
+### Specifying code cell _context_
+
+At the time of writing, the `quarto-webr` extension supports code cells with
+three different
+[contexts](https://quarto-webr.thecoatlessprofessor.com/qwebr-internal-cell.html)[^3]
+
+- `interactive` cells are executed when the user clicks the `Run Code` button
+ and display both the code and the output.
+- `setup` cells are executed automatically and neither their code nor their
+ output is shown.
+- `output` cells execute automatically but don't show their code.
+
+[^3]: Contexts are very recent feature, and at the time of writing there are
+ still bugs to iron out e.g. controlling the
+[execution order of loading third party packages](https://github.com/coatless/quarto-webr/issues/105)
+
+I will use `setup` contexts to download & parse the output of three
+differential expression analyses, and then use `interactive` contexts to
+trigger filtering, plotting and printing the result tables.
+
+## Reproducibility
+
+::: {.callout-note title="Session Information" collapse=true}
+
+```{r}
+sessioninfo::session_info()
+```
+:::
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/posts/quarto-webr/quarto_webr_example.html b/docs/posts/quarto-webr/quarto_webr_example.html
new file mode 100644
index 0000000..2a889c2
--- /dev/null
+++ b/docs/posts/quarto-webr/quarto_webr_example.html
@@ -0,0 +1,2653 @@
+
+
+
+
+
+
+
+
+
+
+
+Embedding R into Quarto documents with quarto-webr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Embedding R into Quarto documents with quarto-webr
This interactive website allows users to intersect differential expression results of three different comparisons from an experiment published by Xia et al
+
The experiment was performed in three different batches (e.g. samples were collected on three different days). For this report, the differences between samples from homozygous APP-SAA and wiltdype animals were estimated separately for each batch/day.1
+
By manipulating the code cells include in this website, you can select a set of genes that passes a user-defined FDR threshold on all three days.2
+
+
+
Step 1: Choose a false-discovery (FDR) threshold
+
+
Please choose an FDR threshold by changing the FDR_threshold the code box below.
+
+
+
It will be applied to results from all three comparisons.
+
For example, setting FDR_threshold < 0.1 will retain all genes with a false-discovery rate < 10% in all three experiments.
+
+
+
Press the “Run Code” button to create a Venn diagram.
+
+
+
You can save the plot by right-clicking and selecting Save image as.
+
+
+
Repeat until you like the results.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Step 2: List the genes that pass the FDR threshold in all three comparisons
+
Next, press the Run Code button to see the list of genes that pass your selected FDR threshold.
+
+
If you triple-click on the list of gene symbols, you can copy them into another document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Step 3: See the differential expression results for the selected genes
+
For more context, the following sections show you tables with the results from your three experiments for the genes you selected in Step 1.
+
+
+
+
Day 1
+
Hit Run Code button to see the expression of the selected genes or download results for all genes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Day 2
+
Hit Run Code button to see the expression of the selected genes or download results for all genes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Day 3
+
Hit Run Code button to see the expression of the selected genes or download results for all genes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Footnotes
+
+
+
Analyzing each batch separately is not the best way to estimate genotype effects across the full experiment. For this exercise, I just want to obtain three different contrasts to compare in a Venn diagram. You can find more details about a more realistic analysis of this experiment in this blog post.↩︎
+
The FDR does not reveal the direction of differential expression. So it is possible that we will pick up genes that are statistically significantly differentially expressed in opposite directions.↩︎