docs: def aggregate_by(self, tagnames, array):
          """
          :param tagnames: a list of valid tag names
          :param array: an array with the same length as the asset collection
          :returns: an array of aggregate values with the proper shape
          """
          missing = set(tagnames) - set(self.tagcol.tagnames)
          if missing:
              raise ValueError('Unknown tagname(s) %s' % missing)
          A, *shp = array.shape
          if A != len(self):
              raise ValueError('The array must have length %d, got %d' %
                               (len(self), A))
          if not tagnames:
              return array.sum(axis=0)
          shape = [len(getattr(self.tagcol, tagname))-1 for tagname in tagnames]
          acc = numpy.zeros(shape, (F32, shp) if shp else F32)
          for asset, row in zip(self.array, array):
              acc[tuple(idx - 1 for idx in asset[tagnames])] += row
          return acc