Repository: librosa/librosa

Issue:
specshow auto-aspect should match on data, not decoration
**Is your feature request related to a problem? Please describe.**

PR #1312 implemented auto-aspect pinning for the case where we have a square image to show (e.g., a self-similarity matrix).  The logic requires that the x and y coordinates are equivalent, and that `x_axis == y_axis`:

https://github.com/librosa/librosa/blob/7ac022a8496126e95b46a7d56ad880a328359bda/librosa/display.py#L1278-L1282

This works great much of the time, but is I think overly strict when we might want to have similarly-typed axes with different decorations, eg `time` vs `time_s`.  In that situation, the axes matching fails, and the aspect is not set square.

I could see this being useful in the context of #1364 

**Describe the solution you'd like**

I think we can soften the comparison logic a bit, so that any "time-like" axis types are considered compatible; ditto for frequency, ditto for tempo.  Probably the easiest way to implement this is to create a module-level `set` object with all tuples of compatible axis types:

```python
AXIS_COMPAT = set([('time', 's'), ('time', ms'), ...])

...

axes_same_and_not_none = (x_axis, y_axis) in AXIS_COMPAT
...
```


Discussion:

Resolving patch:
diff --git a/librosa/display.py b/librosa/display.py
--- a/librosa/display.py
+++ b/librosa/display.py
@@ -35,6 +35,7 @@
 
 """
 
+from itertools import product
 import warnings
 
 import numpy as np
@@ -617,6 +618,23 @@ def __envelope(x, hop):
     return x_frame.max(axis=1)
 
 
+_chroma_ax_types = ('chroma', 'chroma_h', 'chroma_c',)
+_cqt_ax_types = ('cqt_hz', 'cqt_note', 'cqt_svara',)
+_freq_ax_types = ('linear', 'fft', 'hz', 'fft_note', 'fft_svara',)
+_time_ax_types = ('time', 's', 'ms',)
+_lag_ax_types = ('lag', 'lag_s', 'lag_ms',)
+_misc_ax_types = ('tempo', 'fourier_tempo', 'mel', 'log', 'tonnetz', 'frames',)
+
+_AXIS_COMPAT = set(
+    [(t, t) for t in _misc_ax_types] +
+    [t for t in product(_chroma_ax_types, _chroma_ax_types)] +
+    [t for t in product(_cqt_ax_types, _cqt_ax_types)] +
+    [t for t in product(_freq_ax_types, _freq_ax_types)] +
+    [t for t in product(_time_ax_types, _time_ax_types)] +
+    [t for t in product(_lag_ax_types, _lag_ax_types)]
+)
+
+
 @deprecate_positional_args
 def specshow(
     data,
@@ -1276,10 +1294,10 @@ def __coord_time(n, sr=22050, hop_length=512, **_kwargs):
 
 
 def __same_axes(x_axis, y_axis, xlim, ylim):
-    """Check if two axes are the same, used to determine squared plots"""
-    axes_same_and_not_none = (x_axis == y_axis) and (x_axis is not None)
+    """Check if two axes are similar, used to determine squared plots"""
+    axes_compatible_and_not_none = (x_axis, y_axis) in _AXIS_COMPAT
     axes_same_lim = xlim == ylim
-    return axes_same_and_not_none and axes_same_lim
+    return axes_compatible_and_not_none and axes_same_lim
 
 
 @deprecate_positional_args


Test patch:
diff --git a/tests/test_display.py b/tests/test_display.py
--- a/tests/test_display.py
+++ b/tests/test_display.py
@@ -833,6 +833,7 @@ def test_display_fourier_tempo_odd():
         ("time", "linear", (0.0, 1.0), (0.0, 1.0), False),
         ("time", "time", (0.0, 1.0), (0.0, 2.0), False),
         ("chroma", "chroma", (0.0, 1.0), (0.0, 1.0), True),
+        ("s", "ms", (0.0, 1.0), (0.0, 1.0), True),
     ],
 )
 def test_same_axes(x_axis, y_axis, xlim, ylim, out):
@@ -841,7 +842,7 @@ def test_same_axes(x_axis, y_axis, xlim, ylim, out):
 
 def test_auto_aspect():
 
-    fig, ax = plt.subplots(nrows=4)
+    fig, ax = plt.subplots(nrows=5)
 
     # Ensure auto aspect by default
     for axi in ax:
@@ -849,7 +850,7 @@ def test_auto_aspect():
 
     X = np.zeros((12, 12))
 
-    # Different axes should retain auto scaling
+    # Different axes with incompatible types should retain auto scaling
     librosa.display.specshow(X, x_axis="chroma", y_axis="time", ax=ax[0])
     assert ax[0].get_aspect() == "auto"
 
@@ -869,6 +870,10 @@ def test_auto_aspect():
     )
     assert ax[3].get_aspect() == "auto"
 
+    # different axes with compatible types and auto_aspect=True should force equal scaling
+    librosa.display.specshow(X, x_axis="time", y_axis="ms", ax=ax[4])
+    assert ax[4].get_aspect() == 1.0
+
 
 @pytest.mark.mpl_image_compare(
     baseline_images=["specshow_unicode_true"], extensions=["png"], tolerance=6, style=STYLE