def main(argv=sys.argv[1:]): """Command-line program Parameters ---------- argv : list, optional A list of command-line arguments, which will be processed as if the script were called from the command line if :py:func:`main` is called directly. Default: sys.argv[1:] (actually command-line arguments) """ ap = AnnotationParser(input_choices=_ANNOTATION_INPUT_CHOICES) annotation_file_parser = ap.get_parser() bp = BaseParser() base_parser = bp.get_parser() parser = argparse.ArgumentParser(description=format_module_docstring(__doc__), formatter_class=argparse.RawDescriptionHelpFormatter, parents=[base_parser,annotation_file_parser]) parser.add_argument("--export_tophat",default=False,action="store_true", help="Export tophat `.juncs` file in addition to BED output") parser.add_argument("outbase",type=str,help="Basename for output files") args = parser.parse_args(argv) bp.get_base_ops_from_args(args) transcripts = ap.get_transcripts_from_args(args,printer=printer,return_type=SegmentChain) with argsopener("%s.bed" % args.outbase,args,"w") as bed_out: if args.export_tophat == True: tophat_out = open("%s.juncs" % args.outbase,"w") printer.write("params: " +" ".join(argv)) printer.write("Detecting & comparing junctions...") ex_pairs = {} c = 0 u = 0 for chain in transcripts: if len(chain) > 1: # if multi-exon chrom = chain.chrom strand = chain.strand try: ep = ex_pairs[(chrom,strand)] except KeyError: ex_pairs[(chrom,strand)] = [] ep = ex_pairs[(chrom,strand)] for i in range(0,len(chain)-1): seg1 = chain[i] seg2 = chain[i+1] if c % 1000 == 0 and c > 0: printer.write("Processed %s junctions. Found %s unique..." % (c,u) ) c+=1 key = (seg1.end,seg2.start) if key not in ep: ep.append(key) u += 1 new_chain = SegmentChain(seg1,seg2) bed_out.write(new_chain.as_bed()) if args.export_tophat == True: my_junc = (chrom,seg1.end-1,seg2.start,strand) tophat_out.write("%s\t%s\t%s\t%s\n" % my_junc) del new_chain del seg1 del seg2 del chain printer.write("Processed %s total junctions. Found %s unique." % (c,u) ) bed_out.close() if args.export_tophat == True: tophat_out.close() printer.write("Done.")
def process_partial_group(transcripts, mask_hash, printer): """Correct boundaries of merged genes, as described in :func:`do_generate` Parameters ---------- transcripts : dict Dictionary mapping unique transcript IDs to |Transcripts|. This set should be complete in the sense that it should contain all transcripts that have any chance of mutually overlapping each other (e.g. all on same chromosome and strand). mask_hash : |GenomeHash| |GenomeHash| of regions to exclude from analysis Returns ------- :class:`pandas.DataFrame` Table of merged gene positions :class:`pandas.DataFrame` Table of adjusted transcript positions :class:`dict` Dictionary mapping raw gene names to merged gene names """ gene_table = { "region": [], "transcript_ids": [], "exon_unmasked": [], "exon": [], "masked": [], "utr5": [], "cds": [], "utr3": [], "exon_bed": [], "utr5_bed": [], "cds_bed": [], "utr3_bed": [], "masked_bed": [], } # data table for transcripts transcript_table = { "region": [], "exon": [], "utr5": [], "cds": [], "utr3": [], "masked": [], "exon_unmasked": [], "transcript_ids": [], "exon_bed": [], "utr5_bed": [], "cds_bed": [], "utr3_bed": [], "masked_bed": [], } keycombos = list(itertools.permutations(("utr5", "cds", "utr3"), 2)) # merge genes that share exons & write output printer.write("Collapsing genes that share exons ...") merged_genes = merge_genes(transcripts) # remap transcripts to merged genes # and vice-versa merged_gene_tx = {} tx_merged_gene = {} printer.write("Mapping transcripts to merged genes...") for txid in transcripts: my_tx = transcripts[txid] my_gene = my_tx.get_gene() my_merged = merged_genes[my_gene] tx_merged_gene[txid] = my_merged try: merged_gene_tx[my_merged].append(txid) except KeyError: merged_gene_tx[my_merged] = [txid] # flatten merged genes printer.write( "Flattening merged genes, masking positions, and labeling subfeatures ..." ) for n, (gene_id, my_txids) in enumerate(merged_gene_tx.items()): if n % 1000 == 0 and n > 0: printer.write(" %s genes ..." % n) my_gene_positions = [] chroms = [] strands = [] for my_txid in my_txids: my_segmentchain = transcripts[my_txid] chroms.append(my_segmentchain.chrom) strands.append(my_segmentchain.strand) my_gene_positions.extend(my_segmentchain.get_position_list()) try: assert len(set(chroms)) == 1 except AssertionError: printer.write( "Skipping gene %s which contains multiple chromosomes: %s" % (gene_id, ",".join(chroms))) try: assert len(set(strands)) == 1 except AssertionError: printer.write( "Skipping gene %s which contains multiple strands: %s" % (gene_id, ",".join(strands))) my_gene_positions = set(my_gene_positions) gene_ivc_raw = SegmentChain( *positions_to_segments(chroms[0], strands[0], my_gene_positions)) gene_table["region"].append(gene_id) gene_table["transcript_ids"].append(",".join(sorted(my_txids))) gene_table["exon_unmasked"].append(gene_ivc_raw) printer.write(" %s genes total." % (n + 1)) # mask genes printer.write("Masking positions and labeling subfeature positions ...") gene_hash = GenomeHash(gene_table["exon_unmasked"], do_copy=False) for n, (gene_id, gene_ivc_raw) in enumerate( zip(gene_table["region"], gene_table["exon_unmasked"])): if n % 2000 == 0: printer.write(" %s genes ..." % n) my_chrom = gene_ivc_raw.spanning_segment.chrom my_strand = gene_ivc_raw.spanning_segment.strand masked_positions = [] nearby_genes = gene_hash[gene_ivc_raw] # don't mask out positions from identical gene gene_ivc_raw_positions = gene_ivc_raw.get_position_set() nearby_genes = [ X for X in nearby_genes if X.get_position_set() != gene_ivc_raw_positions ] for gene in nearby_genes: masked_positions.extend(gene.get_position_list()) nearby_masks = mask_hash[gene_ivc_raw] for mask in nearby_masks: masked_positions.extend(mask.get_position_list()) masked_positions = set(masked_positions) gene_positions_raw = gene_ivc_raw.get_position_set() mask_ivc_positions = gene_positions_raw & masked_positions total_mask_ivc = SegmentChain(*positions_to_segments( my_chrom, my_strand, mask_ivc_positions), ID=gene_id) gene_table["masked"].append(total_mask_ivc) gene_table["masked_bed"].append(total_mask_ivc.as_bed()) gene_post_mask = gene_positions_raw - masked_positions gene_post_mask_ivc = SegmentChain(*positions_to_segments( my_chrom, my_strand, gene_post_mask), ID=gene_id) gene_table["exon"].append(gene_post_mask_ivc) gene_table["exon_bed"].append(gene_post_mask_ivc.as_bed()) masked_positions = total_mask_ivc.get_position_set() tmp_positions = { "utr5": set(), "cds": set(), "utr3": set(), } txids = sorted(merged_gene_tx[gene_id]) chrom = gene_post_mask_ivc.chrom strand = gene_post_mask_ivc.strand # pool transcript positions for txid in txids: transcript = transcripts[txid] utr5pos = transcript.get_utr5().get_position_set() cdspos = transcript.get_cds().get_position_set() utr3pos = transcript.get_utr3().get_position_set() tmp_positions["utr5"] |= utr5pos tmp_positions["cds"] |= cdspos tmp_positions["utr3"] |= utr3pos # eliminate positions in which CDS & UTRs overlap from each transcript for txid in txids: transcript = transcripts[txid] transcript_positions = { "utr5": transcript.get_utr5().get_position_set(), "cds": transcript.get_cds().get_position_set(), "utr3": transcript.get_utr3().get_position_set(), } for key1, key2 in keycombos: transcript_positions[key1] -= tmp_positions[key2] transcript_positions[key1] -= masked_positions transcript_table["region"].append(txid) # all unmasked positions my_chain = SegmentChain(*positions_to_segments( chrom, strand, transcript.get_position_set() - masked_positions), ID=txid) transcript_table["exon"].append(str(my_chain)) transcript_table["exon_bed"].append(my_chain.as_bed()) # all uniquely-labeled unmasked positions for k, v in transcript_positions.items(): my_chain = SegmentChain(*positions_to_segments( chrom, strand, v), ID=txid) transcript_table[k].append(str(my_chain)) transcript_table["%s_bed" % k].append(my_chain.as_bed()) total_mask_ivc.attr["ID"] = txid transcript_table["masked"].append(str(total_mask_ivc)) transcript_table["masked_bed"].append(total_mask_ivc.as_bed()) transcript_table["exon_unmasked"].append(str(transcript)) transcript_table["transcript_ids"].append(txid) tmp_positions2 = copy.deepcopy(tmp_positions) for k1, k2 in keycombos: tmp_positions[k1] -= tmp_positions2[k2] tmp_positions[k1] -= masked_positions for k in (tmp_positions.keys()): my_chain = SegmentChain(*positions_to_segments( chrom, strand, tmp_positions[k]), ID=gene_id) gene_table[k].append(str(my_chain)) gene_table["%s_bed" % k].append(my_chain.as_bed()) printer.write(" %s genes total." % (n + 1)) # cast SegmentChains/Transcripts to strings to keep numpy from unpacking them conversion_keys = [ "exon", "utr5", "cds", "utr3", "masked", "exon_unmasked" ] for k in conversion_keys: gene_table[k] = [str(X) for X in gene_table[k]] transcript_table[k] = [str(X) for X in transcript_table[k]] gene_df = pd.DataFrame(gene_table) gene_df.sort_values(["region"], inplace=True) transcript_df = pd.DataFrame(transcript_table) transcript_df.sort_values(["region"], inplace=True) return gene_df, transcript_df, merged_genes