Introduction

Polyester is an R package designed to simulate an RNA sequencing experiment. Given a set of annotated transcripts, polyester will simulate the steps of an RNA-seq experiment (fragmentation, reverse-complementing, and sequencing) and produce files containing simulated RNA-seq reads. Simulated reads can be analyzed using any of several downstream analysis tools.

In particular, Polyester was designed to simulate a case/control experiment with biological replicates. Users are able to set differential transcript expression between cases and controls. This allows users to create datasets with known differential expression, which means they can the accuracy of statistical methods for differential expression detection.

Polyester was developed with several specific features in mind:

Installation

Start R and run:

source("http://bioconductor.org/biocLite.R")
biocLite("polyester")

Polyester depends on the Biostrings and IRanges libraries from Bioconductor.

Required Input

You will need either:

Simulating reads

Simulating an RNA-seq experiment with Polyester requires just one function call. You can choose either simulate_experiment() or simulate_experiment_countmat().

examples

A FASTA file called chr22.fa is provided with polyester. This file contains sequences for 918 transcripts on chromosome 22, as annotated in hg19. For this example, we will only simulate from the first 20 of these transcripts, and we will set the first 2 transcripts to be overexpressed in group A and the next 2 transcripts to be overexpressed in group B, each at a fold change of 4. A small experiment like this will only take a few seconds to run, even with many reps. A larger experiment (say, with all 918 transcripts) will run also run in a reasonable amount of time (minutes, not hours), with the exact timing depending on number of reads generated and number of reps.

To simulate a two-group experiment with 10 biological replicates in each group where the first 3 transcripts are differentially expressed with a fold change of 4, you can use code like this:

library(polyester)
library(Biostrings)

fasta_file = system.file('extdata', 'chr22.fa', package='polyester')
fasta = readDNAStringSet(fasta_file)
small_fasta = fasta[1:20]
writeXStringSet(small_fasta, 'chr22_small.fa')
fold_changes = c(4, 4, 1/4, 1/4, rep(1, 16))
outdir = 'simulated_reads'

# ~20x coverage ----> reads per transcript = length/readlength * 20
# "width" is operating on a DNAStringSet (from Biostrings)
readspertx = round(20 * width(small_fasta) / 100)
simulate_experiment('chr22_small.fa', reads_per_transcript=readspertx, 
    num_reps=10, fold_changes=fold_changes, outdir=outdir) 

The simulate_experiment function draws the number of reads to simulate from each transcript from a negative binomial model. See below for details. Depending on your use case, it may be important to account for transcript length when deciding on the baseline mean number of reads to simulate from that transcript (as we did above with readspertx).

For more flexibility, you can use the simulate_experiment_countmat function. For example, we may want to simulate timecourse data. To do this, we can explicitly specify the number of reads for each transcript (rows), at each timepoint (columns). We will again only simulate from 20 transcripts.

# set up matrix:
num_timepoints = 12
countmat = matrix(readspertx, nrow=length(small_fasta), ncol=num_timepoints)

# add spikes in expression at certain timepoints to certain transcripts:
up_early = c(1,2) 
up_late = c(3,4)
countmat[up_early, 2] = 3*countmat[up_early, 2]
countmat[up_early, 3] = round(1.5*countmat[up_early, 3])
countmat[up_late, 10] = 6*countmat[up_late, 10]
countmat[up_late, 11] = round(1.2*countmat[up_late, 11])

# simulate reads:
simulate_experiment_countmat('chr22_small.fa', readmat=countmat, 
    outdir='timecourse_reads') 

In this scenario, we simulated 12 total timepoints. We also added differential expression: transcripts 1 and 2 are overexpressed (compared to baseline) at timepoints 2 and 3, with a fold change of 3 at timepoint 2 and a fold change of 1.5 at timepoint 3. Similarly, transcripts 3 and 4 are overexpressed at timepoints 10 and 11, with a fold change of 6 at timepoint 10 and a fold change of 1.2 at timepoint 11.

More on the negative binomial model

The simulate_experiment function draws the number of reads to simulate from each transcript from a negative binomial distribution. For this function, you need to specify:

More on the count-matrix model

The simulate_experiment_readmat function takes a count matrix as an argunent. Each row of this matrix represents a transcript, and each column represents a sample in the experiment. Entry i,j of the matrix specifies how many reads should be sampled from transcript i for sample j, allowing you to precisely and flexibly define the (differential) transcript expression structure for the experiment.

other simulation parameters that can be set:

For both simulate_experiment and simulate_experiment_countmat, you can change these parameters:

This review paper (Oshlack, Robinson, and Young, Genome Biology 2010, open access) provides a good overview of the RNA sequencing process, and might be particularly useful for understanding where some of these simulation parameters come into play.

If you'd like to explore specific steps in the sequencing process (fragmentation, reverse-complementing, error-adding), the functions called within simulate_experiment are also available and individually documented in Polyester.

See ?simulate_experiment and ?simulate_experiment_countmat for details on how to change these parameters.

Using real data to guide simulation

To create a count matrix that resembles a real dataset, use the create_read_numbers function. To run this example, you will need to install the Ballgown package from Bioconductor if you do not already have it:

source("http://bioconductor.org/biocLite.R")
biocLite("ballgown")
library(ballgown)
data(bg)
countmat = fpkm_to_counts(bg, threshold=0.01, mean_rps=400000)
params = get_params(countmat)
Ntranscripts = 50
Nsamples = 10
custom_readmat = create_read_numbers(mu=params$mu, fit=params$fit,
    p0=params$p0, m=Ntranscripts, n=Nsamples, seed=103)
## Generating data from baseline model.

The Ballgown package here is optional: the mean/variance relationship for each transcript can be estimated from any matrix of counts using get_params. You can add differential expression to the output from create_read_numbers (here, custom_readmat) and pass the resulting matrix to simulate_experiment_countmat.

error models

Sequencing error is part of the read generation process, so Polyester provides three options for simulating this sequencing error:

option 1: uniform error model

Simulate a uniform error model (equal probability of making each sequencing error at each base) by speficying a single parameter, error_rate, in your call to simulate_experiment or simulate_experiment_countmat.

option 2: built-in empirical error model

Simulate sequencing error based on one of three empirical error models published with GemSIM (paper, software). The GemSIM software ships with three different empirical error models: one from Illumina Genome Analyzer IIx with Illumina Sequencing Kit v4 chemistry (illumina4), Illumina Genome Analyzer IIx with TrueSeq SBS Kit v5-GA (illumina5), and Roche/454 FLX Titanium (roche454). Specify, e.g., error_model = 'illumina4 in your call to simulate_experiment or simulate_experiment_countmat to use an empirical error model. The error rate for each nucleotide in a sequencing read depends on the following:

In addition, separate error probabilities are estimated for each of the 4 possible sequencing errors (the 3 incorrect nucleotides + 'N'). Details on the GemSIM error model are available in the GemSIM paper, and details/code we used to create the error models that ship with Polyester are available at our GitHub repository.

option 3: use your own empirical error model

If you would like to simulate reads with a sequencing error model derived from a set of aligned reads, you can do so by running the GemErr.py program from GemSIM. The software is available from Sourceforge and includes a Manual with detailed instructions. You can provide a list of known SNP locations when estimating sequencing error probabilities.

You will then need to run our script, [build_error_models.py](https://github.com/alyssafrazee/polyester/blob/master/build_error_models.py), using the output directory from GemErr.py as the model_path argument and specifying --prefix mymodelname, where the output file from GemErr.py is either mymodelname_p.gzip (paired-end model) or mymodelname_s.gzip (single-end model). If your error model is paired end, you will also need to specify --paired. So your full command could look like:

python build_error_model.py /path/to/gemerr_output /path/to/custom_output --prefix mymodelname --paired

When using the custom error model in the Polyester R package, you will need to provide /path/to/custom_output and whatever was given as the --prefix argument, in your call to simulate_experiment or simulate_experiment_countmat. You will also need to specify error_model = 'custom'.

Output

A call to simulate_experiment or simulate_experiment_countmat will write FASTA files to the directory specified by the outdir argument. Reads in the FASTA file will be labeled with the transcript from which they were simulated.

If paired is true, you'll get two FASTA files per biological replicate (left mates are designated by the suffix _1.fasta; right mates by _2.fasta). If single-end reads are generated (paired=FALSE) you'll get one FASTA file per replicate.

Files will be named sample_01 through sample_N where N is the total number of replicates. The first num_reps (or num_reps[1]) samples belong to the same group in the two-group experiment scenario.

In simulate_experiment, by default, a table called sim_info.txt is written to outdir, which will contain transcript IDs, fold changes, and whether or not that transcript was set to be differentially expressed. This file could be useful for downstream analysis. If the transcript names in the FASTA file cause problems down the line (e.g., a dangling single quote from a 5'-end label), you can specify your own transcript names with the transcriptid argument. You will need to keep track of this information separately if you use simulate_experiment_countmat.

Bug reports

Report bugs as issues on our GitHub repository.

Session Information

sessionInfo()
## R version 3.3.0 (2016-05-03)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 14.04.4 LTS
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=C              
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats4    parallel  stats     graphics  grDevices utils     datasets 
## [8] methods   base     
## 
## other attached packages:
## [1] ballgown_2.4.2      Biostrings_2.40.1   XVector_0.12.0     
## [4] IRanges_2.6.0       S4Vectors_0.10.1    BiocGenerics_0.18.0
## [7] polyester_1.8.3    
## 
## loaded via a namespace (and not attached):
##  [1] AnnotationDbi_1.34.3       knitr_1.13                
##  [3] magrittr_1.5               GenomicRanges_1.24.1      
##  [5] splines_3.3.0              zlibbioc_1.18.0           
##  [7] GenomicAlignments_1.8.1    BiocParallel_1.6.2        
##  [9] xtable_1.8-2               lattice_0.20-33           
## [11] stringr_1.0.0              GenomeInfoDb_1.8.2        
## [13] tools_3.3.0                grid_3.3.0                
## [15] SummarizedExperiment_1.2.2 nlme_3.1-128              
## [17] Biobase_2.32.0             mgcv_1.8-12               
## [19] DBI_0.4-1                  genefilter_1.54.2         
## [21] survival_2.39-4            Matrix_1.2-6              
## [23] sva_3.20.0                 rtracklayer_1.32.0        
## [25] RColorBrewer_1.1-2         formatR_1.4               
## [27] bitops_1.0-6               RCurl_1.95-4.8            
## [29] RSQLite_1.0.0              evaluate_0.9              
## [31] limma_3.28.5               stringi_1.1.1             
## [33] Rsamtools_1.24.0           XML_3.98-1.4              
## [35] annotate_1.50.0