def group_by_allele(self, locus):
        '''
        Split the PileupCollection by the alleles suggested by the reads at the
        specified locus.
        If a read has an insertion immediately following the locus, then the
        insertion is included in the allele. For example, if locus is the
        1-base range [5,6), one allele might be "AGA", indicating that at
        locus 5 some read has an "A" followed by a 2-base insertion ("GA"). If
        a read has a deletion at the specified locus, the allele is the empty
        string.
        The given locus may include any number of bases. If the locus includes
        multiple bases, then the alleles consist of all bases aligning to that
        range in any read. Note that only sequences actually sequenced in a
        particular read are included. For example, if one read has "ATT" at a
        locus and another read has "GCC", then the alleles are "ATT" and
        "GCC", but not "GTT". That is, the bases in each allele are phased. For
        this reason, only reads that overlap the entire locus are included.
        If the locus is an empty interval (e.g. [5,5) ), then the alleles
        consist only of inserted bases. In this example, only bases inserted
        immediately after locus 5 would be included (but *not* the base
        actually at locus 5). In the previous insertion example, the allele
        would be "GA", indicating a 2-base insertion. Reads that have no
        insertion at that position (matches or deletions) would have the empty
        string as their allele.
        Parameters
        ----------
        locus : Locus
            The reference locus, encompassing 0 or more bases.
        Returns
        ----------
        A dict of string -> PileupCollection. The keys are nucleotide strings
        giving the bases sequenced at the locus, and the values are
        PileupCollection instances of the alignments that support that allele.
        '''
        locus = to_locus(locus)
        read_to_allele = None
        loci = []
        if locus.positions:
            for position in locus.positions:
                base_position = Locus.from_interbase_coordinates(
                    locus.contig, position)
                loci.append(base_position)
                new_read_to_allele = {}
                for element in self.pileups[base_position]:
                    allele_prefix = ""
                    key = alignment_key(element.alignment)
                    if read_to_allele is not None:
                        try:
                            allele_prefix = read_to_allele[key]
                        except KeyError:
                            continue
                    allele = allele_prefix + element.bases
                    new_read_to_allele[key] = allele
                read_to_allele = new_read_to_allele
        else:
            position_before = Locus.from_interbase_coordinates(
                locus.contig, locus.start)
            loci.append(position_before)
            read_to_allele = {}
            for element in self.pileups[position_before]:
                allele = element.bases[1:]
                read_to_allele[alignment_key(element.alignment)] = allele
        split = defaultdict(lambda: PileupCollection(pileups={}, parent=self))
        for locus in loci:
            pileup = self.pileups[locus]
            for e in pileup.elements:
                key = read_to_allele.get(alignment_key(e.alignment))
                if key is not None:
                    if locus in split[key].pileups:
                        split[key].pileups[locus].append(e)
                    else:
                        split[key].pileups[locus] = Pileup(locus, [e])
        def sorter(pair):
            (allele, pileup_collection) = pair
            return (-1 * pileup_collection.num_reads(), allele)
        return OrderedDict(sorted(split.items(), key=sorter))