Example #1
0
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.")
Example #2
0
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