snATAC-seq#
Check this GitHub page to see how snATAC-seq libraries are generated experimentally. The library preparation steps are the same as the sci-ATAC-seq method. It is a split-pool based combinatorial indexing strategy, where nuclei are transposed in mini-bulk with indexed transposase Tn5 (T5 + T7). Then all nuclei are pooled and a fraction of the nuclei is randomly distributed into new wells. Library preparation is performed using a PCR primers with different indices (i5 + i7). Single cells can be identified by the combination of T7 + i7 + T5 + i5. The developers named the Tn5 barcode at the i5 side T5
, and the i7 side T7
. In this documentation, I will keep the name consistent with the developers.
Important
The library of the snATAC-seq is the same as sci-ATAC-seq, with minor sequence difference. It is kind of complicated, especially for beginners. First, make sure you are familiar with different sequencing modes from different Illumina machines by looking at this page. Then make sure you understand how the actual sequencing is done for snATAC-seq by checking this GitHub page and the associated publications.
For Your Own Experiments#
If you use this assay, you have to run the sequencing by yourself using a custom sequencing recipe or ask your core facility to do this for you. We don’t have the proper setup to do this experiment here, so I haven’t tried snATAC-seq by myself. This is the educational guess of the sequencing configuration. Spike-in libraries are used in this assay, so that dark cycles are not needed when sequencing the index.
Using more recent machines and chemistries, like iSeq 100, MiniSeq (Standard), NextSeq, HiSeq X, HiSeq 3000, HiSeq 4000 and NovaSeq 600 (v1.5), it should be (I call this Configuration 1):
Order |
Read |
Cycle |
Description |
---|---|---|---|
1 |
Read 1 |
>50 |
This yields |
2 |
Index 1 (i7) |
43 |
This yields |
3 |
Index 2 (i5) |
37 |
This yields |
4 |
Read 2 |
>50 |
This yields |
Using older machines and chemistries like MiSeq, HiSeq 2000, HiSeq 2500, MiniSeq (Rapid) and NovaSeq 6000 (v1.0), it should be (I call this Configuration 2):
Order |
Read |
Cycle |
Description |
---|---|---|---|
1 |
Read 1 |
>50 |
This yields |
2 |
Index 1 (i7) |
43 |
This yields |
3 |
Index 2 (i5) |
37 |
This yields |
4 |
Read 2 |
>50 |
This yields |
The two configurations are very similar. The only difference is how I2
is sequenced and generated. Again, if you are not sure, check this GitHub page to see the reason.
After the sequencing is done, you could just run the bcl2fastq
without a SampleSheet.csv
, like this:
bcl2fastq --create-fastq-for-index-reads \
--no-lane-splitting \
--ignore-missing-positions \
--ignore-missing-controls \
--ignore-missing-filter \
--ignore-missing-bcls \
-r 4 -w 4 -p 4
In this case, you will have four fastq
files per run:
Undetermined_S0_I1_001.fastq.gz
Undetermined_S0_I2_001.fastq.gz
Undetermined_S0_R1_001.fastq.gz # >50 bp, genomic insert
Undetermined_S0_R2_001.fastq.gz # >50 bp, genomic insert
The R1
and R2
files contain genomic inserts, which can be used directly. We don’t need to do anything about them.
This is the content of Undetermined_S0_I1_001.fastq.gz
regardless of the machine configuration:
Length |
Sequence (5’ -> 3’) |
---|---|
43 bp |
8 bp |
This is the content of Undetermined_S0_I1_001.fastq.gz
:
Configuration |
Length |
Sequence (5’ -> 3’) |
---|---|---|
Configuration 1 |
37 bp |
8 bp |
Configuration 2 |
37 bp |
8 bp |
As you can see, your cell barcodes will be the first 8bp and the last 8 bp of I1
+ the first 8bp and the last 8 bp of I2
. We could stitch them together to get the 32-bp cell barcodes to a single fastq
file:
paste <(zcat Undetermined_S0_I1_001.fastq.gz) <(zcat Undetermined_S0_I2_001.fastq.gz) | \
awk -F '\t' '{ if(NR%4==1||NR%4==3) {print $1} else {print substr($1,1,8) substr($1,36,8) substr($2,1,8) substr($2,30,8)} }' | \
gzip > Undetermined_S0_CB_001.fastq.gz
After that, you are ready to throw Undetermined_S0_R1_001.fastq.gz
, Undetermined_S0_R2_001.fastq.gz
and Undetermined_S0_CB_001.fastq.gz
into chromap
. See the later section.
Public Data#
For the purpose of demonstration, we will use the snATAC-seq data from the following paper:
Note
Fang R, Preissl S, Li Y, Hou X, Lucero J, Wang X, Motamedi A, Shiau AK, Zhou X, Xie F, Mukamel EA, Zhang K, Zhang Y, Behrens MM, Ecker JR, Ren B (2021) Comprehensive analysis of single cell ATAC-seq data with SnapATAC. Nat Commun 12:1337. https://doi.org/10.1038/s41467-021-21583-9
where the authors developed a computational tool called SnapATAC for the analysis of scATAC-seq data. They used snATAC-seq to generate chromatin accessibility profiles of tens of thousands of nuclei from mouse secondary motor cortex. The accession code is GSE126724. There are quite a few samples there. I’m using the snATAC MOs A rep1 sample (SRR8592624). To get the reads, we use fastq-dump
from the sratools
:
mkdir -p snATAC-seq/data
fastq-dump --split-files \
--origfmt \
--outdir snATAC-seq/data \
--defline-seq '@rd.$si:$sg:$sn' SRR8592624
The reason of using the specific options above is a bit complicated. I wrote a post about this, and you can have a look to see the reason. Since the authors also uploaded the index reads, once the program finishes running, you will have four plain files: SRR8592624_1.fastq
, SRR8592624_2.fastq
, SRR8592624_3.fastq
and SRR8592624_4.fastq
. Unfortunately, it seems fastq-dump
cannot generate gzipped file on the fly. I’m going to gzip them manually to save space:
gzip snATAC-seq/data/SRR8592624_*.fastq
SRR8592624_1.fastq
and SRR8592624_2.fastq
are the genomic inserts. SRR8592624_3.fastq
and SRR8592624_4.fastq
are the index reads. Let’s have a look at the first 5 reads from SRR8592624_3.fastq
and SRR8592624_4.fastq
:
# from SRR8592624_3.fastq
@rd.1::AGACGGAGTGCAGCTATGTCAGCTACTGCATA:7001113:948:HLFKYBCX2:1:1108:1237:1880
AGACGGAGTGCAGCTA
+AGACGGAGTGCAGCTATGTCAGCTACTGCATA:7001113:948:HLFKYBCX2:1:1108:1237:1880
DBDDDIHHIIEEFHGH
@rd.2::AGAGCAGTAACCAGGTACAAGGATAAGAGATG:7001113:948:HLFKYBCX2:1:1108:1027:1904
AGAGCAGTAACCAGGT
+AGAGCAGTAACCAGGTACAAGGATAAGAGATG:7001113:948:HLFKYBCX2:1:1108:1027:1904
ADDDDIIIIGGGHIIF
@rd.3::ACATTGGCGCTCATGAGGAAGACTTTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1056:1909
ACATTGGCGCTCATGA
+ACATTGGCGCTCATGAGGAAGACTTTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1056:1909
DDDDDIIIIIIIIIII
@rd.4::TGTTACCATCGACGTCCATTCAGTCCTAGAGT:7001113:948:HLFKYBCX2:1:1108:1119:1911
TGTTACCATCGACGTC
+TGTTACCATCGACGTCCATTCAGTCCTAGAGT:7001113:948:HLFKYBCX2:1:1108:1119:1911
DDD@DHHIIIIHIIHI
@rd.5::CTATTAGGCCTAAGACTGCTGGTATTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1028:1934
CTATTAGGCCTAAGAC
+CTATTAGGCCTAAGACTGCTGGTATTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1028:1934
CDCDDHIIIHIIIIGH
# from SRR8592624_4.fastq
@rd.1::AGACGGAGTGCAGCTATGTCAGCTACTGCATA:7001113:948:HLFKYBCX2:1:1108:1237:1880
TGTCAGCTACTGCATA
+AGACGGAGTGCAGCTATGTCAGCTACTGCATA:7001113:948:HLFKYBCX2:1:1108:1237:1880
IHFHIIFHGHFEEHHH
@rd.2::AGAGCAGTAACCAGGTACAAGGATAAGAGATG:7001113:948:HLFKYBCX2:1:1108:1027:1904
ACAAGGATAAGAGATG
+AGAGCAGTAACCAGGTACAAGGATAAGAGATG:7001113:948:HLFKYBCX2:1:1108:1027:1904
GHHHIIIEHHIIGHII
@rd.3::ACATTGGCGCTCATGAGGAAGACTTTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1056:1909
GGAAGACTTTCTAGCT
+ACATTGGCGCTCATGAGGAAGACTTTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1056:1909
IIIIIIIIIIIIIIII
@rd.4::TGTTACCATCGACGTCCATTCAGTCCTAGAGT:7001113:948:HLFKYBCX2:1:1108:1119:1911
CATTCAGTCCTAGAGT
+TGTTACCATCGACGTCCATTCAGTCCTAGAGT:7001113:948:HLFKYBCX2:1:1108:1119:1911
IIICHIIIHIHIHHIH
@rd.5::CTATTAGGCCTAAGACTGCTGGTATTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1028:1934
TGCTGGTATTCTAGCT
+CTATTAGGCCTAAGACTGCTGGTATTCTAGCT:7001113:948:HLFKYBCX2:1:1108:1028:1934
IIIIIIIIIEHHIIIH
Basically, SRR8592624_3.fastq
is I1
, and SRR8592624_4.fastq
is I2
. It seems the authors already trimmed off the linker sequences (27 bp in I1
and 21 bp in I2
) for us, so we could stitch them together simply by:
paste <(zcat snATAC-seq/data/SRR8592624_3.fastq.gz) \
<(zcat snATAC-seq/data/SRR8592624_4.fastq.gz) | \
awk -F '\t' '{ if(NR%4==1||NR%4==3) {print $1} else {print $1 $2} }' | \
gzip > snATAC-seq/data/SRR8592624_CB.fastq.gz
Then, we are ready to pass SRR8592624_1.fastq.gz
,1SRR8592624_2.fastq.gz
and SRR8592624_CB.fastq.gz
to chromap
.
Prepare Whitelist#
This is the most confusing part of the pipeline, but if you understand the procedures in the snATAC-seq GitHub page, you should be able to understand.
Important
The data we are using in this documentation is from the SnapATAC paper. The protocol is the same as the original snATAC-seq paper, but there are some minor modifications of the oligos. For the Tn5 barcode sequences, you need to check the Supplementary Table S4 from the SnapATAC paper. For the PCR primer (i5 and i7), you need to check the Supplementary Table S5 from the original snATAC-seq paper.
This is the Tn5-T7 oligo sequence: 5’- GTCTCGTGGGCTCGGCTGTCCCTGTCCNNNNNNNNCACCGTCTCCGCCTCAGATGTGTATAAGAGACAG -3’. The 8 Ns are the barcode, and the barcode sequences (from 5’ -> 3’) are:
Oligo |
Barcode T7 |
Reverse complement |
---|---|---|
T7_12 |
CCTAATAG |
CTATTAGG |
T7_13 |
CTGACATG |
CATGTCAG |
T7_14 |
TGTATGAG |
CTCATACA |
T7_15 |
GAAGATCT |
AGATCTTC |
T7_16 |
ACTGCTCT |
AGAGCAGT |
T7_17 |
CTCCGTCT |
AGACGGAG |
T7_18 |
GTTGCACA |
TGTGCAAC |
T7_19 |
GCCAATGT |
ACATTGGC |
T7_20 |
TGGTAACA |
TGTTACCA |
T7_21 |
CTTAGAGC |
GCTCTAAG |
T7_22 |
AACCGATA |
TATCGGTT |
T7_23 |
TGCAACTG |
CAGTTGCA |
This is the Tn5-T5 oligo sequence: 5’- TCGTCGGCAGCGTCTCCACGCNNNNNNNNGCGATCGAGGACGGCAGATGTGTATAAGAGACAG -3’. The 8 Ns are the barcode, and the barcode sequences (from 5’ -> 3’) are:
Oligo |
Barcode T5 |
Reverse complement |
---|---|---|
T5_9 |
TTCGGAAG |
CTTCCGAA |
T5_10 |
AGGCTGGT |
ACCAGCCT |
T5_11 |
GGAAGACT |
AGTCTTCC |
T5_12 |
GAACGCAT |
ATGCGTTC |
T5_13 |
TGCTGGTA |
TACCAGCA |
T5_14 |
CATTCAGT |
ACTGAATG |
T5_15 |
ACAAGGAT |
ATCCTTGT |
T5_16 |
TGTCAGCT |
AGCTGACA |
This is the i7 primer sequence: 5’- CAAGCAGAAGACGGCATACGAGATNNNNNNNNGTCTCGTGGGCTCGG -3’. The 8 Ns are the barcode, and the barcode sequences (from 5’ -> 3’) are:
Primer |
Barcode i7 |
Reverse complement |
---|---|---|
N701 |
TCGCCTTA |
TAAGGCGA |
N702 |
CTAGTACG |
CGTACTAG |
N703 |
TTCTGCCT |
AGGCAGAA |
N704 |
GCTCAGGA |
TCCTGAGC |
N705 |
AGGAGTCC |
GGACTCCT |
N706 |
CATGCCTA |
TAGGCATG |
N707 |
GTAGAGAG |
CTCTCTAC |
N710 |
CAGCCTCG |
CGAGGCTG |
N711 |
TGCCTCTT |
AAGAGGCA |
N712 |
TCCTCTAC |
GTAGAGGA |
N714 |
TCATGAGC |
GCTCATGA |
N715 |
CCTGAGAT |
ATCTCAGG |
N716 |
TAGCGAGT |
ACTCGCTA |
N718 |
GTAGCTCC |
GGAGCTAC |
N719 |
TACTACGC |
GCGTAGTA |
N721 |
GCAGCGTA |
TACGCTGC |
N722 |
CTGCGCAT |
ATGCGCAG |
N723 |
GAGCGCTA |
TAGCGCTC |
N724 |
CGCTCAGT |
ACTGAGCG |
N726 |
GTCTTAGG |
CCTAAGAC |
N727 |
ACTGATCG |
CGATCAGT |
N728 |
TAGCTGCA |
TGCAGCTA |
N729 |
GACGTCGA |
TCGACGTC |
X730 |
TACCAGAG |
CTCTGGTA |
X731 |
GGATGGAA |
TTCCATCC |
X732 |
ATTGAGGC |
GCCTCAAT |
X733 |
CGGATAGA |
TCTATCCG |
X734 |
TGGTAGAC |
GTCTACCA |
X735 |
ACCTGGTT |
AACCAGGT |
X736 |
CAGTTCTG |
CAGAACTG |
X737 |
TCGAACGT |
ACGTTCGA |
X738 |
CGTTGCTT |
AAGCAACG |
X739 |
TACCGTTC |
GAACGGTA |
X740 |
TAGGTTGC |
GCAACCTA |
X741 |
GAGGCTAA |
TTAGCCTC |
X742 |
CGACCATA |
TATGGTCG |
X743 |
AGGCAGTA |
TACTGCCT |
X744 |
ATCAAGCG |
CGCTTGAT |
X745 |
CATTGAAG |
CTTCAATG |
X746 |
CGACTTAT |
ATAAGTCG |
X747 |
TCTATACG |
CGTATAGA |
X748 |
AGCATTAG |
CTAATGCT |
X749 |
AATTGGCA |
TGCCAATT |
X750 |
AGATTCGT |
ACGAATCT |
X751 |
TTCATGAC |
GTCATGAA |
X752 |
TGAACTTG |
CAAGTTCA |
X753 |
ATGGCATA |
TATGCCAT |
X754 |
CGTAATTC |
GAATTACG |
This is the i5 primer sequence: 5’- AATGATACGGCGACCACCGAGATCTACACNNNNNNNNTCGTCGGCAGCGTC -3’. The 8 Ns are the i5 barcode, and the barcode sequences (from 5’ -> 3’) are:
Primer |
Barcode i5 |
Reverse complement |
---|---|---|
S502 |
CTCTCTAT |
ATAGAGAG |
S503 |
TATCCTCT |
AGAGGATA |
S505 |
GTAAGGAG |
CTCCTTAC |
S506 |
ACTGCATA |
TATGCAGT |
S507 |
AAGGAGTA |
TACTCCTT |
S508 |
CTAAGCCT |
AGGCTTAG |
S510 |
CGTCTAAT |
ATTAGACG |
S511 |
TCTCTCCG |
CGGAGAGA |
X512 |
TCGACTAG |
CTAGTCGA |
X513 |
TTCTAGCT |
AGCTAGAA |
X514 |
CCTAGAGT |
ACTCTAGG |
X515 |
GCGTAAGA |
TCTTACGC |
X516 |
AAGGCTAT |
ATAGCCTT |
X517 |
GAGCCTTA |
TAAGGCTC |
X518 |
TTATGCGA |
TCGCATAA |
X519 |
ATCTGAGT |
ACTCAGAT |
X520 |
GGATACTA |
TAGTATCC |
X521 |
TAAGATCC |
GGATCTTA |
X522 |
AAGAGATG |
CATCTCTT |
X523 |
AATGACGT |
ACGTCATT |
X524 |
GAAGTATG |
CATACTTC |
X525 |
ATAGCCTT |
AAGGCTAT |
X526 |
TTGGAAGT |
ACTTCCAA |
X527 |
ATTCGTTG |
CAACGAAT |
X528 |
AGGATAAC |
GTTATCCT |
X529 |
TTCATCCA |
TGGATGAA |
X530 |
AACGAACG |
CGTTCGTT |
X531 |
TGCCTTAC |
GTAAGGCA |
X532 |
CGAATTCC |
GGAATTCG |
X533 |
GGTTAGAC |
GTCTAACC |
X534 |
TCCGGTAA |
TTACCGGA |
X535 |
TTACGACC |
GGTCGTAA |
The 32 bp cell barcode consists of the four parts listed above. To construct full whitelist, we need to get all possible combinations of T7
, i7
, T5
and i5
. In total, we should have a whitelist of 8 * 12 * 32 * 48 = 147456 barcodes. However, the order of those four parts depends on Illumina machines. I have put those four tables into csv
files and you can download them to have a look:
snATAC-T5.csv
snATAC-T7.csv
snATAC-i5.csv
snATAC-i7.csv
Configuration 1#
Position |
Description |
---|---|
1 - 8 |
8 bp Tn5 barcode at the i7 side. This is the |
9 - 16 |
8 bp |
17 - 24 |
8 bp Tn5 barcode at the i5 side. This is the |
25 - 32 |
8 bp |
In this configuration, T7
and i7
are sequenced using the bottom strand as the template, and T5
and i5
are sequenced using the top strand as the template. Therefore, we should take the reverse complementary (rc) in the following order:
T7 rc
+ i7 rc
+ T5 rc
+ i5 rc
Now let’s generate the whitelist using the above order:
# download the tables
wget -P snATAC-seq/data/ \
https://teichlab.github.io/scg_lib_structs/data/snATAC-seq/snATAC-T5.csv \
https://teichlab.github.io/scg_lib_structs/data/snATAC-seq/snATAC-T7.csv \
https://teichlab.github.io/scg_lib_structs/data/snATAC-seq/snATAC-i5.csv \
https://teichlab.github.io/scg_lib_structs/data/snATAC-seq/snATAC-i7.csv
# generate combinations of them
for w in $(tail -n +2 snATAC-seq/data/snATAC-T7.csv | cut -f 3 -d,); do
for x in $(tail -n +2 snATAC-seq/data/snATAC-i7.csv | cut -f 3 -d,); do
for y in $(tail -n +2 snATAC-seq/data/snATAC-T5.csv | cut -f 3 -d,); do
for z in $(tail -n +2 snATAC-seq/data/snATAC-i5.csv | cut -f 3 -d,); do
echo "${w}${x}${y}${z}"
done
done
done
done > snATAC-seq/data/whitelist_config1.txt
Configuration 2#
Position |
Description |
---|---|
1 - 8 |
8 bp Tn5 barcode at the i7 side. This is the |
9 - 16 |
8 bp |
17 - 24 |
8 bp |
25 - 32 |
8 bp Tn5 barcode at the i5 side. This is the |
In this configuration, T7
and i7
are sequenced using the bottom strand as the template, and we should take the reverse complementary (rc) of them. The i5
and T5
are sequenced using the bottom strand as the template as well, and we should take the sequence as they are. Therefore, we should take the index sequence in the following order:
T7 rc
+ i7 rc
+ i5
+ T5
Now let’s generate the whitelist using the above order:
for w in $(tail -n +2 snATAC-seq/data/snATAC-T7.csv | cut -f 3 -d,); do
for x in $(tail -n +2 snATAC-seq/data/snATAC-i7.csv | cut -f 3 -d,); do
for y in $(tail -n +2 snATAC-seq/data/snATAC-i5.csv | cut -f 2 -d,); do
for z in $(tail -n +2 snATAC-seq/data/snATAC-T5.csv | cut -f 2 -d,); do
echo "${w}${x}${y}${z}"
done
done
done
done > snATAC-seq/data/whitelist_config2.txt
Normally, we are all set and ready to go now. However … see below …
Some Extra work#
According to the SnapATAC paper, the data was generated by HiSeq 2500. Therefore, we should use whitelist_config2.txt
as the whitelist. However, if you check carefully the content in SRR8592624_4.fastq.gz
, which should contain i5
+ T5
barcodes, it seems the order has been changed to T5
+ i5
. Maybe the authors changed this before the submission, but I’m not sure. Therefore, for the sake of analysing this specific data set, you need to generate a new whitelist in the order of:
T7 rc
+ i7 rc
+ T5
+ i5
To this end, we do:
for w in $(tail -n +2 snATAC-seq/data/snATAC-T7.csv | cut -f 3 -d,); do
for x in $(tail -n +2 snATAC-seq/data/snATAC-i7.csv | cut -f 3 -d,); do
for y in $(tail -n +2 snATAC-seq/data/snATAC-T5.csv | cut -f 2 -d,); do
for z in $(tail -n +2 snATAC-seq/data/snATAC-i5.csv | cut -f 2 -d,); do
echo "${w}${x}${y}${z}"
done
done
done
done > snATAC-seq/data/whitelist_config2_GSE126724.txt
Now we are ready to go.
From FastQ To Count Matrix#
Now we are ready to map the reads to the genome using chromap
:
mkdir -p snATAC-seq/chromap_outs
# map and generate the fragment file
chromap -t 4 --preset atac \
-x mm10/chromap_index/genome.index \
-r mm10/mm10.fa \
-1 snATAC-seq/data/SRR8592624_1.fastq.gz \
-2 snATAC-seq/data/SRR8592624_2.fastq.gz \
-b snATAC-seq/data/SRR8592624_CB.fastq.gz \
--barcode-whitelist snATAC-seq/data/whitelist_config2_GSE126724.txt \
--bc-error-threshold 2 \
-o snATAC-seq/chromap_outs/fragments.tsv
# compress and index the fragment file
bgzip snATAC-seq/chromap_outs/fragments.tsv
tabix -s 1 -b 2 -e 3 -p bed snATAC-seq/chromap_outs/fragments.tsv.gz
Two new files fragments.tsv.gz
and fragments.tsv.gz.tbi
are generated. They will be useful and sometimes required for other programs to perform downstream analysis.
Explain chromap#
If you understand the snATAC-seq experimental procedures described in this GitHub Page and the content in the previous sections, the command above should be straightforward to understand.
-t 4
Use 4 cores for the preprocessing. Change accordingly if using more or less cores.
-x mm10/chromap_index/genome.index
The
chromap
index. The data we are analysing is from mouse.
-r mm10/mm10.fa
Reference genome sequence in
fasta
format. This is basically the file which you used to create thechromap
index file.
-1
, -2
and -b
They are Read 1 (genomic), Read 2 (genomic) and cell barcode read, respectively. For ATAC-seq, the sequencing is usually done in pair-end mode. Therefore, you normally have two genomic reads for each genomic fragment: Read 1 and Read 2. Multiple input files are supported and they can be listed in a comma-separated manner. In that case, they must be in the same order.
--barcode-whitelist snATAC-seq/data/whitelist_config2_GSE126724.txt
For the reason described previously, we are using the file
whitelist_config2_GSE126724.txt
specifically generated for this data set. However, in your own case, it should be eitherwhitelist_config1.txt
orwhitelist_config2.txt
, depending on your sequencing machines.
--bc-error-threshold 2
Max Hamming distance allowed to correct a barcode. The default is 1. In this case, we have 32 bp cell barcodes, which is a bit long. Therefore, we would like to allow a bit more errors.
-o snATAC-seq/chromap_outs/fragments.tsv
Direct the mapped fragments to a file. The format is described in the 10x Genomics website.
Fragments To Reads#
The fragment file is the following format:
Column number |
Meaning |
---|---|
1 |
fragment chromosome |
2 |
fragment start |
3 |
fragment end |
4 |
cell barcode |
5 |
Number of read pairs of this fragment |
It is very useful, but we often need the peak-by-cell matrix for the downstream analysis. Therefore, we need to perform a peak calling process to identify open chromatin regions. We need to convert the fragment into reads. For each fragment, we will have two reads, a forward read and a reverse read. The read length is not important, but we could generate a 50-bp read pair for each fragment.
First, we need to create a genome size file, which is a tab delimited file with only two columns. The first column is the chromosome name and the second column is the length of the chromosome in bp:
# we also sort the output by chromosome name
# which will be useful later
faSize -detailed mm10/mm10.fa | \
sort -k1,1 > mm10/mm10.chrom.sizes
This is the first 5 lines of mm10/mm10.chrom.sizes
:
chr1 195471971
chr10 130694993
chr11 122082543
chr12 120129022
chr13 120421639
Now let’s generate the reads from fragments:
# we use bedClip to remove reads outside the chromosome boundary
# we also remove reads mapped to the mitochondrial genome (chrM)
zcat snATAC-seq/chromap_outs/fragments.tsv.gz | \
awk 'BEGIN{OFS="\t"}{print $1, $2, $2+50, $4, ".", "+" "\n" $1, $3-50, $3, $4, ".", "-"}' | \
sed '/chrM/d' | \
bedClip stdin mm10/mm10.chrom.sizes stdout | \
sort -k1,1 -k2,2n | \
gzip > snATAC-seq/chromap_outs/reads.bed.gz
Note we also sort the output reads by sort -k1,1 -k2,2n
. In this way, the order of chromosomes in the reads.bed.gz
is the same as that in mm10.chrom.sizes
, which makes downstream processes easier. The output reads.bed.gz
are the reads in bed
format, with the 4th column holding the cell barcodes.
Peak Calling By MACS2#
Now we can use the newly generated read file for the peak calling using MACS2
:
macs2 callpeak -t snATAC-seq/chromap_outs/reads.bed.gz \
-g mm -f BED -q 0.01 \
--nomodel --shift -100 --extsize 200 \
--keep-dup all \
-B --SPMR \
--outdir snATAC-seq/chromap_outs \
-n aggregate
Explain MACS2#
The reasons of choosing those specific parameters are a bit more complicated. I have dedicated a post for this a while ago. Please have a look at this post if you are still confused. The following output files are particularly useful:
File |
Description |
---|---|
aggregate_peaks.narrowPeak |
Open chromatin peak locations in the narrowPeak format |
aggregate_peaks.xls |
More information about peaks |
aggregate_treat_pileup.bdg |
Signal tracks. Can be used to generate the bigWig file for visualisation |
Getting The Peak-By-Cell Count Matrix#
Now that we have the peak and reads files, we can compute the number of reads in each peak for each cell. Then we could get the peak-by-cell count matrix. There are different ways of doing this. The following is the method I use.
Find Reads In Peaks Per Cell#
First, we use the aggregate_peaks.narrowPeak
file. We only need the first 4 columns (chromosome, start, end, peak ID). You can also remove the peaks that overlap the black list regions. The black list is not available for every species and every build, so I’m not doing it here. We also need to sort the peak to make sure the order of the chromosomes in the peak file is the same as that in the dm6.chrom.sizes
and reads.bed.gz
files. Then we could find the overlap by bedtools
. We need to do this in a specific way to get the number of reads in each peak from each cell:
# format and sort peaks
cut -f 1-4 snATAC-seq/chromap_outs/aggregate_peaks.narrowPeak | \
sort -k1,1 -k2,2n > snATAC-seq/chromap_outs/aggregate_peaks_sorted.bed
# prepare the overlap
bedtools intersect \
-a snATAC-seq/chromap_outs/aggregate_peaks_sorted.bed \
-b snATAC-seq/chromap_outs/reads.bed.gz \
-wo -sorted -g mm10/mm10.chrom.sizes | \
sort -k8,8 | \
bedtools groupby -g 8 -c 4 -o freqdesc | \
gzip > snATAC-seq/chromap_outs/peak_read_ov.tsv.gz
Explain Finding Reads In Peaks Per Cell#
We start with the command before the first pipe, that is, the intersection part. If you read the manual of the bedtools intersect
, it should be straightforward to understand. The -wo
option will output the records in both -a
file and -b
file. Since the reads.bed.gz
file has the cell barcode information at the 4th column, we would get an output with both peak and cell information for the overlap. The -sorted -g mm10/mm10.chrom.sizes
options make the program use very little memory. Here is an example (the first 5 lines) of the output of this part:
chr1 3094857 3095503 aggregate_peak_1 chr1 3094812 3094862 CTATTAGGTGCAGCTATGCTGGTACCTAGAGT . + 5
chr1 3094857 3095503 aggregate_peak_1 chr1 3094819 3094869 AGAGCAGTATCTCAGGACAAGGATTTATGCGA . + 12
chr1 3094857 3095503 aggregate_peak_1 chr1 3094821 3094871 CATGTCAGACTGAGCGACAAGGATGTAAGGAG . + 14
chr1 3094857 3095503 aggregate_peak_1 chr1 3094840 3094890 GCTCTAAGCGTATAGATGTCAGCTAATGACGT . + 33
chr1 3094857 3095503 aggregate_peak_1 chr1 3094848 3094898 ACATTGGCGCTCATGAGGAAGACTCCTAGAGT . + 41
We see that the 8th column holds the cell barcode and we want to group them using bedtools groupby
. Therefore, we need to sort by this column, that is the sort -k8,8
. When we group by the 8th column, we are interested in how many times each peak appear per group, so we could gather the information of the peak ID (4th column). That is the -g 8 -c 4 -o freqdesc
. The -o freqdesc
option returns a value:frequency
pair in descending order. Here are some records from peak_read_ov.tsv.gz
:
ACATTGGCAACCAGGTACAAGGATAAGAGATG aggregate_peak_146375:1
ACATTGGCAACCAGGTACAAGGATCTCTCTAT aggregate_peak_176781:2,aggregate_peak_44835:2,aggregate_peak_78312:2,aggregate_peak_87937:2
In a way, that is sort of a count matrix in an awkward format. For example:
The first line means that in cell
ACATTGGCAACCAGGTACAAGGATAAGAGATG
, the peakaggregate_peak_146375
has 1 count. All the rest peaks not mentioned here have 0 counts in this cell.The second line means that in cell
ACATTGGCAACCAGGTACAAGGATCTCTCTAT
, the peakaggregate_peak_176781
has 2 counts, the peakaggregate_peak_44835
has 2 counts, the peakaggregate_peak_78312
has two peaks and the peakaggregate_peak_87937
has 2 counts. All the rest peaks not mentioned here have 0 counts in this cell.
Output The Peak-By-Cell Matrix#
At this stage, we pretty much have all the things needed. Those two files aggregate_peaks_sorted.bed
and peak_read_ov.tsv.gz
contain all information for a peak-by-cell count matrix. We just need a final touch to make the output in a standard format: a market exchange format (MEX). Since most downstream software takes the input from the 10x Genomics Single Cell ATAC results, we are going to generate the MEX and the associated files similar to the output from 10x Genomics.
Here, I’m using a python script for this purpose. You don’t have to do this. Choose whatever works for you. The point here is to just generate similar files as the peak-barcode matrix described from the 10x Genomics website.
First, let’s make a directory to hold the output files and generate the peaks.bed
and barcodes.tsv
files, which are easy to do:
# create dirctory
mkdir -p snATAC-seq/chromap_outs/raw_peak_bc_matrix
# The 10x Genomics peaks.bed is a 3-column bed file, so we do
cut -f 1-3 snATAC-seq/chromap_outs/aggregate_peaks_sorted.bed > \
snATAC-seq/chromap_outs/raw_peak_bc_matrix/peaks.bed
# The barcode is basically the first column of the file peak_read_ov.tsv.gz
zcat snATAC-seq/chromap_outs/peak_read_ov.tsv.gz | \
cut -f 1 > \
snATAC-seq/chromap_outs/raw_peak_bc_matrix/barcodes.tsv
The slightly more difficult file to generate is matrix.mtx
. This is the python script generate_csc_mtx.py
for this purpose:
# import helper packages
# most entries in the count matrix is 0, so we are going to use a sparse matrix
# since we need to keep updating the sparse matrix, we use lil_matrix from scipy
import sys
import gzip
from scipy.io import mmwrite
from scipy.sparse import lil_matrix
# the unique peak ID is a good renference
# generate a dictionary with peak_id : index_in_the_file
# sys.argv[1] is the 4-column bed file aggregate_peaks_sorted.bed
peak_idx = {}
with open(sys.argv[1]) as fh:
for i, line in enumerate(fh):
_, _, _, peak_name = line.strip().split('\t')
peak_idx[peak_name] = i
# determine and create the dimension of the output matrix
# that is, to calculate the number of peaks and the number of barcodes
# sys.argv[2] is barcodes.tsv
num_peaks = len(peak_idx.keys())
num_cells = len(open(sys.argv[2]).readlines())
mtx = lil_matrix((num_peaks, num_cells), dtype = int)
# read the information from peak_read_ov.tsv.gz
# update the counts into the mtx object
# sys.argv[3] is peak_read_ov.tsv.gz
with gzip.open(sys.argv[3], 'rt') as fh:
for i, line in enumerate(fh):
col_idx = i # each column is a cell barcode
count_info = line.strip().split('\t')[1]
items = count_info.split(',')
for pn_count in items:
pn, count = pn_count.split(':')
row_idx = peak_idx[pn] # each row is a peak
mtx[row_idx, col_idx] = int(count)
# get a CSC sparse matrix, which is the same as the 10x Genomics matrix.mtx
mtx = mtx.tocsc()
# sys.argv[4] is the path to the output directory
mmwrite(sys.argv[4] + '/matrix.mtx', mtx, field='integer')
Run that script in the terminal:
python generate_csc_mtx.py \
snATAC-seq/chromap_outs/aggregate_peaks_sorted.bed \
snATAC-seq/chromap_outs/raw_peak_bc_matrix/barcodes.tsv \
snATAC-seq/chromap_outs/peak_read_ov.tsv.gz \
snATAC-seq/chromap_outs/raw_peak_bc_matrix
After that, you should have the matrix.mtx
in the snATAC-seq/chromap_outs/raw_peak_bc_matrix
directory.
Cell Calling (Filter Cell Barcodes)#
Experiments are never perfect. Even for barcodes that do not capture the molecules inside the cells, you may still get some reads due to various reasons, such as ambient RNA or DNA and leakage. In general, the number of reads from those barcodes should be much smaller, often orders of magnitude smaller, than those barcodes from real cells. In order to identify true cells from the background, we could use starolo
. It is used for scRNA-seq in general, but it does have a cell calling function that takes a directory containing raw mtx and associated files, and return the filtered ones. Since starsolo
looks for the following three files in the input directory: matrix.mtx
, features.tsv
and barcodes.tsv
. Those are the output from the 10x Genomics scRNA-seq workflow. In this case, we can use peaks.bed
as our features.tsv
:
# trick starsolo to use peaks.bed as features.tsv by creating symlink
ln -s peaks.bed snATAC-seq/chromap_outs/raw_peak_bc_matrix/features.tsv
# filter cells using starsolo
STAR --runMode soloCellFiltering \
snATAC-seq/chromap_outs/raw_peak_bc_matrix \
snATAC-seq/chromap_outs/filtered_peak_bc_matrix/ \
--soloCellFilter EmptyDrops_CR
# rename the new feature.tsv to peaks.bed or just create symlink
ln -s features.tsv snATAC-seq/chromap_outs/filtered_peak_bc_matrix/peaks.bed
If everything goes well, your directory should look the same as the following:
scg_prep_test/snATAC-seq/
├── chromap_outs
│ ├── aggregate_control_lambda.bdg
│ ├── aggregate_peaks.narrowPeak
│ ├── aggregate_peaks_sorted.bed
│ ├── aggregate_peaks.xls
│ ├── aggregate_summits.bed
│ ├── aggregate_treat_pileup.bdg
│ ├── filtered_peak_bc_matrix
│ │ ├── barcodes.tsv
│ │ ├── features.tsv
│ │ ├── matrix.mtx
│ │ └── peaks.bed -> features.tsv
│ ├── fragments.tsv.gz
│ ├── fragments.tsv.gz.tbi
│ ├── peak_read_ov.tsv.gz
│ ├── raw_peak_bc_matrix
│ │ ├── barcodes.tsv
│ │ ├── features.tsv -> peaks.bed
│ │ ├── matrix.mtx
│ │ └── peaks.bed
│ └── reads.bed.gz
└── data
├── snATAC-i5.csv
├── snATAC-i7.csv
├── snATAC-T5.csv
├── snATAC-T7.csv
├── SRR8592624_1.fastq.gz
├── SRR8592624_2.fastq.gz
├── SRR8592624_3.fastq.gz
├── SRR8592624_4.fastq.gz
├── SRR8592624_CB.fastq.gz
├── whitelist_config1.txt
├── whitelist_config2_GSE126724.txt
└── whitelist_config2.txt
4 directories, 30 files