docs: def map(self, f_list: List[Callable[[np.ndarray], int]], axis: int = 0, chunksize: int = 1000, selection: np.ndarray = None) -> List[np.ndarray]:
  		"""
  		Apply a function along an axis without loading the entire dataset in memory.
  
  		Args:
  			f_list (list of func):		Function(s) that takes a numpy ndarray as argument
  
  			axis (int):		Axis along which to apply the function (0 = rows, 1 = columns)
  
  			chunksize (int): Number of rows (columns) to load per chunk
  
  			selection (array of bool): Columns (rows) to include
  
  		Returns:
  			numpy.ndarray result of function application
  
  			If you supply a list of functions, the result will be a list of numpy arrays. This is more
  			efficient than repeatedly calling map() one function at a time.
  		"""
  		if hasattr(f_list, '__call__'):
  			raise ValueError("f_list must be a list of functions, not a function itself")
  
  		result = []
  		if axis == 0:
  			rows_per_chunk = chunksize
  			for i in range(len(f_list)):
  				result.append(np.zeros(self.shape[0]))
  			ix = 0
  			while ix < self.shape[0]:
  				rows_per_chunk = min(self.shape[0] - ix, rows_per_chunk)
  				if selection is not None:
  					chunk = self[ix:ix + rows_per_chunk, :][:, selection]
  				else:
  					chunk = self[ix:ix + rows_per_chunk, :]
  				for i in range(len(f_list)):
  					result[i][ix:ix + rows_per_chunk] = np.apply_along_axis(f_list[i], 1, chunk)
  				ix = ix + rows_per_chunk
  		elif axis == 1:
  			cols_per_chunk = chunksize
  			for i in range(len(f_list)):
  				result.append(np.zeros(self.shape[1]))
  			ix = 0
  			while ix < self.shape[1]:
  				cols_per_chunk = min(self.shape[1] - ix, cols_per_chunk)
  				if selection is not None:
  					chunk = self[:, ix:ix + cols_per_chunk][selection, :]
  				else:
  					chunk = self[:, ix:ix + cols_per_chunk]
  				for i in range(len(f_list)):
  					result[i][ix:ix + cols_per_chunk] = np.apply_along_axis(f_list[i], 0, chunk)
  				ix = ix + cols_per_chunk
  		return result