Preliminaries

I’ll first load some R packages.

library(data.table)
library(broman)
library(devtools)

I’ll now load the GeneSeek annotation files for the MegaMUGA and GigaMUGA arrays. There are a few rows above the header, and then some control rows at the bottom, that need to be clipped out.

gm <- data.table::fread("../GeneSeek/gigamuga_geneseek.csv",
                        skip=7, data.table=FALSE)
wh <- which(gm[,1]=="[Controls]")
gm <- gm[1:(wh-1),]
rownames(gm) <- gm$Name

mm <- data.table::fread("../GeneSeek/megamuga_geneseek.csv",
                        skip=7, data.table=FALSE)
wh <- which(mm[,1]=="[Controls]")
mm <- mm[1:(wh-1),]
rownames(mm) <- mm$Name

I’ll also load the file with the names that are in common between the two arrays.

common <- data.table::fread("../GeneSeek/common_markers.csv", data.table=FALSE)
common_markers <- common[-1,1]

Numbers of markers

The GigaMUGA array contains 143,446 markers, which the MegaMUGA has 77,808 markers. The marker names in each file are all distinct. There are 66,992 markers in common between the two arrays.

The Excel file that I received from GeneSeek has three worksheets; the second and third worksheets contain annotation information, including probe sequences, for the GigaMUGA and MegaMUGA files, respectively. The first worksheet indicates which markers are in common between the GigaMUGA and MegaMUGA arrays, but it’s a bit odd. It has counts of markers on each array, which are correct, but there’s a count that says “On Both”, but it’s 67,015, which is different that what you get in a direct check of the marker names. Also it has a list of names, presumably the ones that are in common, and it does have 67,015 markers, but only there are only 67,000 distinct names, and only 66,992 are present in the GigaMUGA array (The same, number of markers are in the MegaMUGA array.) The other 23 markers contain all the duplicates and look like control stuff and not real marker names: Extension, Hybridization, Non-Polymorphic, Non-Specific Binding, Restoration, Staining, Stringency, and Target Removal. It seems like those came from the [Control] section of the other two worksheets.

The key summaries here:

  • GigaMUGA has 143,446 SNPs
  • MegaMUGA has 77,808 SNPs
  • There are 66,992 in common

Available sequences

The GeneSeek annotation files contain four sets of sequences. AlleleA_ProbeSeq, AlleleB_ProbeSeq, SourceSeq, and TopGenomicSeq.

The AlleleA_ProbeSeq sequences are all length 50.

The AlleleB_ProbeSeq sequences are mostly missing. In the GigaMUGA file there are 469 that are not missing; in the MegaMUGA file there are 193 that are not missing.

The SourceSeq and TopGenomicSeq sequences are the same information. They’re identical when the IlmnStrand column is TOP. The latter is the reverse complement of the former when the IlmnStrand column is BOT.

The SourceSeq all seem to contain text like [A/C] that indicates the SNP. This is absent from the AlleleA_ProbeSeq which are all strictly ACGT characters.

Duplicate probe sequences

There probe sequences are not all distinct. On the GigaMUGA array, there are 2,711 that appear twice, 49 that appear three times, 2 that appear four times.

On the MegaMUGA array, there are 50 that appear twice, 0 that appear three times, 1 that appears four times.

Probe sequences for common markers

Of the 66,992 markers that are in common between the two arrays, there are 29 markers that have different probe sequences.

  • All cases where one is top strand and other is bottom strand?
  • And so one takes sourceseq to left of snp and other to right of snp? (because in all cases the snp allele is at end or just after the probe sequence?)

AlleleB probe sequences

For the small number of cases where there’s an AlleleB_ProbeSeq, in addition to AlleleA_ProbeSeq, I think the last base on the two is the SNP. They should match at the other 49 bases. Here’s a quick check of that:

# cases with AlleleB probe sequence
wh_gm <- which(gm$AlleleB_ProbeSeq != "")
# grab the two sequences
probeA <- gm$AlleleA_ProbeSeq[wh_gm]
probeB <- gm$AlleleB_ProbeSeq[wh_gm]
# first 49 characters are the same
stopifnot( all(substr(probeA, 1, 49) == substr(probeB, 1, 49)) )
# grab the last base from each, which should be the alleles
alleleA <- substr(probeA, 50, 50)
alleleB <- substr(probeB, 50, 50)
# the alleles are different
stopifnot( all( alleleA != alleleB ) )
# the alleles match the SNP column
stopifnot( all( gm$SNP[wh_gm] == paste0("[", alleleA, "/", alleleB, "]") ) )

# now all that for the MegaMUGA array...
# cases with AlleleB probe sequence
wh_mm <- which(mm$AlleleB_ProbeSeq != "")
# grab the two sequences
probeA <- mm$AlleleA_ProbeSeq[wh_mm]
probeB <- mm$AlleleB_ProbeSeq[wh_mm]
# first 49 characters are the same
stopifnot( all(substr(probeA, 1, 49) == substr(probeB, 1, 49)) )
# grab the last base from each, which should be the alleles
alleleA <- substr(probeA, 50, 50)
alleleB <- substr(probeB, 50, 50)
# the alleles are different
stopifnot( all( alleleA != alleleB ) )
# the alleles match the SNP column
stopifnot( all( mm$SNP[wh_mm] == paste0("[", alleleA, "/", alleleB, "]") ) )

The code aboved checked that for the cases where there’s an AlleleB probe sequence, the first 49 bases are the same, between the two alleles, while the 50th base is the SNP

For the remaining probes, I believe that the SNPs is just after the probe sequence. We’ll check that by comparing it to the source sequences, next.

Compare probes to source sequences

I’m presuming that the probe sequences match the part of the source sequence from just before the [A/T] SNP part, but I want to check.

I’ll first check that the SourceSeq column matches the TopGenomicSeq column, using the reverse complement when SourceStrand is "BOT".

source("revcomp.R") # reverse complement

# reverse complement of source sequence
#    ...a few cases of lower case letters in the latter
#    ...and it's tricky handling the SNP alleles, b/c they shouldn't be swapped
gm_source_rev <- revcomp(gm$SourceSeq)

# when SourceStrand=="TOP", the SourceSeq and TopGenomicSeq match
# when SourceStrand=="BOT", the TopGenomicSeq is the reverse complement
stopifnot( all((gm$SourceSeq == gm$TopGenomicSeq & gm$SourceStrand=="TOP") |
               (gm_source_rev == gm$TopGenomicSeq & gm$SourceStrand=="BOT")) )

# now same thing with MegaMUGA
# reverse complement of source sequence
#    ...a few cases of lower case letters in the latter
#    ...and it's tricky handling the SNP alleles, b/c they shouldn't be swapped
mm_source_rev <- revcomp(mm$SourceSeq)

# when SourceStrand=="TOP", the SourceSeq and TopGenomicSeq match
# when SourceStrand=="BOT", the TopGenomicSeq is the reverse complement
stopifnot( all((mm$SourceSeq == mm$TopGenomicSeq & mm$SourceStrand=="TOP") |
               (mm_source_rev == mm$TopGenomicSeq & mm$SourceStrand=="BOT")) )

If we made it here, than that was true and the SourceSeq and TopGenomicSeq columns have the same information, so we can just focus on the SourceSeq column.

I now want to compare the SNP information in the source sequence to the SNP column.

# grab SNP info from SourceSeq, with alleles in both orders
gm_source_snp <- sub("^.*\\[", "[", sub("\\].*$", "]", gm$SourceSeq))
gm_source_snp_comp <- justcomp(gm_source_snp)

# check that SNP column is one or the other
stopifnot( all(gm_source_snp == gm$SNP | gm_source_snp_comp == gm$SNP) )

# BUT NOTE: I thought whether it was one or the other depended on whether
#           IlmnStrand == SourceStrand or not, but there are some differences.
gm_snp_strand_problem <- ((gm_source_snp == gm$SNP & gm$IlmnStrand != gm$SourceStrand) |
                          (gm_source_snp_comp == gm$SNP & gm$IlmnStrand == gm$SourceStrand))

# but these are all A/T or C/G cases, so just about defining which allele is A vs B
stopifnot( all( gm_source_snp[gm_snp_strand_problem] %in% c("[A/T]", "[C/G]") ) )

mm_source_snp <- sub("^.*\\[", "[", sub("\\].*$", "]", mm$SourceSeq))
mm_source_snp_comp <- justcomp(mm_source_snp)

# check that SNP column is one or the other
stopifnot( all(mm_source_snp == mm$SNP | mm_source_snp_comp == mm$SNP) )

# mismatches between direct/complement match and whether strands are the same
mm_snp_strand_problem <- ((mm_source_snp == mm$SNP & mm$IlmnStrand != mm$SourceStrand) |
                          (mm_source_snp_comp == mm$SNP & mm$IlmnStrand == mm$SourceStrand))

# but these are all A/T or C/G cases, so just about defining which allele is A vs B
stopifnot( all( mm_source_snp[mm_snp_strand_problem] %in% c("[A/T]", "[C/G]") ) )

From this, we find that the SNP column is the same as the SNP in the SourceSeq, or it is the complement of that. I thought whether it was a direct match or a match to the complement would be according to whether IlmnStrand == SourceStrand or not, but that there are 237 mismatches on the GigaMUGA and 99 mismatches on the MegaMUGA. But these are all cases of A/T or C/G SNPs, so it’s just a matter of the definition of the A vs B alleles.

Finally (for this section), I’ll now turn to whether the SNP sequences match the source sequences.

I’ll first split the source sequences into the bits before and after the snp. I’ll also compare the SNP part of the source sequence to the SNP column. Then I’ll compare the parts of the source sequence to AlleleA probes.

# grab the sequence before and after the SNP
gm_source_spl <- strsplit(gm$SourceSeq, "\\[[ACGT]/[ACGT]\\]")
gm_before_snp <- sapply(gm_source_spl, "[", 1) # a bunch of these have length 0
gm_after_snp <- sapply(gm_source_spl, "[", 2)
gm_after_snp[is.na(gm_after_snp)] <- "" # turn NAs into emptys

# for the SNPs that have both A and B probe sequences, drop the last character from the A sequence
gm_probe <- gm$AlleleA_ProbeSeq
gm_probe[gm$AlleleB_ProbeSeq != ""] <- substr(gm_probe[gm$AlleleB_ProbeSeq != ""], 1, 49)
gm_probe_rev <- revcomp(gm_probe)

# subset the before and after sequences to no longer than the probe
nc <- nchar(gm_before_snp)
start <- nc - nchar(gm_probe)+1
start[start < 1] <- 1
gm_before_sub <- gm_before_snp
gm_before_sub[nc>0] <- substr(gm_before_snp[nc>0], start[nc>0], nc[nc>0])

nc <- nchar(gm_after_snp)
end <- nchar(gm_probe)
end[end > nc] <- nc[end > nc]
gm_after_sub <- gm_after_snp
gm_after_sub[nc>0] <- substr(gm_after_snp[nc>0], 1, end[nc>0])

# There were 6 cases where probe doesn't match sequence, but due to lower case letters
# so, convert to upper case
gm_before_sub <- toupper(gm_before_sub)
gm_after_sub <- toupper(gm_after_sub)

stopifnot( all(gm_after_sub == gm_probe_rev | gm_before_sub == gm_probe) )

# also check the strand
stopifnot( all((gm_after_sub == gm_probe_rev & gm$IlmnStrand != gm$SourceStrand) |
               (gm_before_sub == gm_probe & gm$IlmnStrand == gm$SourceStrand)) )

# Now the MegaMUGA
# grab the sequence before and after the SNP
mm_source_spl <- strsplit(mm$SourceSeq, "\\[[ACGT]/[ACGT]\\]")
mm_before_snp <- sapply(mm_source_spl, "[", 1) # a bunch of these have length 0
mm_after_snp <- sapply(mm_source_spl, "[", 2)
mm_after_snp[is.na(mm_after_snp)] <- "" # turn NAs into emptys

# for the SNPs that have both A and B probe sequences, drop the last character from the A sequence
mm_probe <- mm$AlleleA_ProbeSeq
mm_probe[mm$AlleleB_ProbeSeq != ""] <- substr(mm_probe[mm$AlleleB_ProbeSeq != ""], 1, 49)
mm_probe_rev <- revcomp(mm_probe)

# subset the before and after sequences to no longer than the probe
nc <- nchar(mm_before_snp)
start <- nc - nchar(mm_probe)+1
start[start < 1] <- 1
mm_before_sub <- mm_before_snp
mm_before_sub[nc>0] <- substr(mm_before_snp[nc>0], start[nc>0], nc[nc>0])

nc <- nchar(mm_after_snp)
end <- nchar(mm_probe)
end[end > nc] <- nc[end > nc]
mm_after_sub <- mm_after_snp
mm_after_sub[nc>0] <- substr(mm_after_snp[nc>0], 1, end[nc>0])

# There were 9 cases where probe doesn't match sequence, but due to lower case letters
# so, convert to upper case
mm_before_sub <- toupper(mm_before_sub)
mm_after_sub <- toupper(mm_after_sub)

stopifnot( all(mm_after_sub == mm_probe_rev | mm_before_sub == mm_probe) )

# also check the strand
stopifnot( all((mm_after_sub == mm_probe_rev & mm$IlmnStrand != mm$SourceStrand) |
               (mm_before_sub == mm_probe & mm$IlmnStrand == mm$SourceStrand)) )

Having gotten here, the probes match the source sequences exactly. SNP is just after the sequence when IlmnStrand is the same as SourceStrand, and is just before the reverse-complement of the sequence when the strands are different.

Do the sequences match the UNC files?

We should do a quick check of whether the probe sequences match those in the UNC files.

load("../UNC/snps.gigamuga.Rdata")
gm_unc <- snps
load("../UNC/snps.megamuga.Rdata")
mm_unc <- snps
rm(snps)

The UNC files for the GigaMUGA and MegaMUGA arrays have 143,259 and 77,808 rows, respectively. So the MegaMUGA file has the same number of rows as the file from GeneSeek, but the GigaMUGA file seems to be missing 187 rows. The missing markers are all have names that start CTRL or CTRL2. So control probes, I suppose.

stopifnot( all(gm_unc$marker %in% gm$Name) )
stopifnot( all(sort(mm_unc$marker) == sort(mm$Name)) )

Otherwise, all of the marker names are the same.

Let’s look to see if the sequences are the same.

# sequences in MM files match?
stopifnot( all(mm_unc[mm$Name, "seq.A"] ==  mm$AlleleA_ProbeSeq) )

# subset GeneSeek GM file to those in the UNC file
gm_sub <- gm[gm$Name %in% gm_unc$marker,]
# sequences in GM files match?
stopifnot( all(gm_unc[gm_sub$Name, "seq.A"] ==  gm_sub$AlleleA_ProbeSeq) )

Yes, the sequences all match exactly.

I should also compare the chromosome assignments between the GeneSeek and UNC files.

For the MegaMUGA array, they are the same except some of Y in GeneSeek are P in UNC, and most of the P and M in UNC are 0 in GeneSeek.

Here’s a table just of the markers where they differ. The rows are the UNC assignments; the columns are the GeneSeek ones.

chr_lev <- c(0:19,"X","Y","P","M")
mm_unc_chr <- factor(sub("chr", "", mm_unc[mm$Name, "chr"]), chr_lev)
mm_gs_chr <- factor(mm$Chr, chr_lev)
mm_chr_mismatch <- mm_unc_chr != mm_gs_chr
table(mm_unc_chr[mm_chr_mismatch], mm_gs_chr[mm_chr_mismatch])
##     
##       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19  X  Y  P  M
##   0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   1   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   2   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   3   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   4   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   5   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   6   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   7   1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   8   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   9   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   10  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   11  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   12  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   13  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   14  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   15  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   16  5  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   17  5  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   18  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   19  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   X   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   Y   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   P  14  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 69  0  0
##   M  46  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

For GM, on the other hand, there are a whole bunch of markers with disagreement. Also note that the UNC file for the GigaMUGA also has a bunch of markers with NA in the chromosome column. I think they should be “P”, so I’ll call them that for now.

gm_unc_chr <- factor(sub("chr", "", gm_unc[gm_sub$Name, "chr"]), chr_lev)
gm_unc_chr[is.na(gm_unc_chr)] <- "P"
gm_gs_chr <- factor(gm_sub$Chr, chr_lev)
gm_chr_mismatch <- gm_unc_chr != gm_gs_chr
table(gm_unc_chr[gm_chr_mismatch], gm_gs_chr[gm_chr_mismatch])
##     
##       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19  X  Y  P  M
##   0   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
##   1   8  0 11  9 13  3  3  6  1  1  1  5  2  2  5  4  0  4  4  1  1  0  0  0
##   2  14  5  0  6 11 19  6  7  4  3  2  6  2  5  5  5  1  5  0  3  3  0  0  0
##   3   0  8  2  0  3  6  6  6  5  5  6  3  2  1  4  2  1  4  2  3  5  0  0  0
##   4   0  5  2  5  0  4  4  2  2  3  2  8  4  8 10  9  3  4  1  2  7  0  0  0
##   5   0  7  3  2  1  0  4  3  5  1  6  5  0  6  3  7  8  8  3  1  4  1  0  0
##   6   0  2  2  5  6  5  0  4  3  3  0  1  2  3  4  1  3  1  4  4 16  0  0  0
##   7   1  8  7  1  7  2  2  0  4  4  5  0  2  4  2  0  3  2  1  2  2  0  0  0
##   8   0  3  4  3  1  4  1  3  0  3  5  1  3  1  0  0  1  3  3  3  1  0  0  0
##   9   0  6  8  6  2  4  6  4  4  0  1  2  2  3  1  2  2  4  1  0  1  0  0  0
##   10  0  4  4  7  3  1  3  3  1  1  0  2  2  2  1  4  1  3  1  4  0  0  0  0
##   11  1  4  5  2  3  2  0  2  4  1  6  0  3  0  2  5  2  3  0  3  2  0  0  0
##   12  1  1  2  7  2  3  0  2  3  2  2  0  0  3  1  1  0  1  2  3  3  0  0  0
##   13  2  6  3  4  2  5  1  1  1  2  4  2  1  0  4  2  2  6  4  4  7  0  0  0
##   14  0  5  7  4  2  3  3  5  2  2  1  1  2  1  0  5  0  2  0  1  1  0  0  0
##   15  0  5  4  3  3  2  1  4  3  1  2  1  4  0  3  0  1  3  2  2  3  0  0  0
##   16  2  1  1  3  5  3  2  2  1  2  0  1  2  5  1  2  0  3  1  2  1  0  0  0
##   17  3  3  5  0  0  2  1  3  0  1  2  4  3  0  2  1  2  0  2  2  1  0  0  0
##   18  0  4  2  3  3  1  2  4  1  1  1  0  2  2  3  2  3  1  0  5  1  0  0  0
##   19  0  1  3  1  2  1  1  2  1  0  1  0  1  0  0  2  2  2  2  0  2  0  0  0
##   X   0  6 10  5  6  2  6  5  1  2  6  6  1  4  6  5  0  3  4  4  0  0  0  0
##   Y   0  1  0  2  2  1  1  2  1  1  0  1  1  0  1  2  0  0  0  0 33  0  0  0
##   P  11  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  5  0  0 64  0  0
##   M  32  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

BLASTn results

Session info

Here are the versions of R and R packages that I am using.

devtools::session_info()
## ─ Session info ─────────────────────────────────────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 4.0.3 (2020-10-10)
##  os       Pop!_OS 20.10               
##  system   x86_64, linux-gnu           
##  ui       X11                         
##  language en_US:en                    
##  collate  en_US.UTF-8                 
##  ctype    en_US.UTF-8                 
##  tz       America/Chicago             
##  date     2020-11-26                  
## 
## ─ Packages ─────────────────────────────────────────────────────────────────────────────────────────────────
##  package     * version date       lib source        
##  assertthat    0.2.1   2019-03-21 [1] CRAN (R 4.0.0)
##  broman      * 0.71-6  2020-11-24 [1] CRAN (R 4.0.3)
##  callr         3.5.1   2020-10-13 [1] CRAN (R 4.0.3)
##  cli           2.2.0   2020-11-20 [1] CRAN (R 4.0.3)
##  crayon        1.3.4   2017-09-16 [1] CRAN (R 4.0.0)
##  data.table  * 1.13.2  2020-10-19 [1] CRAN (R 4.0.3)
##  desc          1.2.0   2018-05-01 [1] CRAN (R 4.0.0)
##  devtools    * 2.3.2   2020-09-18 [1] CRAN (R 4.0.2)
##  digest        0.6.27  2020-10-24 [1] CRAN (R 4.0.3)
##  ellipsis      0.3.1   2020-05-15 [1] CRAN (R 4.0.0)
##  evaluate      0.14    2019-05-28 [1] CRAN (R 4.0.0)
##  fansi         0.4.1   2020-01-08 [1] CRAN (R 4.0.0)
##  fs            1.5.0   2020-07-31 [1] CRAN (R 4.0.2)
##  glue          1.4.2   2020-08-27 [1] CRAN (R 4.0.2)
##  htmltools     0.5.0   2020-06-16 [1] CRAN (R 4.0.1)
##  knitr         1.30    2020-09-22 [1] CRAN (R 4.0.2)
##  magrittr      2.0.1   2020-11-17 [1] CRAN (R 4.0.3)
##  memoise       1.1.0   2017-04-21 [1] CRAN (R 4.0.0)
##  pkgbuild      1.1.0   2020-07-13 [1] CRAN (R 4.0.2)
##  pkgload       1.1.0   2020-05-29 [1] CRAN (R 4.0.0)
##  prettyunits   1.1.1   2020-01-24 [1] CRAN (R 4.0.0)
##  processx      3.4.4   2020-09-03 [1] CRAN (R 4.0.2)
##  ps            1.4.0   2020-10-07 [1] CRAN (R 4.0.2)
##  R6            2.5.0   2020-10-28 [1] CRAN (R 4.0.3)
##  remotes       2.2.0   2020-07-21 [1] CRAN (R 4.0.3)
##  rlang         0.4.8   2020-10-08 [1] CRAN (R 4.0.2)
##  rmarkdown     2.5     2020-10-21 [1] CRAN (R 4.0.3)
##  rprojroot     2.0.2   2020-11-15 [1] CRAN (R 4.0.3)
##  sessioninfo   1.1.1   2018-11-05 [1] CRAN (R 4.0.0)
##  stringi       1.5.3   2020-09-09 [1] CRAN (R 4.0.2)
##  stringr       1.4.0   2019-02-10 [1] CRAN (R 4.0.0)
##  testthat      3.0.0   2020-10-31 [1] CRAN (R 4.0.3)
##  usethis     * 1.6.3   2020-09-17 [1] CRAN (R 4.0.2)
##  withr         2.3.0   2020-09-22 [1] CRAN (R 4.0.2)
##  xfun          0.19    2020-10-30 [1] CRAN (R 4.0.3)
##  yaml          2.2.1   2020-02-01 [1] CRAN (R 4.0.0)
## 
## [1] /home/kbroman/Rlibs
## [2] /usr/local/lib/R/site-library
## [3] /usr/lib/R/site-library
## [4] /usr/lib/R/library