| 
6
 | 
     1 #! /usr/bin/env python
 | 
| 
 | 
     2 #
 | 
| 
 | 
     3 # Copyright INRA-URGI 2009-2010
 | 
| 
 | 
     4 # 
 | 
| 
 | 
     5 # This software is governed by the CeCILL license under French law and
 | 
| 
 | 
     6 # abiding by the rules of distribution of free software. You can use,
 | 
| 
 | 
     7 # modify and/ or redistribute the software under the terms of the CeCILL
 | 
| 
 | 
     8 # license as circulated by CEA, CNRS and INRIA at the following URL
 | 
| 
 | 
     9 # "http://www.cecill.info".
 | 
| 
 | 
    10 # 
 | 
| 
 | 
    11 # As a counterpart to the access to the source code and rights to copy,
 | 
| 
 | 
    12 # modify and redistribute granted by the license, users are provided only
 | 
| 
 | 
    13 # with a limited warranty and the software's author, the holder of the
 | 
| 
 | 
    14 # economic rights, and the successive licensors have only limited
 | 
| 
 | 
    15 # liability.
 | 
| 
 | 
    16 # 
 | 
| 
 | 
    17 # In this respect, the user's attention is drawn to the risks associated
 | 
| 
 | 
    18 # with loading, using, modifying and/or developing or reproducing the
 | 
| 
 | 
    19 # software by the user in light of its specific status of free software,
 | 
| 
 | 
    20 # that may mean that it is complicated to manipulate, and that also
 | 
| 
 | 
    21 # therefore means that it is reserved for developers and experienced
 | 
| 
 | 
    22 # professionals having in-depth computer knowledge. Users are therefore
 | 
| 
 | 
    23 # encouraged to load and test the software's suitability as regards their
 | 
| 
 | 
    24 # requirements in conditions enabling the security of their systems and/or
 | 
| 
 | 
    25 # data to be ensured and, more generally, to use and operate it in the
 | 
| 
 | 
    26 # same conditions as regards security.
 | 
| 
 | 
    27 # 
 | 
| 
 | 
    28 # The fact that you are presently reading this means that you have had
 | 
| 
 | 
    29 # knowledge of the CeCILL license and that you accept its terms.
 | 
| 
 | 
    30 #
 | 
| 
 | 
    31 """Find random regions in a genome"""
 | 
| 
 | 
    32 
 | 
| 
 | 
    33 import random, math
 | 
| 
 | 
    34 from optparse import OptionParser
 | 
| 
 | 
    35 from commons.core.parsing.FastaParser import *
 | 
| 
 | 
    36 from commons.core.writer.Gff3Writer import *
 | 
| 
 | 
    37 from commons.core.writer.MySqlTranscriptWriter import *
 | 
| 
 | 
    38 from SMART.Java.Python.misc.Progress import *
 | 
| 
 | 
    39 from SMART.Java.Python.structure.Transcript import Transcript
 | 
| 
 | 
    40 from SMART.Java.Python.structure.TranscriptContainer import TranscriptContainer
 | 
| 
 | 
    41 
 | 
| 
 | 
    42 repetitions = 100
 | 
| 
 | 
    43 
 | 
| 
 | 
    44 
 | 
| 
 | 
    45 class RandomRegionsGenerator(object):
 | 
| 
 | 
    46 
 | 
| 
46
 | 
    47 	def __init__(self, verbosity):
 | 
| 
 | 
    48 		self.verbosity      = verbosity
 | 
| 
 | 
    49 		self.strands        = False
 | 
| 
 | 
    50 		self.distribution   = "uniform"
 | 
| 
 | 
    51 		self.transcripts    = None
 | 
| 
 | 
    52 		self.sequenceParser = None
 | 
| 
 | 
    53 		random.seed()
 | 
| 
6
 | 
    54 
 | 
| 
 | 
    55 
 | 
| 
46
 | 
    56 	def setInput(self, fileName):
 | 
| 
 | 
    57 		self.sequenceParser = FastaParser(fileName, self.verbosity)
 | 
| 
6
 | 
    58 
 | 
| 
 | 
    59 
 | 
| 
46
 | 
    60 	def setGenomeSize(self, size):
 | 
| 
 | 
    61 		self.genomeSize = size
 | 
| 
6
 | 
    62 
 | 
| 
 | 
    63 
 | 
| 
46
 | 
    64 	def setChromosomeName(self, name):
 | 
| 
 | 
    65 		self.chromosomeName = name
 | 
| 
6
 | 
    66 
 | 
| 
 | 
    67 
 | 
| 
46
 | 
    68 	def setAnnotation(self, fileName, format):
 | 
| 
 | 
    69 		parser           = TranscriptContainer(fileName, format, self.verbosity)
 | 
| 
 | 
    70 		self.transcripts = []
 | 
| 
 | 
    71 		for transcript in parser.getIterator():
 | 
| 
 | 
    72 			self.transcripts.append(transcript)
 | 
| 
 | 
    73 		self.setNumber(len(self.transcripts))
 | 
| 
 | 
    74 		self.setSize(0)
 | 
| 
6
 | 
    75 
 | 
| 
 | 
    76 
 | 
| 
46
 | 
    77 	def setOutputFile(self, fileName):
 | 
| 
 | 
    78 		self.outputFileName = fileName
 | 
| 
6
 | 
    79 
 | 
| 
 | 
    80 
 | 
| 
46
 | 
    81 	def setSize(self, size):
 | 
| 
 | 
    82 		self.minSize = size
 | 
| 
 | 
    83 		self.maxSize = size
 | 
| 
6
 | 
    84 
 | 
| 
 | 
    85 
 | 
| 
46
 | 
    86 	def setMinSize(self, size):
 | 
| 
 | 
    87 		self.minSize = size
 | 
| 
6
 | 
    88 
 | 
| 
 | 
    89 
 | 
| 
46
 | 
    90 	def setMaxSize(self, size):
 | 
| 
 | 
    91 		self.maxSize = size
 | 
| 
6
 | 
    92 
 | 
| 
 | 
    93 
 | 
| 
46
 | 
    94 	def setNumber(self, number):
 | 
| 
 | 
    95 		self.number = number
 | 
| 
6
 | 
    96 
 | 
| 
 | 
    97 
 | 
| 
46
 | 
    98 	def setStrands(self, strands):
 | 
| 
 | 
    99 		self.strands = strands
 | 
| 
6
 | 
   100 
 | 
| 
 | 
   101 
 | 
| 
46
 | 
   102 	def setMaxDistribution(self, maxElements):
 | 
| 
 | 
   103 		if maxElements == None:
 | 
| 
 | 
   104 			return
 | 
| 
 | 
   105 		self.maxElements = maxElements
 | 
| 
 | 
   106 		self.distribution = "gaussian"
 | 
| 
6
 | 
   107 
 | 
| 
 | 
   108 
 | 
| 
46
 | 
   109 	def setDeviationDistribution(self, deviation):
 | 
| 
 | 
   110 		if deviation == None:
 | 
| 
 | 
   111 			return
 | 
| 
 | 
   112 		self.deviation = deviation
 | 
| 
 | 
   113 		self.distribution = "gaussian"
 | 
| 
6
 | 
   114 
 | 
| 
 | 
   115 
 | 
| 
46
 | 
   116 	def getSizes(self):
 | 
| 
 | 
   117 		if self.sequenceParser == None:
 | 
| 
 | 
   118 			self.chromosomes    = [self.chromosomeName]
 | 
| 
 | 
   119 			self.sizes          = {self.chromosomeName: self.genomeSize}
 | 
| 
 | 
   120 			self.cumulatedSize  = self.genomeSize
 | 
| 
 | 
   121 			self.cumulatedSizes = {self.chromosomeName: self.genomeSize}
 | 
| 
 | 
   122 			return
 | 
| 
 | 
   123 		self.chromosomes    = self.sequenceParser.getRegions()
 | 
| 
 | 
   124 		self.sizes          = {}
 | 
| 
 | 
   125 		self.cumulatedSize  = 0
 | 
| 
 | 
   126 		self.cumulatedSizes = {}
 | 
| 
 | 
   127 		for chromosome in self.chromosomes:
 | 
| 
 | 
   128 			self.sizes[chromosome]          = self.sequenceParser.getSizeOfRegion(chromosome)
 | 
| 
 | 
   129 			self.cumulatedSize             += self.sizes[chromosome]
 | 
| 
 | 
   130 			self.cumulatedSizes[chromosome] = self.cumulatedSize
 | 
| 
6
 | 
   131 
 | 
| 
 | 
   132 
 | 
| 
46
 | 
   133 	def findPosition(self, size = None):
 | 
| 
 | 
   134 		if size == None:
 | 
| 
 | 
   135 			size = random.randint(self.minSize, self.maxSize)
 | 
| 
 | 
   136 		integer = random.randint(0, self.cumulatedSize)
 | 
| 
 | 
   137 		for chromosome in self.chromosomes:
 | 
| 
 | 
   138 			if self.cumulatedSizes[chromosome] > integer:
 | 
| 
 | 
   139 				break
 | 
| 
 | 
   140 		start = random.randint(1, self.sizes[chromosome] - size)
 | 
| 
 | 
   141 		return (chromosome, start, size)
 | 
| 
6
 | 
   142 
 | 
| 
 | 
   143 
 | 
| 
46
 | 
   144 	def createTranscript(self, chromosome, start, size, strand, cpt):
 | 
| 
 | 
   145 		transcript = Transcript()
 | 
| 
 | 
   146 		transcript.setChromosome(chromosome)
 | 
| 
 | 
   147 		transcript.setEnd(start + size-1)
 | 
| 
 | 
   148 		transcript.setStart(start)
 | 
| 
 | 
   149 		transcript.setDirection(strand)
 | 
| 
 | 
   150 		transcript.setName("rand_%d" % (cpt))
 | 
| 
 | 
   151 		return transcript
 | 
| 
6
 | 
   152 
 | 
| 
 | 
   153 
 | 
| 
46
 | 
   154 	def moveTranscript(self, chromosome, start, transcript):
 | 
| 
 | 
   155 		while transcript.getEnd() + start - transcript.getStart() > self.cumulatedSizes[chromosome]:
 | 
| 
 | 
   156 			chromosome, start, size = self.findPosition(transcript.getEnd() - transcript.getStart())
 | 
| 
 | 
   157 		newTranscript = Transcript()
 | 
| 
 | 
   158 		newTranscript.setChromosome(chromosome)
 | 
| 
 | 
   159 		newTranscript.tags = transcript.tags
 | 
| 
 | 
   160 		if transcript.getNbExons() > 1:
 | 
| 
 | 
   161 			for exon in transcript.getNbExons():
 | 
| 
 | 
   162 				newExon = Interval()
 | 
| 
 | 
   163 				newExon.setChromosome(chromosome)
 | 
| 
 | 
   164 				newExon.setEnd(exon.getEnd() + start - transcript.getStart())
 | 
| 
 | 
   165 				newExon.setStart(exon.getStart() + start - transcript.getStart())
 | 
| 
 | 
   166 				newTranscript.addExon(newExon)
 | 
| 
 | 
   167 		newTranscript.setEnd(transcript.getEnd() + start - transcript.getStart())
 | 
| 
 | 
   168 		newTranscript.setStart(start)
 | 
| 
 | 
   169 		newTranscript.setDirection(transcript.getDirection())
 | 
| 
 | 
   170 		return [newTranscript]
 | 
| 
6
 | 
   171 
 | 
| 
 | 
   172 
 | 
| 
46
 | 
   173 	def createUniformCluster(self, chromosome, start, size, strand, cpt):
 | 
| 
 | 
   174 		transcript = self.createTranscript(chromosome, start, size, strand, cpt)
 | 
| 
 | 
   175 		return [transcript]
 | 
| 
6
 | 
   176 
 | 
| 
 | 
   177 
 | 
| 
46
 | 
   178 	def findNbTranscripts(self, cpt):
 | 
| 
 | 
   179 		return min(int(round(math.exp(random.random() * math.log(self.maxElements)))), self.number - cpt + 1)
 | 
| 
6
 | 
   180 
 | 
| 
 | 
   181 
 | 
| 
46
 | 
   182 	def getDev(self):
 | 
| 
 | 
   183 		deviation = 0.0
 | 
| 
 | 
   184 		for j in range(repetitions):
 | 
| 
 | 
   185 			deviation += random.randint(-self.deviation, self.deviation)
 | 
| 
 | 
   186 		deviation /= repetitions
 | 
| 
 | 
   187 		deviation  = int(round(deviation))
 | 
| 
 | 
   188 		return deviation
 | 
| 
 | 
   189 
 | 
| 
 | 
   190 
 | 
| 
 | 
   191 	def createGaussianCluster(self, chromosome, start, size, strand, cpt):
 | 
| 
 | 
   192 		transcripts   = []
 | 
| 
 | 
   193 		nbTranscripts = self.findNbTranscripts(cpt)
 | 
| 
 | 
   194 		for i in range(nbTranscripts):
 | 
| 
 | 
   195 			transcript = self.createTranscript(chromosome, start + self.getDev(), size + self.getDev(), strand, cpt + i)
 | 
| 
 | 
   196 			transcripts.append(transcript)
 | 
| 
 | 
   197 		return transcripts
 | 
| 
6
 | 
   198 
 | 
| 
 | 
   199 
 | 
| 
46
 | 
   200 	def writeRegions(self):
 | 
| 
 | 
   201 		writer     = Gff3Writer(self.outputFileName, self.verbosity)
 | 
| 
 | 
   202 		outputFile = open(self.outputFileName, "w")
 | 
| 
 | 
   203 		progress   = Progress(self.number, "Writing to %s" % (self.outputFileName), self.verbosity)
 | 
| 
 | 
   204 		i          = 0
 | 
| 
 | 
   205 		while i < self.number:
 | 
| 
 | 
   206 			chromosome, start, size = self.findPosition()
 | 
| 
 | 
   207 			strand                  = random.choice([-1, 1]) if self.strands else 1
 | 
| 
 | 
   208 			if self.transcripts != None:
 | 
| 
 | 
   209 				transcripts = self.moveTranscript(chromosome, start, self.transcripts[i])
 | 
| 
 | 
   210 			elif self.distribution == "uniform":
 | 
| 
 | 
   211 				transcripts = self.createUniformCluster(chromosome, start, size, strand, i+1)
 | 
| 
 | 
   212 			else:
 | 
| 
 | 
   213 				transcripts = self.createGaussianCluster(chromosome, start, size, strand, i+1)
 | 
| 
 | 
   214 			for transcript in transcripts:
 | 
| 
 | 
   215 				writer.addTranscript(transcript)
 | 
| 
 | 
   216 				i += 1
 | 
| 
 | 
   217 				progress.inc()
 | 
| 
 | 
   218 		progress.done()
 | 
| 
 | 
   219 		outputFile.close()
 | 
| 
 | 
   220 		writer.write()
 | 
| 
 | 
   221 		writer.close()
 | 
| 
6
 | 
   222 
 | 
| 
 | 
   223 
 | 
| 
46
 | 
   224 	def run(self):
 | 
| 
 | 
   225 		self.getSizes()
 | 
| 
 | 
   226 		self.writeRegions()
 | 
| 
6
 | 
   227 
 | 
| 
 | 
   228 
 | 
| 
 | 
   229 if __name__ == "__main__":
 | 
| 
46
 | 
   230 	
 | 
| 
 | 
   231 	# parse command line
 | 
| 
 | 
   232 	description = "Get Random Regions v1.0.2: Get some random coordinates on a genome. May use uniform or gaussian distribution (in gaussion distribution, # of element per cluster follows a power law). [Category: Other]"
 | 
| 
6
 | 
   233 
 | 
| 
46
 | 
   234 	parser = OptionParser(description = description)
 | 
| 
 | 
   235 	parser.add_option("-r", "--reference",     dest="reference",      action="store",      default=None,  type="string", help="file that contains the sequences [format: file in FASTA format]")
 | 
| 
 | 
   236 	parser.add_option("-S", "--referenceSize", dest="referenceSize",  action="store",      default=None,  type="int",    help="size of the chromosome (when no reference is given) [format: int]")
 | 
| 
 | 
   237 	parser.add_option("-c", "--chromosome",    dest="chromosome",     action="store",      default=None,  type="string", help="name of the chromosome (when no reference is given) [format: string]")
 | 
| 
 | 
   238 	parser.add_option("-o", "--output",        dest="outputFileName", action="store",                     type="string", help="output file [compulsory] [format: output file in FASTA format]")
 | 
| 
 | 
   239 	parser.add_option("-i", "--input",         dest="inputFileName",  action="store",      default=None,  type="string", help="optional file containing regions to shuffle [format: file in transcript format given by -f]")
 | 
| 
 | 
   240 	parser.add_option("-f", "--format",        dest="format",         action="store",      default=None,  type="string", help="format of the previous file [format: transcript file format]")
 | 
| 
 | 
   241 	parser.add_option("-s", "--size",          dest="size",           action="store",      default=None,  type="int",    help="size of the regions (if no region set is provided) [format: int]")
 | 
| 
 | 
   242 	parser.add_option("-z", "--minSize",       dest="minSize",        action="store",      default=None,  type="int",    help="minimum size of the regions (if no region set nor a fixed size are provided) [format: int]")
 | 
| 
 | 
   243 	parser.add_option("-Z", "--maxSize",       dest="maxSize",        action="store",      default=None,  type="int",    help="maximum size of the regions (if no region set nor a fixed size are provided) [format: int]")
 | 
| 
 | 
   244 	parser.add_option("-n", "--number",        dest="number",         action="store",      default=None,  type="int",    help="number of regions (if no region set is provided) [format: int]")
 | 
| 
 | 
   245 	parser.add_option("-t", "--strands",       dest="strands",        action="store_true", default=False,                help="use both strands (if no region set is provided) [format: boolean]")
 | 
| 
 | 
   246 	parser.add_option("-m", "--max",           dest="max",            action="store",      default=None,  type="int",    help="max. # reads in a cluster (for Gaussian dist.) [format: int]")
 | 
| 
 | 
   247 	parser.add_option("-d", "--deviation",     dest="deviation",      action="store",      default=None,  type="int",    help="deviation around the center of the cluster (for Gaussian dist.) [format: int]")
 | 
| 
 | 
   248 	parser.add_option("-v", "--verbosity",     dest="verbosity",      action="store",      default=1,     type="int",    help="trace level [format: int]")
 | 
| 
 | 
   249 	(options, args) = parser.parse_args()
 | 
| 
6
 | 
   250 
 | 
| 
46
 | 
   251 	rrg = RandomRegionsGenerator(options.verbosity)
 | 
| 
 | 
   252 	if options.reference == None:
 | 
| 
 | 
   253 		rrg.setGenomeSize(options.referenceSize)
 | 
| 
 | 
   254 		rrg.setChromosomeName(options.chromosome)
 | 
| 
 | 
   255 	else:
 | 
| 
 | 
   256 		rrg.setInput(options.reference)
 | 
| 
 | 
   257 	rrg.setOutputFile(options.outputFileName)
 | 
| 
 | 
   258 	if options.inputFileName == None:
 | 
| 
 | 
   259 		if options.size != None:
 | 
| 
 | 
   260 			rrg.setSize(options.size)
 | 
| 
 | 
   261 		else:
 | 
| 
 | 
   262 			rrg.setMinSize(options.minSize)
 | 
| 
 | 
   263 			rrg.setMaxSize(options.maxSize)
 | 
| 
 | 
   264 		rrg.setNumber(options.number)
 | 
| 
 | 
   265 		rrg.setStrands(options.strands)
 | 
| 
 | 
   266 	else:
 | 
| 
 | 
   267 		rrg.setAnnotation(options.inputFileName, options.format)
 | 
| 
 | 
   268 	rrg.setMaxDistribution(options.max)
 | 
| 
 | 
   269 	rrg.setDeviationDistribution(options.deviation)
 | 
| 
 | 
   270 	rrg.run()
 | 
| 
6
 | 
   271 
 |