docs: def group_by_ngram(self, labels):
          """Groups result rows by n-gram and label, providing a single summary
          field giving the range of occurrences across each work's
          witnesses. Results are sorted by n-gram then by label (in the
          order given in `labels`).
  
          :param labels: labels to order on
          :type labels: `list` of `str`
  
          """
          if self._matches.empty:
              # Ensure that the right columns are used, even though the
              # results are empty.
              self._matches = pd.DataFrame(
                  {}, columns=[
                      constants.NGRAM_FIELDNAME,
                      constants.SIZE_FIELDNAME,
                      constants.LABEL_FIELDNAME,
                      constants.WORK_COUNTS_FIELDNAME])
              return
          label_order_col = 'label order'
  
          def work_summary(group):
              match = group.iloc[0]
              work = match[constants.WORK_FIELDNAME]
              minimum = group[constants.COUNT_FIELDNAME].min()
              maximum = group[constants.COUNT_FIELDNAME].max()
              if minimum == maximum:
                  work_count = '{}({})'.format(work, minimum)
              else:
                  work_count = '{}({}-{})'.format(work, minimum, maximum)
              match['work count'] = work_count
              return match
  
          def ngram_label_summary(group):
              match = group.iloc[0]
              summary = group.groupby(constants.WORK_FIELDNAME).apply(
                  work_summary)
              work_counts = ', '.join(list(summary['work count']))
              match[constants.WORK_COUNTS_FIELDNAME] = work_counts
              return match
  
          def add_label_order(row, labels):
              row[label_order_col] = labels.index(row[constants.LABEL_FIELDNAME])
              return row
  
          group_cols = [constants.NGRAM_FIELDNAME, constants.LABEL_FIELDNAME]
          matches = self._matches.groupby(group_cols, sort=False).apply(
              ngram_label_summary)
          del matches[constants.WORK_FIELDNAME]
          del matches[constants.SIGLUM_FIELDNAME]
          del matches[constants.COUNT_FIELDNAME]
          matches = matches.apply(add_label_order, axis=1, args=(labels,))
          matches.reset_index(drop=True, inplace=True)
          matches.sort_values(by=[constants.NGRAM_FIELDNAME, label_order_col],
                              ascending=True, inplace=True)
          del matches[label_order_col]
          self._matches = matches