Repository: pyfar/pyfar
Pull request #656 (2024-07-21 13:11:56)

Add complex 2d plots
### Which issue(s) are closed by this pull request?

Closes #

### Changes proposed in this pull request:

-
-
-



Patch:
diff --git a/pyfar/plot/_line.py b/pyfar/plot/_line.py
index 8393b56f..e4d6355a 100644
--- a/pyfar/plot/_line.py
+++ b/pyfar/plot/_line.py
@@ -28,8 +28,8 @@ def _time(signal, dB=False, log_prefix=20, log_reference=1, unit="s",
         ymin = ymax - 100
     else:
         data = signal.time.T
-    # get data defined in 'show_real_imag_abs'
-    data, y_label = _utils._assert_and_match_data_to_mode(data, mode)
+    # get data defined in 'mode'
+    data, y_label = _utils._assert_and_match_data_to_mode(data, signal, mode)
 
     # auto detect the time unit
     if unit in [None, "auto"]:
@@ -80,7 +80,7 @@ def _freq(signal, dB=True, log_prefix=None, log_reference=1, freq_scale='log',
     else:
         data = np.abs(signal.freq)
 
-    data, frequencies = _utils._assert_and_match_data_to_side(
+    data, frequencies, xlabel = _utils._assert_and_match_data_to_side(
         data, signal, side)
 
     # prepare figure
@@ -91,7 +91,8 @@ def _freq(signal, dB=True, log_prefix=None, log_reference=1, freq_scale='log',
         _utils._set_axlim(ax, ax.set_ylim, ymin, ymax, ax.get_ylim())
     else:
         ax.set_ylabel("Magnitude")
-    ax.set_xlabel("Frequency in Hz")
+    ax.set_xlabel(xlabel)
+
     _utils._set_axlim(ax, ax.set_xlim, _utils._lower_frequency_limit(signal),
                       frequencies[-1], ax.get_xlim())
 
@@ -127,7 +128,7 @@ def _phase(signal, deg=False, unwrap=False, freq_scale='log', ax=None,
     kwargs = _utils._return_default_colors_rgb(**kwargs)
     phase_data = dsp.phase(signal, deg=deg, unwrap=unwrap)
 
-    phase_data, frequencies = _utils._assert_and_match_data_to_side(
+    phase_data, frequencies, xlabel = _utils._assert_and_match_data_to_side(
         phase_data, signal, side)
 
     # Construct the correct label string
@@ -181,7 +182,8 @@ def _phase(signal, deg=False, unwrap=False, freq_scale='log', ax=None,
             ax.yaxis.set_minor_locator(MultipleFractionLocator(np.pi, 4))
 
     # prepare figure
-    ax.set_xlabel("Frequency in Hz")
+    ax.set_xlabel(xlabel)
+
     ax.set_ylabel(ylabel_string)
     ax.grid(True, 'both')
     _utils._set_axlim(ax, ax.set_xlim, _utils._lower_frequency_limit(signal),
@@ -225,12 +227,13 @@ def _group_delay(signal, unit="s", freq_scale='log', ax=None, side='right',
         factor, unit = _utils._deal_time_units(unit)
         data = data / signal.sampling_rate * factor
 
-    data, frequencies = _utils._assert_and_match_data_to_side(
+    data, frequencies, xlabel = _utils._assert_and_match_data_to_side(
         data, signal, side)
 
     # prepare figure
     _, ax = _utils._prepare_plot(ax)
-    ax.set_xlabel("Frequency in Hz")
+    ax.set_xlabel(xlabel)
+
     ax.set_ylabel(f"Group delay in {unit}")
     ax.grid(True, 'both')
     _utils._set_axlim(ax, ax.set_xlim, _utils._lower_frequency_limit(signal),
diff --git a/pyfar/plot/_two_d.py b/pyfar/plot/_two_d.py
index 8a46f0d9..8e8f49c3 100644
--- a/pyfar/plot/_two_d.py
+++ b/pyfar/plot/_two_d.py
@@ -12,7 +12,7 @@ from matplotlib.ticker import NullFormatter
 
 
 def _time_2d(signal, dB, log_prefix, log_reference, unit, indices,
-             orientation, method, colorbar, ax, **kwargs):
+             orientation, method, colorbar, ax, mode='real', **kwargs):
 
     # check input and prepare the figure, axis, and common parameters
     fig, ax, indices, kwargs = _utils._prepare_2d_plot(
@@ -27,6 +27,10 @@ def _time_2d(signal, dB, log_prefix, log_reference, unit, indices,
         ymin = ymax - 100
     else:
         data = signal.time
+
+    # get data defined in 'mode'
+    data, y_label = _utils._assert_and_match_data_to_mode(data, signal, mode)
+
     data = data.T if orientation == "vertical" else data
     # auto detect the time unit
     if unit in [None, "auto"]:
@@ -62,13 +66,13 @@ def _time_2d(signal, dB, log_prefix, log_reference, unit, indices,
 
     # colorbar
     cb = _utils._add_colorbar(colorbar, fig, ax, qm,
-                              "Amplitude in dB" if dB else "Amplitude")
+                              f"{y_label} in dB" if dB else y_label)
 
     return ax[0], qm, cb
 
 
 def _freq_2d(signal, dB, log_prefix, log_reference, freq_scale, indices,
-             orientation, method, colorbar, ax, **kwargs):
+             orientation, method, colorbar, ax, side='right', **kwargs):
 
     # check input and prepare the figure, axis, and common parameters
     fig, ax, indices, kwargs = _utils._prepare_2d_plot(
@@ -85,6 +89,12 @@ def _freq_2d(signal, dB, log_prefix, log_reference, freq_scale, indices,
         ymax = ymax + 10
     else:
         data = np.abs(signal.freq)
+
+    # get data according to side
+    data, frequencies, xlabel = _utils._assert_and_match_data_to_side(data,
+                                                                      signal,
+                                                                      side)
+
     data = data.T if orientation == "vertical" else data
     # setup axis label and data
     axis = [ax[0].yaxis, ax[0].xaxis]
@@ -96,7 +106,7 @@ def _freq_2d(signal, dB, log_prefix, log_reference, freq_scale, indices,
         ax_scale = np.roll(ax_scale, 1)
 
     axis[1].set_label_text("Indices")
-    axis[0].set_label_text("Frequency in Hz")
+    axis[0].set_label_text(xlabel)
     ax_lim[0](_utils._lower_frequency_limit(signal), signal.frequencies[-1])
 
     ax_scale[0](freq_scale)
@@ -112,8 +122,8 @@ def _freq_2d(signal, dB, log_prefix, log_reference, freq_scale, indices,
         kwargs["vmax"] = ymax
 
     # plot data
-    indices_x = indices if orientation == "vertical" else signal.frequencies
-    indices_y = signal.frequencies if orientation == "vertical" else indices
+    indices_x = indices if orientation == "vertical" else frequencies
+    indices_y = frequencies if orientation == "vertical" else indices
     qm = _plot_2d(indices_x, indices_y, data, method, ax[0], **kwargs)
 
     # colorbar
@@ -124,7 +134,7 @@ def _freq_2d(signal, dB, log_prefix, log_reference, freq_scale, indices,
 
 
 def _phase_2d(signal, deg, unwrap, freq_scale, indices, orientation, method,
-              colorbar, ax, **kwargs):
+              colorbar, ax, side='right', **kwargs):
 
     # check input and prepare the figure, axis, and common parameters
     fig, ax, indices, kwargs = _utils._prepare_2d_plot(
@@ -134,6 +144,10 @@ def _phase_2d(signal, deg, unwrap, freq_scale, indices, orientation, method,
 
     # prepare input
     data = dsp.phase(signal, deg=deg, unwrap=unwrap)
+    # get data according to side
+    data, frequencies, xlabel = _utils._assert_and_match_data_to_side(data,
+                                                                      signal,
+                                                                      side)
     data = data.T if orientation == "vertical" else data
 
     # setup axis label and data
@@ -146,7 +160,7 @@ def _phase_2d(signal, deg, unwrap, freq_scale, indices, orientation, method,
         ax_scale = np.roll(ax_scale, 1)
 
     axis[1].set_label_text("Indices")
-    axis[0].set_label_text("Frequency in Hz")
+    axis[0].set_label_text(xlabel)
     ax_lim[0](_utils._lower_frequency_limit(signal), signal.frequencies[-1])
 
     # color limits
@@ -157,8 +171,8 @@ def _phase_2d(signal, deg, unwrap, freq_scale, indices, orientation, method,
         kwargs["vmax"] = np.nanmax(data) + phase_margin
 
     # plot data
-    indices_x = indices if orientation == "vertical" else signal.frequencies
-    indices_y = signal.frequencies if orientation == "vertical" else indices
+    indices_x = indices if orientation == "vertical" else frequencies
+    indices_y = frequencies if orientation == "vertical" else indices
     qm = _plot_2d(indices_x, indices_y, data, method, ax[0], **kwargs)
 
     ax_scale[0](freq_scale)
@@ -183,7 +197,7 @@ def _phase_2d(signal, deg, unwrap, freq_scale, indices, orientation, method,
 
 
 def _group_delay_2d(signal, unit, freq_scale, indices, orientation, method,
-                    colorbar, ax, **kwargs):
+                    colorbar, ax, side='right', **kwargs):
 
     # check input and prepare the figure, axis, and common parameters
     fig, ax, indices, kwargs = _utils._prepare_2d_plot(
@@ -194,7 +208,14 @@ def _group_delay_2d(signal, unit, freq_scale, indices, orientation, method,
     kwargs = _utils._return_default_colors_rgb(**kwargs)
     data = dsp.group_delay(signal)
     data = np.reshape(data, signal.freq.shape)
+
+    # get data according to side
+    data, frequencies, xlabel = _utils._assert_and_match_data_to_side(data,
+                                                                      signal,
+                                                                      side)
+
     data = data.T if orientation == "vertical" else data
+
     # auto detect the unit
     if unit is None:
         unit = _utils._time_auto_unit(
@@ -214,7 +235,7 @@ def _group_delay_2d(signal, unit, freq_scale, indices, orientation, method,
         ax_scale = np.roll(ax_scale, 1)
 
     axis[1].set_label_text("Indices")
-    axis[0].set_label_text("Frequency in Hz")
+    axis[0].set_label_text(xlabel)
     ax_lim[0](_utils._lower_frequency_limit(signal), signal.frequencies[-1])
 
     ax_scale[0](freq_scale)
@@ -230,8 +251,8 @@ def _group_delay_2d(signal, unit, freq_scale, indices, orientation, method,
         kwargs["vmax"] = 1.5 * np.nanmax(data)
 
     # plot data
-    indices_x = indices if orientation == "vertical" else signal.frequencies
-    indices_y = signal.frequencies if orientation == "vertical" else indices
+    indices_x = indices if orientation == "vertical" else frequencies
+    indices_y = frequencies if orientation == "vertical" else indices
     qm = _plot_2d(indices_x, indices_y, data, method, ax[0], **kwargs)
 
     # colorbar
@@ -242,7 +263,7 @@ def _group_delay_2d(signal, unit, freq_scale, indices, orientation, method,
 
 def _time_freq_2d(signal, dB_time, dB_freq, log_prefix_time, log_prefix_freq,
                   log_reference, freq_scale, unit, indices, orientation,
-                  method, colorbar, ax, **kwargs):
+                  method, colorbar, ax,  mode='real', side='right', **kwargs):
     """
     Plot the time signal and magnitude spectrum in a 2 by 1 subplot layout.
     """
@@ -251,10 +272,10 @@ def _time_freq_2d(signal, dB_time, dB_freq, log_prefix_time, log_prefix_freq,
 
     _, qm_0, cb_0 = _time_2d(
         signal, dB_time, log_prefix_time, log_reference, unit, indices,
-        orientation, method, colorbar, ax[0], **kwargs)
+        orientation, method, colorbar, ax[0], mode, **kwargs)
     _, qm_1, cb_1 = _freq_2d(
         signal, dB_freq, log_prefix_freq, log_reference, freq_scale, indices,
-        orientation, method, colorbar, ax[1], **kwargs)
+        orientation, method, colorbar, ax[1], side, **kwargs)
     fig.align_ylabels()
 
     return ax, [qm_0, qm_1], [cb_0, cb_1]
@@ -262,17 +283,17 @@ def _time_freq_2d(signal, dB_time, dB_freq, log_prefix_time, log_prefix_freq,
 
 def _freq_phase_2d(signal, dB, log_prefix, log_reference, freq_scale, deg,
                    unwrap, indices, orientation, method, colorbar, ax,
-                   **kwargs):
+                   side='right', **kwargs):
     """Plot the magnitude and phase spectrum in a 2 by 1 subplot layout."""
 
     fig, ax = _utils._prepare_plot(ax, (2, 1))
 
     _, qm_0, cb_0 = _freq_2d(signal, dB, log_prefix, log_reference, freq_scale,
                              indices, orientation, method, colorbar,
-                             ax[0], **kwargs)
+                             ax[0], side, **kwargs)
     _, qm_1, cb_1 = _phase_2d(signal, deg, unwrap, freq_scale, indices,
                               orientation, method, colorbar, ax[1],
-                              **kwargs)
+                              side, **kwargs)
     ax[0].set_xlabel(None)
     fig.align_ylabels()
 
@@ -281,7 +302,7 @@ def _freq_phase_2d(signal, dB, log_prefix, log_reference, freq_scale, deg,
 
 def _freq_group_delay_2d(
         signal, dB, log_prefix, log_reference, unit, freq_scale, indices,
-        orientation, method, colorbar, ax, **kwargs):
+        orientation, method, colorbar, ax, side='right', **kwargs):
     """
     Plot the magnitude and group delay spectrum in a 2 by 1 subplot layout.
     """
@@ -290,10 +311,10 @@ def _freq_group_delay_2d(
 
     _, qm_0, cb_0 = _freq_2d(signal, dB, log_prefix, log_reference, freq_scale,
                              indices, orientation, method, colorbar,
-                             ax[0], **kwargs)
+                             ax[0], side, **kwargs)
     _, qm_1, cb_1 = _group_delay_2d(
         signal, unit, freq_scale, indices, orientation, method,
-        colorbar, ax[1], **kwargs)
+        colorbar, ax[1], side, **kwargs)
     ax[0].set_xlabel(None)
     fig.align_ylabels()
 
diff --git a/pyfar/plot/_utils.py b/pyfar/plot/_utils.py
index 6f1e676f..467ffcec 100644
--- a/pyfar/plot/_utils.py
+++ b/pyfar/plot/_utils.py
@@ -449,14 +449,22 @@ def _assert_and_match_data_to_side(data, signal, side):
         frequencies = np.flipud(np.abs(frequencies))
         data = data[..., ::-1]
 
-    return data, frequencies
+    if (type(signal) is not FrequencyData) and signal.complex:
+        xlabel = f"Frequency in Hz ({side})"
+    else:
+        xlabel = "Frequency in Hz"
+
+    return data, frequencies, xlabel
 
 
-def _assert_and_match_data_to_mode(data, mode):
+def _assert_and_match_data_to_mode(data, signal, mode):
     """Adjust data and y-label for plotting according to specified mode."""
 
     if mode == 'real':
-        return np.real(data), 'Amplitude'
+        if signal.complex:
+            return np.real(data), 'Amplitude (real)'
+        else:
+            return np.real(data), 'Amplitude'
     elif mode == 'imag':
         return np.imag(data), 'Amplitude (imaginary)'
     elif mode == 'abs':
diff --git a/pyfar/plot/line.py b/pyfar/plot/line.py
index 16b7b498..cb96b5b6 100644
--- a/pyfar/plot/line.py
+++ b/pyfar/plot/line.py
@@ -50,10 +50,11 @@ def time(signal, dB=False, log_prefix=20, log_reference=1, unit="s",
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
-    mode : str
+    mode : str, optional
         ``real``, ``imag``, or ``abs`` to specify if the real part, imaginary
-        part or absolute value of a complex-valued time domain signal is
-        plotted. The default is ``real``.
+        part or absolute value of the time data is plotted. ``'imag'`` and
+        ``'abs'``` can only be used for complex Signals.
+        The default is ``real``.
     **kwargs
         Keyword arguments that are passed to :py:func:`matplotlib.pyplot.plot`.
 
@@ -126,8 +127,11 @@ def freq(signal, dB=True, log_prefix=None, log_reference=1, freq_scale='log',
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
-    side : str
-        ``left`` or ``right`` to specify which side of a spectrum is plotted.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to :py:func:`matplotlib.pyplot.plot`.
 
@@ -193,6 +197,11 @@ def phase(signal, deg=False, unwrap=False, freq_scale='log', ax=None,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to :py:func:`matplotlib.pyplot.plot`.
 
@@ -265,6 +274,11 @@ def group_delay(signal, unit="s", freq_scale='log', ax=None, style='light',
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to :py:func:`matplotlib.pyplot.plot`.
 
@@ -355,9 +369,16 @@ def time_freq(signal, dB_time=False, dB_freq=True, log_prefix_time=20,
         ``light`` or ``dark`` to use the pyfar plot styles or a plot style from
         :py:data:`matplotlib.style.available`. Pass a dictionary to set
         specific plot parameters, for example
-        ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
-        ``style = {}`` to use the currently active plotstyle. The default is
-        ``light``.
+    mode : str
+        ``'real'``, ``'imag'``, or ``'abs'`` to specify if the real part,
+        imaginary part or absolute value of the time data is plotted.
+        ``'imag'`` and ``'abs'``` can only be used for complex Signals.
+        The default is ``'real'``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to :py:func:`matplotlib.pyplot.plot`.
 
@@ -438,6 +459,11 @@ def freq_phase(signal, dB=True, log_prefix=None, log_reference=1,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are forwarded to matplotlib.pyplot.plot
 
@@ -524,6 +550,11 @@ def freq_group_delay(signal, dB=True, log_prefix=None, log_reference=1,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to :py:func:`matplotlib.pyplot.plot`.
 
diff --git a/pyfar/plot/two_d.py b/pyfar/plot/two_d.py
index 05208016..ca400391 100644
--- a/pyfar/plot/two_d.py
+++ b/pyfar/plot/two_d.py
@@ -7,7 +7,7 @@ from . import _interaction as ia
 
 def time_2d(signal, dB=False, log_prefix=None, log_reference=1, unit="s",
             indices=None, orientation="vertical", method='pcolormesh',
-            colorbar=True, ax=None, style='light', **kwargs):
+            colorbar=True, ax=None, style='light', mode='real', **kwargs):
     """
     2D color coded plot of time signals.
 
@@ -95,6 +95,11 @@ def time_2d(signal, dB=False, log_prefix=None, log_reference=1, unit="s",
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    mode : str, optional
+        ``real``, ``imag``, or ``abs`` to specify if the real part, imaginary
+        part or absolute value of the time data is plotted. ``'imag'`` and
+        ``'abs'``` can only be used for complex Signals.
+        The default is ``real``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -136,7 +141,7 @@ def time_2d(signal, dB=False, log_prefix=None, log_reference=1, unit="s",
     with context(style):
         ax, qm, cb = _two_d._time_2d(
             signal, dB, log_prefix, log_reference, unit,
-            indices, orientation, method, colorbar, ax, **kwargs)
+            indices, orientation, method, colorbar, ax, mode, **kwargs)
 
     plot_parameter = ia.PlotParameter(
         'time_2d', dB_time=dB, log_prefix_time=log_prefix,
@@ -155,7 +160,7 @@ def time_2d(signal, dB=False, log_prefix=None, log_reference=1, unit="s",
 def freq_2d(signal, dB=True, log_prefix=None, log_reference=1,
             freq_scale='log', indices=None, orientation="vertical",
             method='pcolormesh', colorbar=True, ax=None, style='light',
-            **kwargs):
+            side='right', **kwargs):
     """
     2D color coded plot of magnitude spectra.
 
@@ -232,6 +237,11 @@ def freq_2d(signal, dB=True, log_prefix=None, log_reference=1,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -272,7 +282,7 @@ def freq_2d(signal, dB=True, log_prefix=None, log_reference=1,
     with context(style):
         ax, qm, cb = _two_d._freq_2d(
             signal, dB, log_prefix, log_reference, freq_scale, indices,
-            orientation, method, colorbar, ax, **kwargs)
+            orientation, method, colorbar, ax, side, **kwargs)
 
     # manage interaction
     plot_parameter = ia.PlotParameter(
@@ -291,7 +301,7 @@ def freq_2d(signal, dB=True, log_prefix=None, log_reference=1,
 
 def phase_2d(signal, deg=False, unwrap=False, freq_scale='log', indices=None,
              orientation="vertical", method='pcolormesh',
-             colorbar=True, ax=None, style='light', **kwargs):
+             colorbar=True, ax=None, style='light', side='right', **kwargs):
     """
     2D color coded plot of phase spectra.
 
@@ -363,6 +373,11 @@ def phase_2d(signal, deg=False, unwrap=False, freq_scale='log', indices=None,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -421,7 +436,8 @@ def phase_2d(signal, deg=False, unwrap=False, freq_scale='log', indices=None,
 
 def group_delay_2d(signal, unit="s", freq_scale='log', indices=None,
                    orientation="vertical", method='pcolormesh',
-                   colorbar=True, ax=None, style='light', **kwargs):
+                   colorbar=True, ax=None, style='light', side='right',
+                   **kwargs):
     """
     2D color coded plot of the group delay.
 
@@ -501,6 +517,11 @@ def group_delay_2d(signal, unit="s", freq_scale='log', indices=None,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -541,7 +562,7 @@ def group_delay_2d(signal, unit="s", freq_scale='log', indices=None,
     with context(style):
         ax, qm, cb = _two_d._group_delay_2d(
             signal, unit, freq_scale, indices, orientation, method,
-            colorbar, ax, **kwargs)
+            colorbar, ax, side, **kwargs)
 
     # manage interaction
     plot_parameter = ia.PlotParameter(
@@ -561,7 +582,7 @@ def time_freq_2d(signal, dB_time=False, dB_freq=True, log_prefix_time=20,
                  log_prefix_freq=None, log_reference=1, freq_scale='log',
                  unit='s', indices=None, orientation="vertical",
                  method='pcolormesh', colorbar=True, ax=None, style='light',
-                 **kwargs):
+                 mode='real', side='right', **kwargs):
     """
     2D color coded plot of time signals and magnitude spectra (2 by 1 subplot).
 
@@ -654,6 +675,15 @@ def time_freq_2d(signal, dB_time=False, dB_freq=True, log_prefix_time=20,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    mode : str
+        ``real``, ``imag``, or ``abs`` to specify if the real part, imaginary
+        part or absolute value of a complex-valued time domain signal is
+        plotted. The default is ``real``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -696,7 +726,7 @@ def time_freq_2d(signal, dB_time=False, dB_freq=True, log_prefix_time=20,
         ax, qm, cb = _two_d._time_freq_2d(
             signal, dB_time, dB_freq, log_prefix_time, log_prefix_freq,
             log_reference, freq_scale, unit, indices, orientation, method,
-            colorbar, ax, **kwargs)
+            colorbar, ax, mode, side, **kwargs)
 
     # manage interaction
     plot_parameter = ia.PlotParameter(
@@ -718,7 +748,8 @@ def time_freq_2d(signal, dB_time=False, dB_freq=True, log_prefix_time=20,
 def freq_phase_2d(signal, dB=True, log_prefix=None, log_reference=1,
                   freq_scale='log', deg=False, unwrap=False, indices=None,
                   orientation="vertical", method='pcolormesh',
-                  colorbar=True, ax=None, style='light', **kwargs):
+                  colorbar=True, ax=None, style='light', side='right',
+                  **kwargs):
     """
     2D color coded plot of magnitude and phase spectra (2 by 1 subplot).
 
@@ -795,6 +826,11 @@ def freq_phase_2d(signal, dB=True, log_prefix=None, log_reference=1,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -837,7 +873,7 @@ def freq_phase_2d(signal, dB=True, log_prefix=None, log_reference=1,
     with context(style):
         ax, qm, cb = _two_d._freq_phase_2d(
             signal, dB, log_prefix, log_reference, freq_scale, deg, unwrap,
-            indices, orientation, method, colorbar, ax, **kwargs)
+            indices, orientation, method, colorbar, ax, side, **kwargs)
 
     # manage interaction
     plot_parameter = ia.PlotParameter(
@@ -858,7 +894,8 @@ def freq_phase_2d(signal, dB=True, log_prefix=None, log_reference=1,
 def freq_group_delay_2d(signal, dB=True, log_prefix=None, log_reference=1,
                         unit="s", freq_scale='log', indices=None,
                         orientation="vertical", method='pcolormesh',
-                        colorbar=True, ax=None, style='light', **kwargs):
+                        colorbar=True, ax=None, style='light', side='right',
+                        **kwargs):
     """
     2D color coded plot of magnitude spectra and group delay (2 by 1 subplot).
 
@@ -944,6 +981,11 @@ def freq_group_delay_2d(signal, dB=True, log_prefix=None, log_reference=1,
         ``style = {'axes.facecolor':'black'}``. Pass an empty dictionary
         ``style = {}`` to use the currently active plotstyle. The default is
         ``light``.
+    side : str, optional
+        ``'right'`` to plot the right-sided spectrum containing the positive
+        frequencies, or ``'left'``to plot the left-sided spectrum containing
+        the negative frequencies (only possible for complex Signals). The
+        default is ``'right'``.
     **kwargs
         Keyword arguments that are passed to
         :py:func:`matplotlib.pyplot.pcolormesh` or
@@ -985,7 +1027,7 @@ def freq_group_delay_2d(signal, dB=True, log_prefix=None, log_reference=1,
     with context(style):
         ax, qm, cb = _two_d._freq_group_delay_2d(
             signal, dB, log_prefix, log_reference, unit, freq_scale, indices,
-            orientation, method, colorbar, ax, **kwargs)
+            orientation, method, colorbar, ax, side, **kwargs)
 
     # manage interaction
     plot_parameter = ia.PlotParameter(


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 4e11800c..4569dc8e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -552,6 +552,28 @@ def handsome_signal_2d():
     return signal
 
 
+@pytest.fixture
+def handsome_signal_complex_2d():
+    """
+    45 channel signal with delayed, scaled and bell-filtered impulses
+    for testing 2D plots
+
+    Returns
+    -------
+    signal : Signal
+        Multi channel signal
+    """
+
+    delays = np.array(np.sin(np.linspace(0, 2*np.pi, 45))*50 + 55, dtype=int)
+    amplitudes = 10**(-10*(1-np.cos(np.linspace(0, 2*np.pi, 45)))/20)
+    signal = pyfar.signals.impulse(2**9, delays, amplitudes)
+    signal.complex = True
+    for idx, s in enumerate(signal):
+        signal[idx] = pf.dsp.filter.bell(s, (idx+1)*200, -20, 5)
+
+    return signal
+
+
 @pytest.fixture
 def time_data():
     """
diff --git a/tests/test_plot.py b/tests/test_plot.py
index 3186f2bf..8b86bdfd 100644
--- a/tests/test_plot.py
+++ b/tests/test_plot.py
@@ -315,6 +315,18 @@ def test_2d_plots(function, handsome_signal_2d):
                      file_type, compare_output)
 
 
+@pytest.mark.parametrize('function', [
+    (plot.time_2d), (plot.freq_2d), (plot.phase_2d), (plot.group_delay_2d),
+    (plot.time_freq_2d), (plot.freq_phase_2d), (plot.freq_group_delay_2d)])
+def test_2d_plots_complex(function, handsome_signal_complex_2d):
+    """Test all 2d plots with default arguments."""
+    filename = f'complex_2d_{function.__name__}'
+    create_figure()
+    function(handsome_signal_complex_2d)
+    save_and_compare(create_baseline, baseline_path, output_path, filename,
+                     file_type, compare_output)
+
+
 @pytest.mark.parametrize('function', [
     (plot.time_2d), (plot.freq_2d), (plot.phase_2d),
     (plot.group_delay_2d), (plot.time_freq_2d), (plot.freq_phase_2d),
diff --git a/tests/test_plot__utils.py b/tests/test_plot__utils.py
index 84d4271e..0cd9ce71 100644
--- a/tests/test_plot__utils.py
+++ b/tests/test_plot__utils.py
@@ -165,17 +165,19 @@ def test_assert_and_match_data_to_side():
     signal.fft_norm = 'none'
     signal.complex = True
 
-    data, frequencies = plot._utils._assert_and_match_data_to_side(
+    data, frequencies, _xlabel = plot._utils._assert_and_match_data_to_side(
         signal.freq, signal, side='left')
 
     assert not np.any(frequencies < 0.0)
     assert data.shape[-1] == frequencies.shape[0]
+    assert _xlabel == "Frequency in Hz (left)"
 
-    data, frequencies = plot._utils._assert_and_match_data_to_side(
+    data, frequencies, _xlabel = plot._utils._assert_and_match_data_to_side(
         signal.freq, signal, side='right')
 
     assert not np.any(frequencies < 0.0)
     assert data.shape[-1] == frequencies.shape[0]
+    assert _xlabel == "Frequency in Hz (right)"
 
 
 def test_assert_and_match_data_to_side_freq():
@@ -187,8 +189,9 @@ def test_assert_and_match_data_to_side_freq():
         plot._utils._assert_and_match_data_to_side(
             signal.freq, signal, side='left')
 
-    data, frequencies = plot._utils._assert_and_match_data_to_side(
+    data, frequencies, _xlabel = plot._utils._assert_and_match_data_to_side(
         signal.freq, signal, side='right')
+
     assert not np.any(frequencies < 0.0)
     assert data.shape[-1] == frequencies.shape[0]
 
@@ -199,19 +202,25 @@ def test_assert_and_match_data_to_side_freq():
         plot._utils._assert_and_match_data_to_side(
             signal.freq, signal, side='right')
 
-    data, frequencies = plot._utils._assert_and_match_data_to_side(
+    data, frequencies, _xlabel = plot._utils._assert_and_match_data_to_side(
         signal.freq, signal, side='left')
     assert not np.any(frequencies < 0.0)
     assert data.shape[-1] == frequencies.shape[0]
 
 
 @pytest.mark.parametrize("mode, ylabel", [('real', 'Amplitude'),
+                                          ('real', 'Amplitude (real)'),
                                           ('imag', 'Amplitude (imaginary)'),
                                           ('abs', 'Amplitude (absolute)')])
 def test_assert_and_match_data_to_mode(mode, ylabel):
     signal = pf.signals.sine(20, 32)
 
+    if not ylabel == 'Amplitude':
+        signal.fft_norm = 'none'
+        signal.complex = True
+
     data, _ylabel = plot._utils._assert_and_match_data_to_mode(signal.time,
+                                                               signal,
                                                                mode)
 
     if mode == 'real':