Repository: ARM-DOE/ACT

Issue:
Setting y-range for groupby causes error
* ACT version: 1.4.2
* Python version: 3.10.9
* Operating System: Mac Os

### Description

Trying to set the y-range for a groupby plot creates an error

### What I Did

```
files = glob.glob('./Data/sgpmetE13.b1/*2021*')
files.sort()
obj = act.io.armfiles.read_netcdf(files)
display = act.plotting.TimeSeriesDisplay(obj, subplot_shape=(4, 3))
groupby = display.group_by('month')
groupby.plot_group('plot', None, field='temp_mean', marker=' ')
groupby.display.set_yrng([-20, 50])
plt.show()
```

Error
```
  File "/Users/atheisen/Code/development-space/test_plotting.py", line 26, in <module>
    groupby.display.set_yrng([-20, 50])
  File "/Users/atheisen/Code/ACT/act/plotting/timeseriesdisplay.py", line 267, in set_yrng
    self.axes[subplot_index].set_ylim(yrng)
AttributeError: 'numpy.ndarray' object has no attribute 'set_ylim'
```



Discussion:
Note, I did find a workaround with the groupby object.

```
for ax1 in groupby.display.axes:
    for ax2 in ax1:
        ax2.set_ylim([-30, 50])
```
@AdamTheisen I can take a look at this tomorrow and either implement that as a fix or see what else can be done.
@AdamTheisen I think its due to the two dimensional subplot shape. When applying subplot_index to set_yrng to 0,0 it fixes the problem on my end, but might still need a check for when the value isn't provided.
@AdamTheisen Or using this:
```
for i in range(display.axes.shape[0]):
    for j in range(display.axes.shape[1]):
        display.set_yrng([0, 50], subplot_index=(i, j))
```
Should we add a groupby parameter like if groupby all subplots change to the yrng or something?

Resolving patch:
diff --git a/act/plotting/timeseriesdisplay.py b/act/plotting/timeseriesdisplay.py
--- a/act/plotting/timeseriesdisplay.py
+++ b/act/plotting/timeseriesdisplay.py
@@ -242,7 +242,7 @@ def set_xrng(self, xrng, subplot_index=(0,)):
             self.xrng[subplot_index][0] = xrng[0].astype('datetime64[D]').astype(float)
             self.xrng[subplot_index][1] = xrng[1].astype('datetime64[D]').astype(float)
 
-    def set_yrng(self, yrng, subplot_index=(0,)):
+    def set_yrng(self, yrng, subplot_index=(0,), match_axes_ylimits=False):
         """
         Sets the y range of the plot.
 
@@ -251,7 +251,12 @@ def set_yrng(self, yrng, subplot_index=(0,)):
         yrng : 2 number array
             The y limits of the plot.
         subplot_index : 1 or 2D tuple, list, or array
-            The index of the subplot to set the x range of.
+            The index of the subplot to set the y range of. This is
+            ignored if match_axes_ylimits is True.
+        match_axes_ylimits : boolean
+            If True, all axes in the display object will have matching
+            provided ylims. Default is False. This is especially useful
+            when utilizing a groupby display with many axes.
 
         """
         if self.axes is None:
@@ -265,7 +270,14 @@ def set_yrng(self, yrng, subplot_index=(0,)):
         if yrng[0] == yrng[1]:
             yrng[1] = yrng[1] + 1
 
-        self.axes[subplot_index].set_ylim(yrng)
+        # Sets all axes ylims to the same values.
+        if match_axes_ylimits:
+            for i in range(self.axes.shape[0]):
+                for j in range(self.axes.shape[1]):
+                    self.axes[i, j].set_ylim(yrng)
+        else:
+            self.axes[subplot_index].set_ylim(yrng)
+
         try:
             self.yrng[subplot_index, :] = yrng
         except IndexError:


Test patch:
diff --git a/act/tests/baseline/test_match_ylimits_plot.png b/act/tests/baseline/test_match_ylimits_plot.png
new file mode 100644
Binary files /dev/null and b/act/tests/baseline/test_match_ylimits_plot.png differ
diff --git a/act/tests/test_plotting.py b/act/tests/test_plotting.py
--- a/act/tests/test_plotting.py
+++ b/act/tests/test_plotting.py
@@ -1159,6 +1159,19 @@ def test_groupby_plot():
     return display.fig
 
 
+@pytest.mark.mpl_image_compare(tolerance=30)
+def test_match_ylimits_plot():
+    files = glob.glob(sample_files.EXAMPLE_MET_WILDCARD)
+    ds = arm.read_netcdf(files)
+    display = act.plotting.TimeSeriesDisplay(ds, figsize=(10, 8),
+                                             subplot_shape=(2, 2))
+    groupby = display.group_by('day')
+    groupby.plot_group('plot', None, field='temp_mean', marker=' ')
+    groupby.display.set_yrng([0, 20], match_axes_ylimits=True)
+    ds.close()
+    return display.fig
+
+
 @pytest.mark.mpl_image_compare(tolerance=30)
 def test_enhanced_skewt_plot():
     ds = arm.read_netcdf(sample_files.EXAMPLE_SONDE1)