docs: def plot_pseudosection_type2(self, mid, **kwargs):
          """Create a pseudosection plot of type 2.
  
          For a given measurement data set, create plots that graphically show
          the data in a 2D color plot. Hereby, x and y coordinates in the plot
          are determined by the current dipole (x-axis) and voltage dipole
          (y-axis) of the corresponding measurement configurations.
  
          This type of rawdata plot can plot any type of measurement
          configurations, i.e., it is not restricted to certain types of
          configurations such as Dipole-dipole or Wenner configurations. However,
          spatial inferences cannot be made from the plots for all configuration
          types.
  
          Coordinates are generated by separately sorting the dipoles
          (current/voltage) along the first electrode, and then subsequently
          sorting by the difference (skip) between both electrodes.
  
          Note that this type of raw data plot does not take into account the
          real electrode spacing of the measurement setup.
  
          Type 2 plots can show normal and reciprocal data at the same time.
          Hereby the upper left triangle of the plot area usually contains normal
          data, and the lower right triangle contains the corresponding
          reciprocal data. Therefore a quick assessment of normal-reciprocal
          differences can be made by visually comparing the symmetry on the 1:1
          line going from the lower left corner to the upper right corner.
  
          Note that this interpretation usually only holds for Dipole-Dipole data
          (and the related Wenner configurations).
  
          Parameters
          ----------
  
          mid: integer or numpy.ndarray
              Measurement ID of stored measurement data, or a numpy array (size
              N) with data that will be plotted. Must have the same length as
              number of configurations.
          ax: matplotlib.Axes object, optional
              axes object to plot to. If not provided, a new figure and axes
              object will be created and returned
          nocb: bool, optional
              if set to False, don't plot the colorbar
          cblabel: string, optional
              label for the colorbar
          cbmin: float, optional
              colorbar minimum
          cbmax: float, optional
              colorbar maximum
          xlabel: string, optional
              xlabel for the plot
          ylabel: string, optional
              ylabel for the plot
          do_not_saturate: bool, optional
              if set to True, then values outside the colorbar range will not
              saturate with the respective limit colors. Instead, values lower
              than the CB are colored "cyan" and vaues above the CB limit are
              colored "red"
          log10: bool, optional
              if set to True, plot the log10 values of the provided data
  
          Returns
          -------
          fig:
              figure object
          ax:
              axes object
  
          Examples
          --------
  
          You can just supply a data vector to the plot function:
  
          .. plot::
              :include-source:
  
              import numpy as np
              import crtomo.configManager as CRConfig
              configs = CRConfig.ConfigManager(nr_of_electrodes=48)
              configs.gen_dipole_dipole(skipc=1, stepc=2)
              measurements = np.random.random(configs.nr_of_configs)
              configs.plot_pseudosection_type2(
                  mid=measurements,
              )
  
          Generate a simple type 2 plot:
  
          .. plot::
              :include-source:
  
              import numpy as np
              import crtomo.configManager as CRConfig
              configs = CRConfig.ConfigManager(nr_of_electrodes=48)
              configs.gen_dipole_dipole(skipc=1, stepc=2)
              measurements = np.random.random(configs.nr_of_configs)
              mid = configs.add_measurements(measurements)
              configs.plot_pseudosection_type2(
                  mid,
                  cblabel='this label',
                  xlabel='xlabel',
                  ylabel='ylabel',
              )
  
          You can also supply axes to plot to:
  
          .. plot::
              :include-source:
  
              import numpy as np
              from crtomo.mpl_setup import *
              import crtomo.configManager as CRConfig
  
              configs = CRConfig.ConfigManager(nr_of_electrodes=48)
              configs.gen_dipole_dipole(skipc=1, stepc=2)
              K = configs.compute_K_factors(spacing=1)
              measurements = np.random.random(configs.nr_of_configs)
              mid = configs.add_measurements(measurements)
  
              fig, axes = plt.subplots(1, 2)
  
              configs.plot_pseudosection_type2(
                  mid,
                  ax=axes[0],
                  cblabel='this label',
                  xlabel='xlabel',
                  ylabel='ylabel',
              )
              configs.plot_pseudosection_type2(
                  K,
                  ax=axes[1],
                  cblabel='K factor',
                  xlabel='xlabel',
                  ylabel='ylabel',
              )
              fig.tight_layout()
  
  
  
  
          """
          c = self.configs
  
          AB_ids = self._get_unique_identifiers(c[:, 0:2])
          MN_ids = self._get_unique_identifiers(c[:, 2:4])
  
          ab_sorted = np.sort(c[:, 0:2], axis=1)
          mn_sorted = np.sort(c[:, 2:4], axis=1)
  
          AB_coords = [
              AB_ids[x] for x in
              (ab_sorted[:, 0] * 1e5 + ab_sorted[:, 1]).astype(int)
          ]
          MN_coords = [
              MN_ids[x] for x in
              (mn_sorted[:, 0] * 1e5 + mn_sorted[:, 1]).astype(int)
          ]
  
          # check for duplicate positions
          ABMN_coords = np.vstack((AB_coords, MN_coords)).T.copy()
          _, counts = np.unique(
              ABMN_coords.view(
                  ABMN_coords.dtype.descr * 2
              ),
              return_counts=True,
          )
          if np.any(counts > 1):
              print('found duplicate coordinates!')
              duplicate_configs = np.where(counts > 1)[0]
              print('duplicate configs:')
              print('A B M N')
              for i in duplicate_configs:
                  print(c[i, :])
  
          # prepare matrix
          if isinstance(mid, int):
              plot_values = self.measurements[mid]
          elif isinstance(mid, np.ndarray):
              plot_values = np.squeeze(mid)
          else:
              raise Exception('Data in parameter "mid" not understood')
  
          if kwargs.get('log10', False):
              plot_values = np.log10(plot_values)
  
          C = np.zeros((len(MN_ids.items()), len(AB_ids))) * np.nan
          C[MN_coords, AB_coords] = plot_values
  
          # for display purposes, reverse the first dimension
          C = C[::-1, :]
  
          ax = kwargs.get('ax', None)
          if ax is None:
              fig, ax = plt.subplots(1, 1, figsize=(15 / 2.54, 10 / 2.54))
          fig = ax.get_figure()
  
          cmap = mpl.cm.get_cmap('viridis')
          if kwargs.get('do_not_saturate', False):
              cmap.set_over(
                  color='r'
              )
              cmap.set_under(
                  color='c'
              )
          im = ax.matshow(
              C,
              interpolation='none',
              cmap=cmap,
              aspect='auto',
              vmin=kwargs.get('cbmin', None),
              vmax=kwargs.get('cbmax', None),
              extent=[
                  0, max(AB_coords),
                  0, max(MN_coords),
              ],
          )
  
          max_xy = max((max(AB_coords), max(MN_coords)))
          ax.plot(
              (0, max_xy),
              (0, max_xy),
              '-',
              color='k',
              linewidth=1.0,
          )
  
          if not kwargs.get('nocb', False):
              cb = fig.colorbar(im, ax=ax)
              cb.set_label(
                  kwargs.get('cblabel', '')
              )
  
          ax.set_xlabel(
              kwargs.get('xlabel', 'current dipoles')
          )
          ax.set_ylabel(
              kwargs.get('ylabel', 'voltage dipoles')
          )
  
          return fig, ax