docs: def calculate_frequencies(data, ndim: int, binnings, weights=None, dtype=None) -> Tuple[np.ndarray, np.ndarray, float]:
      """"Get frequencies and bin errors from the data (n-dimensional variant).
  
      Parameters
      ----------
      data : array_like
          2D array with ndim columns and row for each entry.
      ndim : int
          Dimensionality od the data.
      binnings:
          Binnings to apply in all axes.
      weights : Optional[array_like]
          1D array of weights to assign to values.
          (If present, must have same length as the number of rows.)
      dtype : Optional[type]
          Underlying type for the histogram.
          (If weights are specified, default is float. Otherwise int64.)
  
      Returns
      -------
      frequencies : array_like
      errors2 : array_like
      missing : scalar[dtype]
      """
  
      # TODO: Remove ndim
      # TODO: What if data is None
  
      # Prepare numpy array of data
      if data is not None:
          data = np.asarray(data)
          if data.ndim != 2:
              raise RuntimeError("histogram_nd.calculate_frequencies requires 2D input data.")
              # TODO: If somewhere, here we would check ndim
  
      # Guess correct dtype and apply to weights
      if weights is None:
          if not dtype:
              dtype = np.int64
          if data is not None:
              weights = np.ones(data.shape[0], dtype=dtype)
      else:
          weights = np.asarray(weights)
          if data is None:
              raise RuntimeError("Weights specified but data not.")
          else:
              if data.shape[0] != weights.shape[0]:
                  raise RuntimeError("Different number of entries in data and weights.")
          if dtype:
              dtype = np.dtype(dtype)
              if dtype.kind in "iu" and weights.dtype.kind == "f":
                  raise RuntimeError("Integer histogram requested but float weights entered.")
          else:
              dtype = weights.dtype
  
      edges_and_mask = [binning.numpy_bins_with_mask for binning in binnings]
      edges = [em[0] for em in edges_and_mask]
      masks = [em[1] for em in edges_and_mask]
  
      ixgrid = np.ix_(*masks)  # Indexer to select parts we want
  
      # TODO: Right edges are not taken into account because they fall into inf bin
  
      if data.shape[0]:
          frequencies, _ = np.histogramdd(data, edges, weights=weights)
          frequencies = frequencies.astype(dtype)      # Automatically copy
          frequencies = frequencies[ixgrid]
          missing = weights.sum() - frequencies.sum()
          err_freq, _ = np.histogramdd(data, edges, weights=weights ** 2)
          errors2 = err_freq[ixgrid].astype(dtype)  # Automatically copy
      else:
          frequencies = None
          missing = 0
          errors2 = None
  
      return frequencies, errors2, missing