docs: def _build_tag_families(tagged_paired_aligns,
                          ranked_tags,
                          hamming_threshold,
                          consensus_threshold,
                          family_filter=lambda _: None):
      '''Partition paired aligns into families.
  
      Each read is considered against each ranked tag until all reads are
      partitioned into families.'''
      tag_aligns = defaultdict(set)
      tag_inexact_match_count = defaultdict(int)
  
      for paired_align in tagged_paired_aligns:
          (left_umt, right_umt) =  paired_align.umt
          for best_tag in ranked_tags:
              if paired_align.umt == best_tag:
                  tag_aligns[best_tag].add(paired_align)
                  break
              elif left_umt == best_tag[0] or right_umt == best_tag[1]:
                  tag_aligns[best_tag].add(paired_align)
                  tag_inexact_match_count[best_tag] += 1
                  break
              elif (_hamming_dist(left_umt, best_tag[0]) <= hamming_threshold) \
                  or (_hamming_dist(right_umt, best_tag[1]) <= hamming_threshold):
                  tag_aligns[best_tag].add(paired_align)
                  tag_inexact_match_count[best_tag] += 1
                  break
      tag_families = []
      for tag in sorted(tag_aligns):
          tag_family = TagFamily(tag,
                                 tag_aligns[tag],
                                 tag_inexact_match_count[tag],
                                 consensus_threshold,
                                 family_filter)
          tag_families.append(tag_family)
      return tag_families