library(dplyr)
library(edgeR)
library(ggplot2)
library(here)
library(org.Mm.eg.db)
library(readxl)
library(SummarizedExperiment)
library(tibble)
library(tidyr)
This is the third 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 without unique molecular identifiers.
- 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 Xia et al.
Next, we use Bioconductor/R packages to reproduce the downstream results. We perform the same differential gene expression 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 as the sample_metadata.csv CSV file in case 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", "Batch", "sex",
"Genotype", "Sample.Name")])
Run Animal.ID Age age_unit Batch sex Genotype Sample.Name
SRX9142648 SRR12661924 LA1 8 months Day1 male WT GSM4793335
SRX9142649 SRR12661925 LA1 8 months Day1 male WT GSM4793336
SRX9142650 SRR12661926 LA6 8 months Day1 male WT GSM4793337
SRX9142651 SRR12661927 LA6 8 months Day1 male WT GSM4793338
SRX9142652 SRR12661928 LA9 8 months Day2 male WT GSM4793339
SRX9142653 SRR12661929 LA9 8 months Day2 male WT GSM4793340
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 GSM4793335 DRN-18429
2 GSM4793336 DRN-18430
3 GSM4793337 DRN-18439
4 GSM4793338 DRN-18440
5 GSM4793339 DRN-18445
6 GSM4793340 DRN-18446
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 "batch")]
$genotype <- factor(sample_anno$genotype,
sample_annolevels = c("WT", "Het", "Hom"))
$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 batch sample_title
SRX9142648 GSM4793335 LA1 WT male Day1 DRN-18429
SRX9142649 GSM4793336 LA1 WT male Day1 DRN-18430
SRX9142650 GSM4793337 LA6 WT male Day1 DRN-18439
SRX9142651 GSM4793338 LA6 WT male Day1 DRN-18440
SRX9142652 GSM4793339 LA9 WT male Day2 DRN-18445
SRX9142653 GSM4793340 LA9 WT male Day2 DRN-18446
This experiment includes 36 samples of microglia cells obtained from 18 different 8-month old mice. Both male and female animals were included in the study.
The animals carry one of three different genotypes of the gene encoding the APP amyloid beta precursor protein, either
- the wildtype mouse gene (
WT
) or - one copy (
Het
) or - two copies (
Hom
)
of a mutant APP gene carrying mutations associated with familial Alzheimer’s Disease.
Samples from all three genotypes were collected on three days, and we will use this batch
information to model the experiment.
Two separate microglia samples were obtained from each animal, and we will include this nested relationship by modeling the animal
as random effect in our linear model.
Xia et al’s original count data
First, we retrieve the authors’ count matrix from NCBI GEO, available as a Supplementary Excel file.
<- paste0("https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE158152&",
url "format=file&file=GSE158152%5Fdst150%5Fprocessed%2Exlsx")
<- tempfile(fileext = ".xlsx")
temp_file download.file(url, destfile = temp_file)
The Excel file has three different worksheets
sample_annotations
raw_counts
normalized_cpm
<- read_excel(temp_file, sheet = "raw_counts")
raw_counts head(colnames(raw_counts), 10)
[1] "feature_id" "name" "meta" "source" "symbol"
[6] "DRN-18429" "DRN-18430" "DRN-18439" "DRN-18440" "DRN-18445"
The raw_counts
excel sheet contains information about the detected genes ( feature_ID
, name
) and the samples are identified by their GEO title (e.g. DRN-18459
, DRN-184560
). 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("DRN-", colnames(raw_counts))])
count_data row.names(count_data) <- raw_counts$feature_id
colnames(count_data) <- row.names(sample_anno)[
match(colnames(count_data), sample_anno$sample_title)
]
<- data.frame(
gene_data gene_id = raw_counts$feature_id,
gene_name = raw_counts$symbol,
row.names = raw_counts$feature_id
)
<- data.frame(
col_data colnames(count_data),
sample_anno[c("sample_title", "animal_id", "sex", "genotype", "batch")],
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
Next, we project the samples into two dimensions by performing multi-dimensional scaling of the top 500 most variable genes. The samples cluster by genotype, with WT
and Het
segregating from the Hom
samples.
plotMDS(dge, labels = dge$samples$genotype,
main = "Multi-dimensional scaling",
sub = "Based on the top 500 most variable genes")
Let’s identify which genes are significantly differentially expressed between the three 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. (The min.count = 25
parameter was determined by examining the mean-variance plot by the voomLmFit()
function.)
<- model.matrix(~ genotype + sex + batch, data = dge$samples)
design colnames(design) <- sub("genotype", "", colnames(design))
<- filterByExpr(dge, design = design, min.count = 25) keep
Next, we fit a linear model to the data using the limma/voom approach. The model includes the following fixed effects:
- The
genotype
coded as a factor with theWT
as the reference level. - The
sex
andbatch
covariates, to account for systematic differences in mean gene expression.
Because the dataset included two replicate samples from each animal, we model the animal
as a random effect (via the block
argument of the voomLmFit()
function). We then extract the coefficients, log2 fold changes and p-values via limma’s empirical Bayes approach.
We use the limma::treat()
function to test the null hypothesis that genes display significant differential expression greater than 1.2-fold. This is more stringent than the conventional null hypothesis of zero change. (Please consult the limma::treat()
help page for details.)
<- voomLmFit(
fit row.names(design)],
dge[keep, design = design,
block = dge$samples$animal_id,
sample.weights = TRUE,
plot = FALSE
)<- treat(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%. While we did not detect significant differences between Het
and WT
animals, the analysis revealed > 450 differentially expressed genes between Hom
and WT
microglia.
summary(decideTests(fit))[, c("Het", "Hom")]
Het Hom
Down 0 73
NotSig 10131 9660
Up 0 398
The top 10 genes with the smallest p-values include well known markers of microglia activation:
topTreat(fit, coef = "Hom")[, c("gene_name", "logFC", "P.Value", "adj.P.Val")]
gene_name logFC P.Value adj.P.Val
ENSMUSG00000027523 Gnas 1.5904760 3.467080e-24 3.512499e-20
ENSMUSG00000022265 Ank 3.7866023 1.258687e-20 6.375881e-17
ENSMUSG00000021477 Ctsl 1.2829086 2.294556e-20 7.748717e-17
ENSMUSG00000018927 Ccl6 2.7405831 3.514945e-20 8.902477e-17
ENSMUSG00000030579 Tyrobp 1.2880854 9.748473e-19 1.975236e-15
ENSMUSG00000022415 Syngr1 1.5033949 1.367158e-18 2.308446e-15
ENSMUSG00000016256 Ctsz 1.1557870 2.878895e-18 4.166584e-15
ENSMUSG00000023992 Trem2 0.9460719 7.386805e-18 9.354465e-15
ENSMUSG00000056737 Capg 2.4537482 1.927765e-17 2.170021e-14
ENSMUSG00000030342 Cd9 1.0596747 1.328189e-16 1.328093e-13
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.
The nf-core pipeline returned the versioned ENSEMBL gene identifiers (e.g.) ENSMUSG00000000001.4
. Because Xia et al only provided the unversioned identifiers (e.g. ENSMUSG00000000001
) we trim the numeric suffix.
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 row.names(se) <- sapply(
strsplit(row.names(se), split = ".", fixed = TRUE), "[[", 1)
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
$genes$gene_id <- row.names(dge_nfcore)
dge_nfcore
$samples <- data.frame(
dge_nfcore$samples,
dge_nfcorecolnames(dge_nfcore),
sample_anno[c("sample_title", "animal_id", "sex", "genotype", "batch")],
workflow = "nfcore"
)stopifnot(all(colnames(dge) %in% colnames(dge_nfcore)))
<- dge_nfcore[, colnames(dge)]
dge_nfcore
<- model.matrix(~ genotype + sex + batch, data = dge_nfcore$samples)
design colnames(design) <- sub("genotype", "", colnames(design))
<- filterByExpr(dge_nfcore, design = design, min.count = 25)
keep <- voomLmFit(
fit_nfcore row.names(design)],
dge_nfcore[keep, design = design,
block = dge_nfcore$samples$animal_id,
sample.weights = TRUE,
plot = FALSE
)
First sample weights (min/max) 0.5573601/2.1684287
First intra-block correlation 0.02343718
Final sample weights (min/max) 0.536793/2.237000
Final intra-block correlation 0.02358457
Code
<- treat(fit_nfcore, robust=TRUE) fit_nfcore
As with the original count data from NCBI GEO, we detect > 450 differentially expressed genes between Hom
and WT
genotypes (FDR < 5%, null hypothesis: fold change > 1.2).
summary(decideTests(fit_nfcore))[, c("Het", "Hom")]
Het Hom
Down 0 76
NotSig 10428 9917
Up 0 435
Comparing results across preprocessing workflows
Next, we compare the results obtained with the two datasets. We create the cpms
and tt
dataframes, holding the combined absolute and differential expression results, respectively.
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 = "Xia 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 = "Hom", number = Inf)[
c("gene_id", "gene_name", "logFC", "P.Value", "adj.P.Val")] %>%
, ::mutate(dataset = "geo"),
dplyrtopTreat(fit_nfcore, coef = "Hom", 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.
Code
<- 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(`Xia et al`), log1p(`nf-core`)),
mean_xia = mean(`Xia 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)
The correlation between normalized log2 expression estimates is very high, with 95% of all genes showing a Pearson correlation coefficient > 0.94.
Most of the 10022 examined genes were detected with > 10 normalized counts per million reads.
<- ggplot(data = sum_stats, aes(x = mean_xia + 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 = "Xia 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
<- cbind(
results decideTests(fit)[common_genes, "Hom"],
decideTests(fit_nfcore)[common_genes, "Hom"]
)colnames(results) <- c("Xia et al", "nf-core")
class(results) <- "TestResults"
summary(results)
Xia et al nf-core
-1 73 75
0 9554 9528
1 395 419
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 vast 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 a few highly differentially expressed genes (known markers of active microglia).
Code
for (gene in topTreat(fit, coef = "Hom", 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, "Hom"],
fit$coefficients[common_genes, "Hom"]) fit_nfcore
The log2 fold estimates for the Hom
vs WT
comparison are highly correlated across the two analysis workflows (Pearson correlation coefficient R = 0.99 ):
smoothScatter(
$coefficients[common_genes, "Hom"],
fit$coefficients[common_genes, "Hom"],
fit_nfcoreylab = "nf-core (log2FC)",
xlab = "Xia et al (log2FC)",
main = "Homozygous APP vs WT (effect size)"
)text(x = 10, y = -2, 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, "Hom"],
fit$t[common_genes, "Hom"])
fit_nfcoresmoothScatter(
$t[common_genes, "Hom"],
fit$t[common_genes, "Hom"],
fit_nfcoreylab = "nf-core (t-statistic)",
xlab = "Xia et al (t-statistic)",
main = "Homozygous APP vs WT (t-statistic)")
text(x = 10, y = -2, labels = sprintf("R = %s", signif(p_cor, 2)))
abline(0, 1)
abline(h = 0, v = 0, lty = 2)
Discordant significance calls
# genes detected in GEO, but not significant with nf-core
<- row.names(results)[which(abs(results[, 1]) == 1 & results[, 2] == 0)] genes
At FDR < 5% 14 genes were reported as significantly differentially expressed with the original Xia 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 vast majority display significant close to the 5% threshold in the nf-core/rnaseq output as well. (This is in line with the high overall correlation of the t-statistics observed above.)
print(tt[genes, ])
gene_name geo nfcore
ENSMUSG00000078193 RP24-228M19.1 0.0041 0.120
ENSMUSG00000027427 Polr3f 0.0490 0.052
ENSMUSG00000028394 Pole3 0.0480 0.073
ENSMUSG00000029027 Dffb 0.0360 0.082
ENSMUSG00000029649 Pomp 0.0430 0.056
ENSMUSG00000054404 Slfn5 0.0440 0.064
ENSMUSG00000050965 Prkca 0.0310 0.087
ENSMUSG00000020641 Rsad2 0.0440 0.063
ENSMUSG00000021057 Akap5 0.0260 0.074
ENSMUSG00000115230 RP24-123O20.1 0.0400 0.061
ENSMUSG00000042622 Maff 0.0500 0.051
ENSMUSG00000050410 Tcf19 0.0500 0.051
ENSMUSG00000059040 Eno1b 0.0290 0.410
ENSMUSG00000042712 Tceal9 0.0480 0.054
Finally, we plot the normalized gene expression estimates for the 14 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.
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 cellranger 1.1.0 2016-07-27 [?] 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)
P readxl * 1.4.3 2023-07-06 [?] 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.↩︎