code: def batchfy_by_bin(
      sorted_data,
      batch_bins,
      num_batches=0,
      min_batch_size=1,
      shortest_first=False,
      ikey="input",
      okey="output",
  ):
      """Make variably sized batch set, which maximizes
  
      the number of bins up to `batch_bins`.
  
      :param Dict[str, Dict[str, Any]] sorted_data: dictionary loaded from data.json
      :param int batch_bins: Maximum frames of a batch
      :param int num_batches: # number of batches to use (for debug)
      :param int min_batch_size: minimum batch size (for multi-gpu)
      :param int test: Return only every `test` batches
      :param bool shortest_first: Sort from batch with shortest samples
          to longest if true, otherwise reverse
  
      :param str ikey: key to access input (for ASR ikey="input", for TTS ikey="output".)
      :param str okey: key to access output (for ASR okey="output". for TTS okey="input".)
  
      :return: List[Tuple[str, Dict[str, List[Dict[str, Any]]]] list of batches
      """
      if batch_bins <= 0:
          raise ValueError(f"invalid batch_bins={batch_bins}")
      length = len(sorted_data)
      idim = int(sorted_data[0][1][ikey][0]["shape"][1])
      odim = int(sorted_data[0][1][okey][0]["shape"][1])
      logging.info("# utts: " + str(len(sorted_data)))
      minibatches = []
      start = 0
      n = 0
      while True:
          # Dynamic batch size depending on size of samples
          b = 0
          next_size = 0
          max_olen = 0
          while next_size < batch_bins and (start + b) < length:
              ilen = (
                  int(sorted_data[start + b][1][ikey][0]["shape"][0])
                  * idim
              )
              olen = (
                  int(sorted_data[start + b][1][okey][0]["shape"][0])
                  * odim
              )
              if olen > max_olen:
                  max_olen = olen
              next_size = (max_olen + ilen) * (b + 1)
              if next_size <= batch_bins:
                  b += 1
              elif next_size == 0:
                  raise ValueError(
                      f"Can't fit one sample in batch_bins ({batch_bins}): "
                      f"Please increase the value"
                  )
          end = min(length, start + max(min_batch_size, b))
          batch = sorted_data[start:end]
          if shortest_first:
              batch.reverse()
          minibatches.append(batch)
          # Check for min_batch_size and fixes the batches if needed
          i = -1
          while len(minibatches[i]) < min_batch_size:
              missing = min_batch_size - len(minibatches[i])
              if -i == len(minibatches):
                  minibatches[i + 1].extend(minibatches[i])
                  minibatches = minibatches[1:]
                  break
              else:
                  minibatches[i].extend(minibatches[i - 1][:missing])
                  minibatches[i - 1] = minibatches[i - 1][missing:]
                  i -= 1
          if end == length:
              break
          start = end
          n += 1
      if num_batches > 0:
          minibatches = minibatches[:num_batches]
      lengths = [len(x) for x in minibatches]
      logging.info(
          str(len(minibatches))
          + " batches containing from "
          + str(min(lengths))
          + " to "
          + str(max(lengths))
          + " samples "
          + "(avg "
          + str(int(np.mean(lengths)))
          + " samples)."
      )
      return minibatches
  
imports: import itertools
  import logging
  import numpy
label_desc: Use image node_id 2 for calling a global function with example usage: batchfy_by_bin(sorted_data, batch_bins, num_batches, min_batch_size, shortest_first, ikey, okey) and returns: minibatches