docs: def decode_complex(data, complex_names=(None, None)):
      """ Decodes possibly complex data read from an HDF5 file.
  
      Decodes possibly complex datasets read from an HDF5 file. HDF5
      doesn't have a native complex type, so they are stored as
      H5T_COMPOUND types with fields such as 'r' and 'i' for the real and
      imaginary parts. As there is no standardization for field names, the
      field names have to be given explicitly, or the fieldnames in `data`
      analyzed for proper decoding to figure out the names. A variety of
      reasonably expected combinations of field names are checked and used
      if available to decode. If decoding is not possible, it is returned
      as is.
  
      Parameters
      ----------
      data : arraylike
          The data read from an HDF5 file, that might be complex, to
          decode into the proper Numpy complex type.
      complex_names : tuple of 2 str and/or Nones, optional
          ``tuple`` of the names to use (in order) for the real and
          imaginary fields. A ``None`` indicates that various common
          field names should be tried.
  
      Returns
      -------
      c : decoded data or data
          If `data` can be decoded into a complex type, the decoded
          complex version is returned. Otherwise, `data` is returned
          unchanged.
  
      See Also
      --------
      encode_complex
  
      Notes
      -----
      Currently looks for real field names of ``('r', 're', 'real')`` and
      imaginary field names of ``('i', 'im', 'imag', 'imaginary')``
      ignoring case.
  
      """
      # Now, complex types are stored in HDF5 files as an H5T_COMPOUND type
      # with fields along the lines of ('r', 're', 'real') and ('i', 'im',
      # 'imag', 'imaginary') for the real and imaginary parts, which most
      # likely won't be properly extracted back into making a Python
      # complex type unless the proper h5py configuration is set. Since we
      # can't depend on it being set and adjusting it is hazardous (the
      # setting is global), it is best to just decode it manually. These
      # fields are obtained from the fields of its dtype. Obviously, if
      # there are no fields, then there is nothing to do.
      if data.dtype.fields is None:
          return data
  
      fields = list(data.dtype.fields)
  
      # If there aren't exactly two fields, then it can't be complex.
      if len(fields) != 2:
          return data
  
      # We need to grab the field names for the real and imaginary
      # parts. This will be done by seeing which list, if any, each field
      # is and setting variables to the proper name if it is in it (they
      # are initialized to None so that we know if one isn't found).
  
      real_fields = ['r', 're', 'real']
      imag_fields = ['i', 'im', 'imag', 'imaginary']
  
      cnames = list(complex_names)
      for s in fields:
          if s.lower() in real_fields:
              cnames[0] = s
          elif s.lower() in imag_fields:
              cnames[1] = s
  
      # If the real and imaginary fields were found, construct the complex
      # form from the fields. This is done by finding the complex type
      # that they cast to, making an array, and then setting the
      # parts. Otherwise, return what we were given because it isn't in
      # the right form.
      if cnames[0] is not None and cnames[1] is not None:
          cdata = np.result_type(data[cnames[0]].dtype, \
              data[cnames[1]].dtype, 'complex64').type(data[cnames[0]])
          cdata.imag = data[cnames[1]]
          return cdata
      else:
          return data