docs: def aggregate(self, out_file: str = None, select: np.ndarray = None, group_by: Union[str, np.ndarray] = "Clusters", aggr_by: str = "mean", aggr_ca_by: Dict[str, str] = None) -> np.ndarray:
  		"""
  		Aggregate the Loom file by applying aggregation functions to the main matrix as well as to the column attributes
  
  		Args:
  			out_file	The name of the output Loom file (will be appended to if it exists)
  			select		Bool array giving the columns to include (or None, to include all)
  			group_by	The column attribute to group by, or an np.ndarray of integer group labels
  			aggr_by 	The aggregation function for the main matrix
  			aggr_ca_by	A dictionary of aggregation functions for the column attributes (or None to skip)
  
  		Returns:
  			m			Aggregated main matrix
  
  		Remarks:
  			aggr_by gives the aggregation function for the main matrix
  			aggr_ca_by is a dictionary with column attributes as keys and aggregation functionas as values
  			
  			Aggregation functions can be any valid aggregation function from here: https://github.com/ml31415/numpy-groupies
  
  			In addition, you can specify:
  				"tally" to count the number of occurences of each value of a categorical attribute
  		
  		"""
  		ca = {}  # type: Dict[str, np.ndarray]
  		if select is not None:
  			raise ValueError("The 'select' argument is deprecated")
  		if isinstance(group_by, np.ndarray):
  			labels = group_by
  		else:
  			labels = (self.ca[group_by]).astype('int')
  		_, zero_strt_sort_noholes_lbls = np.unique(labels, return_inverse=True)
  		n_groups = len(set(labels))
  		if aggr_ca_by is not None:
  			for key in self.ca.keys():
  				if key not in aggr_ca_by:
  					continue
  				func = aggr_ca_by[key]
  				if func == "tally":
  					for val in set(self.ca[key]):
  						if np.issubdtype(type(val), np.str_):
  							valnew = val.replace("/", "-")  # Slashes are not allowed in attribute names
  							valnew = valnew.replace(".", "_")  # Nor are periods
  						ca[key + "_" + str(valnew)] = npg.aggregate(zero_strt_sort_noholes_lbls, (self.ca[key] == val).astype('int'), func="sum", fill_value=0)
  				elif func == "mode":
  					def mode(x):  # type: ignore
  						return scipy.stats.mode(x)[0][0]
  					ca[key] = npg.aggregate(zero_strt_sort_noholes_lbls, self.ca[key], func=mode, fill_value=0).astype('str')
  				elif func == "mean":
  					ca[key] = npg.aggregate(zero_strt_sort_noholes_lbls, self.ca[key], func=func, fill_value=0)
  				elif func == "first":
  					ca[key] = npg.aggregate(zero_strt_sort_noholes_lbls, self.ca[key], func=func, fill_value=self.ca[key][0])
  
  		m = np.empty((self.shape[0], n_groups))
  		for (_, selection, view) in self.scan(axis=0, layers=[""]):
  			vals_aggr = npg.aggregate(zero_strt_sort_noholes_lbls, view[:, :], func=aggr_by, axis=1, fill_value=0)
  			m[selection, :] = vals_aggr
  
  		if out_file is not None:
  			loompy.create(out_file, m, self.ra, ca)
  
  		return m