library(dplyr)
library(edgeR)
library(ggplot2)
library(here)
library(org.Mm.eg.db)
library(statmod)
library(SummarizedExperiment)
library(tibble)
library(tidyr)
This is the fourth of four posts documenting my progress toward processing and analyzing QuantSeq FWD 3’ tag RNAseq data with the nf-core/rnaseq workflow.
- Configuring & executing the nf-core/rnaseq workflow
- Exploring the workflow outputs
- Validating the workflow by reproducing results published by Xia et al (no UMIs)
- Validating the workflow by reproducing results published by Nugent et al (including UMIs)
Many thanks to Harshil Patel, António Miguel de Jesus Domingues and Matthias Zepper for their generous guidance & input via nf-core slack. (Any mistakes are mine.)
tl;dr
- This analysis compares the performance of the nf-core/rnaseq workflow for QuantSeq FWD 3’ tag RNAseq data with unique molecular identifiers (UMIs).
- The differential expression analysis results are highly concordant with those obtained in the original publication.
- With the appropriate settings, the nf-core/rnaseq workflow is a valid data processing pipeline for this data type.
The first post in this series walked through the preprocesssing of QuantSeq FWD data published in a preprint by Nugent et al, 2020, who used the QuantSeq FWD library preparation protocol and added unique molecular identifiers (UMIs). The UMIs were used to identify and remove PCR duplicates during the data preprocessing steps.
Here, we use Bioconductor/R packages to reproduce the downstream results. We perform the same analysis twice with either
- the original counts matrix published by the authors 1
- the output of the nf-core/rnaseq workflow
Sample annotations
We start by retrieving the sample annotation table, listing e.g. the sex
, and genotype
for each mouse, and the batch
for each collected sample.
This information is available in the SRA Run Explorer. (I saved it in the sample_metadata.csv CSV file if you want to follow along>.
<- file.path(work_dir, "sample_metadata.csv")
sample_sheet <- read.csv(sample_sheet, row.names = "Experiment")
sample_anno head(sample_anno[, c("Run", "Animal.ID", "Age", "age_unit", "Cell_type",
"sex", "Genotype", "Sample.Name")])
Run Animal.ID Age age_unit Cell_type sex Genotype
SRX6420531 SRR9659551 IL1 2 months astrocyte female TREM2 +/+
SRX6420532 SRR9659552 IL1 2 months microglia female TREM2 +/+
SRX6420533 SRR9659553 IL10 2 months astrocyte female TREM2 -/-
SRX6420534 SRR9659554 IL10 2 months microglia female TREM2 -/-
SRX6420535 SRR9659555 IL11 16 months astrocyte female TREM2 +/+
SRX6420536 SRR9659556 IL11 16 months microglia female TREM2 +/+
Sample.Name
SRX6420531 GSM3933549
SRX6420532 GSM3933550
SRX6420533 GSM3933551
SRX6420534 GSM3933552
SRX6420535 GSM3933553
SRX6420536 GSM3933554
Because our SRA metadata doesn’t include the GEO sample title, I saved the identifier mappings in the GEO_sample_ids.csv CSV file.
<- read.csv(file.path(work_dir, "GEO_sample_ids.csv"))
geo_ids head(geo_ids)
sample_name sample_id
1 GSM3933549 IL1_A
2 GSM3933550 IL1_M
3 GSM3933551 IL10_A
4 GSM3933552 IL10_M
5 GSM3933553 IL11_A
6 GSM3933554 IL11_M
Code
colnames(sample_anno)<- tolower(colnames(sample_anno))
colnames(sample_anno) <- sub(".", "_", colnames(sample_anno),
fixed = TRUE)
<- sample_anno[, c("sample_name", "animal_id", "genotype", "sex",
sample_anno "age", "cell_type")]
$genotype <- factor(sample_anno$genotype,
sample_annolevels = c("TREM2 +/+", "TREM2 -/-"))
$genotype <- dplyr::recode_factor(
sample_anno$genotype,"TREM2 +/+" = "WT", "TREM2 -/-" = "KO")
sample_anno$age <- factor(sample_anno$age)
sample_anno$sample_title <- geo_ids[
sample_annomatch(sample_anno$sample_name, geo_ids$sample_name), "sample_id"]
head(sample_anno)
sample_name animal_id genotype sex age cell_type sample_title
SRX6420531 GSM3933549 IL1 WT female 2 astrocyte IL1_A
SRX6420532 GSM3933550 IL1 WT female 2 microglia IL1_M
SRX6420533 GSM3933551 IL10 KO female 2 astrocyte IL10_A
SRX6420534 GSM3933552 IL10 KO female 2 microglia IL10_M
SRX6420535 GSM3933553 IL11 WT female 16 astrocyte IL11_A
SRX6420536 GSM3933554 IL11 WT female 16 microglia IL11_M
This experiment includes 56 samples of astrocytes or microglia cells obtained from 28 female mice that were either 2- or 16 months of age.
The animals are either wildtype (WT
) or homozygous knockouts (KO
) for the Trem2 gene.
Nugent et al’s original count data
First, we retrieve the authors’ count matrix from NCBI GEO, available as a Supplementary tab-delimited text file.
<- paste0("https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE134031&",
geo_url "format=file&file=GSE134031%5FDST120%2Etab%2Egz")
<- read.delim(textConnection(readLines(gzcon(url(geo_url)))))
raw_counts head(colnames(raw_counts), 10)
[1] "mgi_symbol" "gene_biotype"
[3] "ensembl_gene_id_version" "IL1_A"
[5] "IL1_M" "IL10_A"
[7] "IL10_M" "IL11_A"
[9] "IL11_M" "IL12_A"
The raw_counts
data.frame contains information about the detected genes ( mgi_symbol
, ensembl_gene_id_version
) and the samples are identified by a shorthand of their GEO title (e.g. IL1_M
, IL1_A
).
We use the raw counts to populate a new DGEList
object and perform Library Size Normalization with the TMM
approach.
Code
<- as.matrix(raw_counts[, grep("^IL", colnames(raw_counts))])
count_data row.names(count_data) <- raw_counts$ensembl_gene_id_version
colnames(count_data) <- row.names(sample_anno)[
match(colnames(count_data), sample_anno$sample_title)
]
<- data.frame(
gene_data gene_id = raw_counts$ensembl_gene_id_version,
gene_name = raw_counts$mgi_symbol,
row.names = raw_counts$ensembl_gene_id_version
)
<- data.frame(
col_data colnames(count_data),
sample_anno[c("sample_title", "animal_id", "age", "genotype", "cell_type")],
workflow = "geo"
)
<- DGEList(
dge counts = as.matrix(count_data),
samples = col_data[colnames(count_data), ],
genes = gene_data[row.names(count_data), ]
)
<- calcNormFactors(dge, method = "TMM") dge
This is a large dataset, containing e.g. samples from two different cell types (microglia and astrocytes) and two different age groups (2 and 16 months).
Here, we will restrict the analysis to microglia samples collected from older animals.
<- dge[, dge$samples$cell_type == "microglia" & dge$samples$age == "16"] dge
Let’s identify which genes are significantly differentially expressed between the two genotypes!
Linear modeling with limma/voom
First, we use the edgeR::filterByExpr()
function to identify genes with sufficiently large counts to be examined for differential expression.
<- model.matrix(~ genotype, data = dge$samples)
design colnames(design) <- sub("genotype", "", colnames(design))
<- filterByExpr(dge, design = design) keep
Next, we fit a linear model to the data using the limma/voom approach. The model only includes the genotype
(with WT
as the reference level) as a fixed effect.
<- voomLmFit(
fit row.names(design)],
dge[keep, design = design,
sample.weights = TRUE,
plot = FALSE
)<- eBayes(fit, robust=TRUE) fit
The following table displays the number of differentially up- and down-regulated genes after applying a false-discovery (adj.P.Val
) threshold of 5%. We detect significant differences between KO
and WT
animals in a small number of genes
summary(decideTests(fit)[, "KO"])
KO
Down 10
NotSig 6343
Up 2
The top 10 genes with the smallest p-values include well known markers of microglia activation:
topTreat(fit, coef = "KO")[, c("gene_name", "logFC", "P.Value", "adj.P.Val")]
gene_name logFC P.Value adj.P.Val
ENSMUSG00000015568.16 Lpl -3.1524285 2.088121e-17 1.327001e-13
ENSMUSG00000023992.14 Trem2 -2.0556690 3.260855e-15 1.036137e-11
ENSMUSG00000079293.11 Clec7a -1.5285199 9.357950e-12 1.982326e-08
ENSMUSG00000029304.14 Spp1 -4.6265897 4.968572e-10 7.893819e-07
ENSMUSG00000003418.11 St8sia6 -1.6639344 7.279411e-09 8.240295e-06
ENSMUSG00000002602.16 Axl -1.2096969 7.779979e-09 8.240295e-06
ENSMUSG00000068129.5 Cst7 -2.0966828 9.881687e-08 8.971160e-05
ENSMUSG00000008845.9 Cd163 0.8165263 1.513307e-06 1.202133e-03
ENSMUSG00000039109.16 F13a1 0.8081490 1.735344e-06 1.225345e-03
ENSMUSG00000000682.7 Cd52 -0.7939756 4.846100e-06 3.079696e-03
Next we repeat the same analysis with the output of the nf-core/rnaseq workflow.
nf-core/rnaseq results
We start with the raw counts contained in the salmon.merged.gene_counts.rds file generated by the nf-core/rnaseq workflow.
We TMM-normalize the data, as before. (This step converts the SummarizedExperiment
into a `DGEList object as well.)
<- file.path(work_dir, "salmon.merged.gene_counts.rds")
count_file <- readRDS(count_file)
se stopifnot(all(colnames(se) %in% row.names(sample_anno)))
<- calcNormFactors(se, method = "TMM") dge_nfcore
Next, we add the sample metadata and fit the same linear model as before.
Code
$samples <- data.frame(
dge_nfcore$samples,
dge_nfcorecolnames(dge_nfcore),
sample_anno[c("sample_title", "animal_id", "age", "genotype", "cell_type")],
workflow = "nfcore"
)stopifnot(all(colnames(dge) %in% colnames(dge_nfcore)))
<- dge_nfcore[, colnames(dge)]
dge_nfcore
<- model.matrix(~ genotype, data = dge_nfcore$samples)
design colnames(design) <- sub("genotype", "", colnames(design))
<- filterByExpr(dge_nfcore, design = design)
keep <- voomLmFit(
fit_nfcore row.names(design)],
dge_nfcore[keep, design = design,
sample.weights = TRUE,
plot = FALSE
)
First sample weights (min/max) 0.3856833/1.6439976
Final sample weights (min/max) 0.3853171/1.6423319
Code
<- eBayes(fit_nfcore, robust=TRUE) fit_nfcore
As with the original count data from NCBI GEO, we detect small number of differentially expressed genes (FDR < 5%).
summary(decideTests(fit_nfcore)[, "KO"])
KO
Down 10
NotSig 7824
Up 2
Code
<- local({
cpms <- cpm(dge, normalized.lib.sizes = TRUE) %>%
geo as.data.frame() %>%
cbind(dge$genes) %>%
pivot_longer(cols = starts_with("SRX"),
names_to = "sample_name",
values_to = "cpm") %>%
::left_join(
dplyr::rownames_to_column(dge$samples, "sample_name"),
tibbleby = "sample_name"
%>%
) ::mutate(dataset = "Nugent et al")
dplyr
<- cpm(dge_nfcore, normalized.lib.sizes = TRUE) %>%
nfcore as.data.frame() %>%
cbind(dge_nfcore$genes) %>%
pivot_longer(cols = starts_with("SRX"),
names_to = "sample_name",
values_to = "cpm") %>%
::left_join(
dplyr::rownames_to_column(dge_nfcore$samples, "sample_name"),
tibbleby = "sample_name"
%>%
) ::mutate(dataset = "nf-core")
dplyr
::bind_rows(
dplyr::select(geo, any_of(intersect(colnames(geo), colnames(nfcore)))),
dplyr::select(nfcore, any_of(intersect(colnames(geo), colnames(nfcore))))
dplyr
)
})
<- rbind(
tt topTreat(fit, coef = "KO", number = Inf)[
c("gene_id", "gene_name", "logFC", "P.Value", "adj.P.Val")] %>%
, ::mutate(dataset = "geo"),
dplyrtopTreat(fit_nfcore, coef = "KO", number = Inf)[
c("gene_id", "gene_name", "logFC", "P.Value", "adj.P.Val")] %>%
, ::mutate(dataset = "nfcore")
dplyr%>%
) ::mutate(adj.P.Val = signif(adj.P.Val, 2)) %>%
dplyr::pivot_wider(
tidyrid_cols = c("gene_id", "gene_name"),
names_from = "dataset",
values_from = "adj.P.Val") %>%
::arrange(nfcore) %>%
dplyras.data.frame() %>%
::column_to_rownames("gene_id") tibble
Normalized expression
First, we examine the correlation between the normalized log-transformed gene expression estimates returned from the two workflows. We focus on those genes that passed the filterByExpr
thresholds above, e.g. those genes deemed sufficiently highly expressed to be assessed for differential expression.
<- intersect(row.names(fit), row.names(fit_nfcore))
common_genes <- cpms %>%
sum_stats ::filter(gene_id %in% common_genes) %>%
dplyr::pivot_wider(
tidyrid_cols = c("gene_id", "sample_name"),
values_from = "cpm",
names_from = "dataset") %>%
::group_by(gene_id) %>%
dplyr::summarise(
dplyrr = cor(log1p(`Nugent et al`), log1p(`nf-core`)),
mean_nugent = mean(`Nugent et al`),
mean_nfcore = mean(`nf-core`))
<- ggplot(data = sum_stats, aes(x = r)) +
p geom_histogram(bins = 50) +
scale_x_continuous(limits = c(0, 1.02), breaks = seq(0, 1, by = 0.2)) +
labs(x = "Pearson correlation coefficient (R)",
y = "Number of genes",
title = "Correlation between normalized log2 counts") +
theme_linedraw(14)
print(p)
Warning: Removed 35 rows containing non-finite values (`stat_bin()`).
Warning: Removed 2 rows containing missing values (`geom_bar()`).
The correlation between normalized log2 expression estimates is reasonably high, e.g. 80% of all genes showing a Pearson correlation coefficient > 0.82.
The relatively low correlation might reflect the low RNA input of this experiment, e.g. only 6314 of the genes genes were detected with > 10 UMI-corrected normalized counts per million reads.
<- ggplot(data = sum_stats, aes(x = mean_nugent + 1)) +
p geom_histogram(bins = 50) +
scale_x_continuous(trans = scales::log10_trans(),
labels = scales::comma_format()) +
labs(x = "Mean normalized counts per million",
y = "Number of genes",
title = "Average expression",
subtitle = "Nugent et al") +
theme_linedraw(14)
print(p)
Next, we will examine the results of the differential expression analysis.
Differential expression results
Analyses based on either preprocessing pipeline yield similar numbers of differentially expressed genes.
Code
<- intersect(row.names(fit), row.names(fit_nfcore))
common_genes <- cbind(
results decideTests(fit)[common_genes, "KO"],
decideTests(fit_nfcore)[common_genes, "KO"]
)colnames(results) <- c("Nugent et al", "nf-core")
class(results) <- "TestResults"
summary(results)
Nugent et al nf-core
-1 10 9
0 6302 6303
1 2 2
But are these the same genes in both sets of results?
We can visualize the overlap between the sets of significant genes in a Venn diagram (FDR < 5%). The majority of differentially expressed genes is detected with both quantitation approaches (for both up- and down-regulated genes.)
::vennDiagram(results, include = c("up", "down"),
limmacounts.col=c("red", "blue"), mar = rep(0,4))
For example, the following plots show the normalized expression of the most significantly differentially expressed genes (known markers of active microglia).
Code
for (gene in topTreat(fit, coef = "KO", number = 6)[["gene_id"]]) {
<- cpms %>%
p ::filter(gene_id == gene) %>%
dplyrggplot(aes(x = genotype, y = cpm)) +
geom_point(position = position_jitter(width = 0.05), alpha = 0.8) +
facet_grid(dataset ~ ., scales = "free") +
labs(title = dge$genes[gene, "gene_name"],
y = "Normalized expression (CPM)",
x = element_blank(),
subtitle = sprintf("FDR nf-core: %s\nFDR GEO: %s",
"nfcore"],
tt[gene, "geo"]
tt[gene,
)+
) theme_linedraw(14)
print(p)
}
Applying a hard FDR threshold can inflate the number of apparent differences, e.g. when a gene is close to the significance threshold (see below).
<- cor(
p_cor $coefficients[common_genes, "KO"],
fit$coefficients[common_genes, "KO"]) fit_nfcore
The log2 fold estimates for the Hom
vs WT
comparison are well correlated across the two analysis workflows (Pearson correlation coefficient R = 0.88 ).
smoothScatter(
$coefficients[common_genes, "KO"],
fit$coefficients[common_genes, "KO"],
fit_nfcoreylab = "nf-core (log2FC)",
xlab = "Nugent et al (log2FC)",
main = "Homozygous APP vs WT (effect size)"
)text(x = 1, y = -4, labels = sprintf("R = %s", signif(p_cor, 2)))
abline(0, 1)
abline(h = 0, v = 0, lty = 2)
as are the t-statistics across all examined genes:
<- cor(
p_cor $t[common_genes, "KO"],
fit$t[common_genes, "KO"])
fit_nfcoresmoothScatter(
$t[common_genes, "KO"],
fit$t[common_genes, "KO"],
fit_nfcoreylab = "nf-core (t-statistic)",
xlab = "Nugent et al (t-statistic)",
main = "Homozygous APP vs WT (t-statistic)")
text(x = 3, y = -15, labels = sprintf("R = %s", signif(p_cor, 2)))
abline(0, 1)
abline(h = 0, v = 0, lty = 2)
Because this comparison yields only a small number of bona-fide differentially expressed genes, we don’t expect to see a high correlation between the log2 fold changes or the t-statistics between the two analyses: most of the values are very close to zero.
Discordant significance calls
# genes detected in Nugent et al, but not significant with nf-core
<- row.names(results)[which(abs(results[, 1]) == 1 & results[, 2] == 0)] genes
At FDR < 5% 2 genes were reported as significantly differentially expressed with the original Nugent et al count matrix but not with the output of the nf-core/rnaseq workflow.
As side-by-side comparison of the FDR (adj.P.Val
) for these genes confirms that the one of them (Cd52) displays significance close to the 5% threshold in the nf-core/rnaseq output as well. The second gene (Slamf8) also displays the same trend in both datasets, but is detected at lower levels (e.g. lower normalized CPMs) in the nf-core/rnaseq output.
print(tt[genes, ])
gene_name geo nfcore
ENSMUSG00000000682.7 Cd52 0.0031 0.09
ENSMUSG00000053318.7 Slamf8 0.0190 0.27
Examples
Finally, we plot the normalized gene expression estimates for the 2 discordant genes.
Code
for (gene in genes) {
<- cpms %>%
p ::filter(gene_id == gene) %>%
dplyrggplot(aes(x = genotype, y = cpm)) +
geom_point(position = position_jitter(width = 0.05), alpha = 0.8) +
facet_grid(dataset ~ ., scales = "free") +
labs(title = dge$genes[gene, "gene_name"],
y = "Normalized expression (CPM)",
x = element_blank(),
subtitle = sprintf("FDR nf-core: %s\nFDR GEO: %s",
"nfcore"],
tt[gene, "geo"]
tt[gene,
)+
) theme_linedraw(14)
print(p)
}
Conclusions
- Differential expression analyses of raw counts obtained with the
nc-core/rnaseq
workflow yields results that are highly concordant with those obtained with the raw counts the authors deposited in NCBI GEO. - With appropriate parameters the
nf-core/rnaseq
workflow can be applied to QuantSeq FWD 3’tag RNA-seq data that includes unique molecular identifiers.
Reproducibility
SessionInfo
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.3.1 (2023-06-16)
os macOS Ventura 13.5.1
system aarch64, darwin20
ui X11
language (EN)
collate en_US.UTF-8
ctype en_US.UTF-8
tz America/Los_Angeles
date 2023-08-30
pandoc 3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
─ Packages ───────────────────────────────────────────────────────────────────
! package * version date (UTC) lib source
P abind 1.4-5 2016-07-21 [?] CRAN (R 4.3.0)
P AnnotationDbi * 1.62.2 2023-07-02 [?] Bioconductor
P askpass 1.1 2019-01-13 [?] CRAN (R 4.3.0)
P Biobase * 2.60.0 2023-05-08 [?] Bioconductor
P BiocGenerics * 0.46.0 2023-06-04 [?] Bioconductor
P BiocManager 1.30.22 2023-08-08 [?] CRAN (R 4.3.0)
P Biostrings 2.68.1 2023-05-21 [?] Bioconductor
P bit 4.0.5 2022-11-15 [?] CRAN (R 4.3.0)
P bit64 4.0.5 2020-08-30 [?] CRAN (R 4.3.0)
P bitops 1.0-7 2021-04-24 [?] CRAN (R 4.3.0)
P blob 1.2.4 2023-03-17 [?] CRAN (R 4.3.0)
P cachem 1.0.8 2023-05-01 [?] CRAN (R 4.3.0)
P cli 3.6.1 2023-03-23 [?] CRAN (R 4.3.0)
P colorspace 2.1-0 2023-01-23 [?] CRAN (R 4.3.0)
P crayon 1.5.2 2022-09-29 [?] CRAN (R 4.3.0)
R credentials 1.3.2 <NA> [?] <NA>
P DBI 1.1.3 2022-06-18 [?] CRAN (R 4.3.0)
P DelayedArray 0.26.7 2023-07-30 [?] Bioconductor
P digest 0.6.33 2023-07-07 [?] CRAN (R 4.3.0)
P dplyr * 1.1.2 2023-04-20 [?] CRAN (R 4.3.0)
P edgeR * 3.42.4 2023-06-04 [?] Bioconductor
P evaluate 0.21 2023-05-05 [?] CRAN (R 4.3.0)
P fansi 1.0.4 2023-01-22 [?] CRAN (R 4.3.0)
P farver 2.1.1 2022-07-06 [?] CRAN (R 4.3.0)
P fastmap 1.1.1 2023-02-24 [?] CRAN (R 4.3.0)
P generics 0.1.3 2022-07-05 [?] CRAN (R 4.3.0)
P GenomeInfoDb * 1.36.1 2023-07-02 [?] Bioconductor
P GenomeInfoDbData 1.2.10 2023-08-23 [?] Bioconductor
P GenomicRanges * 1.52.0 2023-05-08 [?] Bioconductor
P ggplot2 * 3.4.3 2023-08-14 [?] CRAN (R 4.3.0)
P glue 1.6.2 2022-02-24 [?] CRAN (R 4.3.0)
P gtable 0.3.4 2023-08-21 [?] CRAN (R 4.3.1)
P here * 1.0.1 2020-12-13 [?] CRAN (R 4.3.0)
P htmltools 0.5.6 2023-08-10 [?] CRAN (R 4.3.0)
P htmlwidgets 1.6.2 2023-03-17 [?] CRAN (R 4.3.0)
P httr 1.4.7 2023-08-15 [?] CRAN (R 4.3.0)
P IRanges * 2.34.1 2023-07-02 [?] Bioconductor
P jsonlite 1.8.7 2023-06-29 [?] CRAN (R 4.3.0)
P KEGGREST 1.40.0 2023-05-08 [?] Bioconductor
P KernSmooth 2.23-21 2023-05-03 [?] CRAN (R 4.3.1)
P knitr 1.43 2023-05-25 [?] CRAN (R 4.3.0)
P labeling 0.4.2 2020-10-20 [?] CRAN (R 4.3.0)
P lattice 0.21-8 2023-04-05 [?] CRAN (R 4.3.1)
P lifecycle 1.0.3 2022-10-07 [?] CRAN (R 4.3.0)
P limma * 3.56.2 2023-06-04 [?] Bioconductor
P locfit 1.5-9.8 2023-06-11 [?] CRAN (R 4.3.0)
P magrittr 2.0.3 2022-03-30 [?] CRAN (R 4.3.0)
P Matrix 1.5-4.1 2023-05-18 [?] CRAN (R 4.3.1)
P MatrixGenerics * 1.12.3 2023-07-30 [?] Bioconductor
P matrixStats * 1.0.0 2023-06-02 [?] CRAN (R 4.3.0)
P memoise 2.0.1 2021-11-26 [?] CRAN (R 4.3.0)
P munsell 0.5.0 2018-06-12 [?] CRAN (R 4.3.0)
P openssl 2.1.0 2023-07-15 [?] CRAN (R 4.3.0)
P org.Mm.eg.db * 3.17.0 2023-08-23 [?] Bioconductor
P pillar 1.9.0 2023-03-22 [?] CRAN (R 4.3.0)
P pkgconfig 2.0.3 2019-09-22 [?] CRAN (R 4.3.0)
P png 0.1-8 2022-11-29 [?] CRAN (R 4.3.0)
P purrr 1.0.2 2023-08-10 [?] CRAN (R 4.3.0)
P R6 2.5.1 2021-08-19 [?] CRAN (R 4.3.0)
P Rcpp 1.0.11 2023-07-06 [?] CRAN (R 4.3.0)
P RCurl 1.98-1.12 2023-03-27 [?] CRAN (R 4.3.0)
renv 1.0.2 2023-08-15 [1] CRAN (R 4.3.0)
P rlang 1.1.1 2023-04-28 [?] CRAN (R 4.3.0)
P rmarkdown 2.24 2023-08-14 [?] CRAN (R 4.3.0)
P rprojroot 2.0.3 2022-04-02 [?] CRAN (R 4.3.0)
P RSQLite 2.3.1 2023-04-03 [?] CRAN (R 4.3.0)
P rstudioapi 0.15.0 2023-07-07 [?] CRAN (R 4.3.0)
P S4Arrays 1.0.5 2023-07-30 [?] Bioconductor
P S4Vectors * 0.38.1 2023-05-08 [?] Bioconductor
P scales 1.2.1 2022-08-20 [?] CRAN (R 4.3.0)
P sessioninfo 1.2.2 2021-12-06 [?] CRAN (R 4.3.0)
P statmod * 1.5.0 2023-01-06 [?] CRAN (R 4.3.0)
P SummarizedExperiment * 1.30.2 2023-06-11 [?] Bioconductor
P sys 3.4.2 2023-05-23 [?] CRAN (R 4.3.0)
P tibble * 3.2.1 2023-03-20 [?] CRAN (R 4.3.0)
P tidyr * 1.3.0 2023-01-24 [?] CRAN (R 4.3.0)
P tidyselect 1.2.0 2022-10-10 [?] CRAN (R 4.3.0)
P utf8 1.2.3 2023-01-31 [?] CRAN (R 4.3.0)
P vctrs 0.6.3 2023-06-14 [?] CRAN (R 4.3.0)
P withr 2.5.0 2022-03-03 [?] CRAN (R 4.3.0)
P xfun 0.40 2023-08-09 [?] CRAN (R 4.3.0)
P XVector 0.40.0 2023-05-08 [?] Bioconductor
P yaml 2.3.7 2023-01-23 [?] CRAN (R 4.3.0)
P zlibbioc 1.46.0 2023-05-08 [?] Bioconductor
[1] /Users/sandmann/repositories/blog/renv/library/R-4.3/aarch64-apple-darwin20
[2] /Users/sandmann/Library/Caches/org.R-project.R/R/renv/sandbox/R-4.3/aarch64-apple-darwin20/ac5c2659
P ── Loaded and on-disk path mismatch.
R ── Package was removed from disk.
──────────────────────────────────────────────────────────────────────────────
This work is licensed under a Creative Commons Attribution 4.0 International License.
Footnotes
Full disclosure: I am a co-author of this publication.↩︎