Repository: PmagPy/PmagPy
Pull request #753: plot_ts fix from aluthfian
Author: login: apivarunas
Created: 2025-05-23T23:44:44Z | Merged: 2025-05-24T19:35:59Z

This PR addresses #694. The pmagplotlib.plot_ts() function has issues when decimal values are given from the agemin parameter. In the notebook examples, whole numbers are given, which is why it looks fine there.

@aluthfian provided code within the issue that provides a correct implementation of plot_ts, which appropriately handles decimal values for the agemin parameter. Thanks (and credit) to them!

The functionality seems good upon testing in a Jupyter notebook. When whole numbers are given, the output is equivalent for the old and new function. When decimals are given, the old function is errant and the new correct.

For good measure, I deleted a duplicate pmagplotlib.plot_ts() function that appeared earlier in the module. 


Diff:
diff --git a/pmagpy/pmagplotlib.py b/pmagpy/pmagplotlib.py
index 89f7df3da..21ebb326b 100644
--- a/pmagpy/pmagplotlib.py
+++ b/pmagpy/pmagplotlib.py
@@ -1884,49 +1884,6 @@ def plot_vs(fignum, Xs, c, ls):
             x=xv, ymin=bounds[2], ymax=bounds[3], linewidth=1, color=c, linestyle=ls)
 
 
-def plot_ts(fignum, dates, ts):
-    """
-    plot the geomagnetic polarity time scale
-
-    Parameters
-    __________
-    fignum : matplotlib figure number
-    dates : bounding dates for plot
-    ts : time scale ck95, gts04, or gts12
-    """
-    vertical_plot_init(fignum, 10, 3)
-    TS, Chrons = pmag.get_ts(ts)
-    p = 1
-    X, Y = [], []
-    for d in TS:
-        if d <= dates[1]:
-            if d >= dates[0]:
-                if len(X) == 0:
-                    ind = TS.index(d)
-                    X.append(TS[ind - 1])
-                    Y.append(p % 2)
-                X.append(d)
-                Y.append(p % 2)
-                p += 1
-                X.append(d)
-                Y.append(p % 2)
-        else:
-            X.append(dates[1])
-            Y.append(p % 2)
-            plt.plot(X, Y, 'k')
-            plot_vs(fignum, dates, 'w', '-')
-            plot_hs(fignum, [1.1, -.1], 'w', '-')
-            plt.xlabel("Age (Ma): " + ts)
-            isign = -1
-            for c in Chrons:
-                off = -.1
-                isign = -1 * isign
-                if isign > 0:
-                    off = 1.05
-                if c[1] >= X[0] and c[1] < X[-1]:
-                    plt.text(c[1] - .2, off, c[0])
-            return
-
 
 def plot_hys(fignum, B, M, s):
     """
@@ -3426,25 +3383,36 @@ def plot_eq_cont(fignum, DIblock, color_map='coolwarm'):
     plt.axis("equal")
 
 
-def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
+def plot_ts(ax, agemin, agemax, step=1.0, timescale='gts20', ylabel="Age (Ma)"):
     """
-    Make a time scale plot between specified ages.
+    This function makes a time scale plot between specified ages, using timescales 
+    as defined in pmag.get_ts(). The maximum possible age is ca. 83 Ma.
 
     Parameters:
-    ------------
     ax : figure object
-    agemin : Minimum age for timescale
-    agemax : Maximum age for timescale
-    timescale : Time Scale [ default is Gradstein et al., (2012)]
-       for other options see pmag.get_ts()
-    ylabel : if set, plot as ylabel
+    agemin : (float) Minimum age for timescale in Ma
+    agemax : (float) Maximum age for timescale in Ma
+    step : (float) Y tick label spacing in Ma
+    timescale : (string) polarity time scale, default is gts20 (Gradstein et al. 2020), other options ck95, gts04, gts20
+    ylabel : (string) if set, plot as ylabel
+
+    Returns:
+        figure object
+
+    Example:
+        Creates time scale plot from 0.5 to 5.5 Ma using the gts12 timescale:
+
+        >>> fig=plt.figure(figsize=(9,12))
+        >>> ax=fig.add_subplot(121)
+        >>> pmagplotlib.plot_ts(ax, 0.5, 5.5, timescale='gts12')
     """
     ax.set_title(timescale.upper())
+    column_bnd = 0.8 # width of timescale column
     ax.axis([-.25, 1.5, agemax, agemin])
     ax.axes.get_xaxis().set_visible(False)
     # get dates and chron names for timescale
     TS, Chrons = pmag.get_ts(timescale)
-    X, Y, Y2 = [0, 1], [], []
+    X, Y, Y2 = [0, column_bnd], [], []
     cnt = 0
     if agemin < TS[1]:  # in the Brunhes
         Y = [agemin, agemin]  # minimum age
@@ -3460,20 +3428,38 @@ def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
             if pol:
                 # fill in every other time
                 ax.fill_between(X, Y, Y1, facecolor='black')
-    ax.plot([0, 1, 1, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
-    plt.yticks(np.arange(agemin, agemax+1, 1))
+    ax.plot([0, column_bnd, column_bnd, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
+    max_y_tick = agemin + np.floor((agemax-agemin)/step)*step
+    total_step = np.rint(((max_y_tick-agemin)/step)+1).astype(int)
+    ax.set_yticks(np.linspace(agemin, max_y_tick, total_step))
+    ax.set_ylim(agemin, agemax)
     if ylabel != "":
         ax.set_ylabel(ylabel)
     ax2 = ax.twinx()
+    ax2.set_yticks(ax.get_yticks())  # Synchronize y-ticks with ax
+    ax2.set_ylim(ax.get_ylim())  # Synchronize y-axis limits with ax
     ax2.axis('off')
+    # fix courtesy of aluthfian
+    within_range = [(age[1]>=agemin)&(age[1]<=agemax) for age in Chrons]
+    ticker_num = 0
     for k in range(len(Chrons)-1):
         c = Chrons[k]
         cnext = Chrons[k+1]
-        d = cnext[1]-(cnext[1]-c[1])/3.
-        if d >= agemin and d < agemax:
+        d_plot = (c[1] + cnext[1]) / 2
+        if (d_plot >= agemin) and (d_plot < agemax):
             # make the Chron boundary tick
-            ax2.plot([1, 1.5], [c[1], c[1]], 'k-')
-            ax2.text(1.05, d, c[0])
+            ax2.plot([column_bnd, 1.5], [c[1], c[1]], 'k-')
+        if ((within_range[k]==False) and (within_range[k+1]==True)) and (ticker_num == 0):
+            d_txt = agemin + 0.5*np.abs(cnext[1]-agemin)
+            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
+            ticker_num += 1
+        elif ((within_range[k]==True) and (within_range[k+1]==False)) and (ticker_num == 1):
+            d_txt = agemax - 0.5*np.abs(agemax-c[1])
+            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
+            ticker_num += 1
+        elif (within_range[k]==True) and (within_range[k+1]==True):
+            d_txt = cnext[1]-(cnext[1]-c[1])/2.5
+            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
     ax2.axis([-.25, 1.5, agemax, agemin])
 
 
diff --git a/programs/strip_magic.py b/programs/strip_magic.py
index 0a15fa466..60a55a96d 100755
--- a/programs/strip_magic.py
+++ b/programs/strip_magic.py
@@ -349,9 +349,9 @@ def main():
         pmagplotlib.plot_hs(FIG['strat'], [0], 'b', '-')
         pmagplotlib.plot_hs(FIG['strat'], [-90, 90], 'g', '-')
     if pTS == 1:
-        FIG['ts'] = 2
-        pmagplotlib.plot_init(FIG['ts'], 10, 5)
-        pmagplotlib.plot_ts(FIG['ts'], [amin, amax], ts)
+        FIG['ts'] = pmagplotlib.plot_init(2, 10, 5)
+        ax_ts = FIG['ts'].add_subplot(111)
+        pmagplotlib.plot_ts(ax_ts, amin, amax,step=1.0, timescale=ts)
     files = {}
     for key in list(FIG.keys()):
         files[key] = key+'.'+fmt


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR replaces the old `plot_ts` implementation in `pmagplotlib.py` with a new version that correctly handles decimal `agemin`/`agemax` values and adds a `step` parameter for tick spacing; it also updates the call in `strip_magic.py` to the new signature and removes the duplicate old function.

- Introduce `step` argument and decimal support in `plot_ts`
- Remove the legacy `plot_ts` definition
- Update `strip_magic.py` to call the new `plot_ts` signature

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

| File                          | Description                                                           |
|-------------------------------|-----------------------------------------------------------------------|
| programs/strip_magic.py       | Updated `plot_ts` call to use named args (`agemin`, `agemax`, `step`, `timescale`) |
| pmagpy/pmagplotlib.py         | Removed old `plot_ts`, added new implementation with `step`, updated tick logic and labeling |


<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**pmagpy/pmagplotlib.py:3386**
* New decimal handling and `step` functionality have been introduced. Add unit tests to verify tick placement and label rendering with fractional `agemin`/`agemax` values.
```
def plot_ts(ax, agemin, agemax, step=1.0, timescale='gts12', ylabel="Age (Ma)"):
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-24T15:01:14Z
user: login: Swanson-Hysell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T15:01:31Z
user: login: Swanson-Hysell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T15:03:09Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR fixes issues in the pmagplotlib.plot_ts() function so that it correctly handles decimal values for the agemin parameter and removes a duplicate function.  
- Updated call to plot_init() in programs/strip_magic.py to correctly obtain a figure object and axis for plotting.  
- Revised the plot_ts() function to accept decimal boundaries, add customizable tick spacing, and update plot elements accordingly.  
- Removed an old duplicate implementation of plot_ts().

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

| File                        | Description                                                               |
| --------------------------- | ------------------------------------------------------------------------- |
| programs/strip_magic.py     | Updated figure initialization and parameter passing for plot_ts().       |
| pmagpy/pmagplotlib.py       | Reworked plot_ts() signature and implementation to correctly handle decimals. |




state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-24T19:22:03Z
user: login: Swanson-Hysell
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-24T19:27:49Z

Review comments:
diff_hunk: @@ -351,7 +351,7 @@ def main():
     if pTS == 1:
         FIG['ts'] = 2
         pmagplotlib.plot_init(FIG['ts'], 10, 5)
-        pmagplotlib.plot_ts(FIG['ts'], [amin, amax], ts)
+        pmagplotlib.plot_ts(FIG['ts'], amin, amax,step=1.0, timescale=ts)
path: programs/strip_magic.py
user: login: Copilot
body: The code passes a figure number to `plot_ts`, but the updated function expects an Axes object. Retrieve or create an Axes (e.g., via `fig.add_subplot(...)` or `plt.gca()`) and pass that instead of the figure number.
created_at: 2025-05-24T15:01:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3426,25 +3383,37 @@ def plot_eq_cont(fignum, DIblock, color_map='coolwarm'):
     plt.axis("equal")
 
 
-def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
+def plot_ts(ax, agemin, agemax, step=1.0, timescale='gts12', ylabel="Age (Ma)"):
     """
-    Make a time scale plot between specified ages.
+    This function makes a time scale plot between specified ages, using timsecales defined pmag.get_ts().
 
     Parameters:
-    ------------
     ax : figure object
-    agemin : Minimum age for timescale
-    agemax : Maximum age for timescale
-    timescale : Time Scale [ default is Gradstein et al., (2012)]
-       for other options see pmag.get_ts()
-    ylabel : if set, plot as ylabel
+    agemin : (float) Minimum age for timescale in Ma
+    agemax : (float) Maximum age for timescale in Ma
+    step : (float) Y tick label spacing in Ma
+    timescale : (string) polarity time scale, default is gts12 (Gradstein et al. 2012), other options ck95, gts04, gts20
+    ylabel : (string) if set, plot as ylabel
+
+    Returns:
+        figure object
+
+    Example:
+        Provide an angle and two precision parameter estimates to get the probability of
path: pmagpy/pmagplotlib.py
user: login: Copilot
body: [nitpick] The Example block in this docstring doesn't relate to `plot_ts`. Remove or replace it with a relevant example demonstrating decimal `agemin`/`agemax` and `step` usage.
created_at: 2025-05-24T15:01:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3460,20 +3429,37 @@ def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
             if pol:
                 # fill in every other time
                 ax.fill_between(X, Y, Y1, facecolor='black')
-    ax.plot([0, 1, 1, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
-    plt.yticks(np.arange(agemin, agemax+1, 1))
+    ax.plot([0, column_bnd, column_bnd, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
+    max_y_tick = agemin + np.floor((agemax-agemin)/step)*step
+    total_step = np.rint(((max_y_tick-agemin)/step)+1).astype(int)
+    plt.yticks(np.linspace(agemin, max_y_tick, total_step))
+    ax.set_ylim(agemin, agemax)
     if ylabel != "":
         ax.set_ylabel(ylabel)
     ax2 = ax.twinx()
+    ax2.sharey(ax)
     ax2.axis('off')
+    # fix courtesy of aluthfian
+    within_range = [(age[1]>=agemin)&(age[1]<=agemax) for age in Chrons]
+    ticker_num = 0
     for k in range(len(Chrons)-1):
         c = Chrons[k]
         cnext = Chrons[k+1]
-        d = cnext[1]-(cnext[1]-c[1])/3.
-        if d >= agemin and d < agemax:
+        d_plot = cnext[1]-(cnext[1]-c[1])
path: pmagpy/pmagplotlib.py
user: login: Copilot
body: This calculation simplifies to `c[1]` instead of the intended midpoint between `c[1]` and `cnext[1]`. Use `(c[1] + cnext[1]) / 2` or `c[1] + (cnext[1] - c[1]) / 2` for center alignment.
```suggestion
        d_plot = (c[1] + cnext[1]) / 2
```
created_at: 2025-05-24T15:01:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3426,25 +3383,37 @@ def plot_eq_cont(fignum, DIblock, color_map='coolwarm'):
     plt.axis("equal")
 
 
-def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
+def plot_ts(ax, agemin, agemax, step=1.0, timescale='gts12', ylabel="Age (Ma)"):
     """
-    Make a time scale plot between specified ages.
+    This function makes a time scale plot between specified ages, using timsecales defined pmag.get_ts().
 
     Parameters:
-    ------------
     ax : figure object
-    agemin : Minimum age for timescale
-    agemax : Maximum age for timescale
-    timescale : Time Scale [ default is Gradstein et al., (2012)]
-       for other options see pmag.get_ts()
-    ylabel : if set, plot as ylabel
+    agemin : (float) Minimum age for timescale in Ma
+    agemax : (float) Maximum age for timescale in Ma
+    step : (float) Y tick label spacing in Ma
+    timescale : (string) polarity time scale, default is gts12 (Gradstein et al. 2012), other options ck95, gts04, gts20
+    ylabel : (string) if set, plot as ylabel
+
+    Returns:
+        figure object
+
+    Example:
+        Provide an angle and two precision parameter estimates to get the probability of
+        simultaneity, compare to RC / 11 comparison from Table 2 of the original publication
+        (exact value may differ due to RNG):
+
+        >>> fig=plt.figure(figsize=(9,12))
+        >>> ax=fig.add_subplot(121)
+        >>> pmagplotlib.plot_ts(ax, 0.5, 5.5, timescale='gts12')
     """
     ax.set_title(timescale.upper())
+    column_bnd = 0.8
path: pmagpy/pmagplotlib.py
user: login: Copilot
body: [nitpick] The literal `0.8` controls the width of the timescale column. Consider defining a named constant or adding a comment to explain its purpose.
```suggestion
    column_bnd = TIMESCALE_COLUMN_WIDTH
```
created_at: 2025-05-24T15:01:13Z
author_association: CONTRIBUTOR
path: pmagpy/pmagplotlib.py
user: login: Swanson-Hysell
body: This example text:
```
Example:
         Provide an angle and two precision parameter estimates to get the probability of
         simultaneity, compare to RC / 11 comparison from Table 2 of the original publication
         (exact value may differ due to RNG):
```
in the `plot_ts()` docstring looks to be from a different function
created_at: 2025-05-24T15:01:31Z
author_association: MEMBER
diff_hunk: @@ -3426,25 +3383,37 @@ def plot_eq_cont(fignum, DIblock, color_map='coolwarm'):
     plt.axis("equal")
 
 
-def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
+def plot_ts(ax, agemin, agemax, step=1.0, timescale='gts12', ylabel="Age (Ma)"):
     """
-    Make a time scale plot between specified ages.
+    This function makes a time scale plot between specified ages, using timsecales defined pmag.get_ts().
 
     Parameters:
-    ------------
     ax : figure object
-    agemin : Minimum age for timescale
-    agemax : Maximum age for timescale
-    timescale : Time Scale [ default is Gradstein et al., (2012)]
-       for other options see pmag.get_ts()
-    ylabel : if set, plot as ylabel
+    agemin : (float) Minimum age for timescale in Ma
+    agemax : (float) Maximum age for timescale in Ma
+    step : (float) Y tick label spacing in Ma
+    timescale : (string) polarity time scale, default is gts12 (Gradstein et al. 2012), other options ck95, gts04, gts20
+    ylabel : (string) if set, plot as ylabel
+
+    Returns:
+        figure object
+
+    Example:
+        Provide an angle and two precision parameter estimates to get the probability of
path: pmagpy/pmagplotlib.py
user: login: Swanson-Hysell
body: It looks like Copilot and I found the same issue in this docstring...
created_at: 2025-05-24T15:03:09Z
author_association: MEMBER
diff_hunk: @@ -3460,20 +3427,37 @@ def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
             if pol:
                 # fill in every other time
                 ax.fill_between(X, Y, Y1, facecolor='black')
-    ax.plot([0, 1, 1, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
-    plt.yticks(np.arange(agemin, agemax+1, 1))
+    ax.plot([0, column_bnd, column_bnd, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
+    max_y_tick = agemin + np.floor((agemax-agemin)/step)*step
+    total_step = np.rint(((max_y_tick-agemin)/step)+1).astype(int)
+    plt.yticks(np.linspace(agemin, max_y_tick, total_step))
+    ax.set_ylim(agemin, agemax)
     if ylabel != "":
         ax.set_ylabel(ylabel)
     ax2 = ax.twinx()
+    ax2.sharey(ax)
path: pmagpy/pmagplotlib.py
user: login: Copilot
body: The method 'sharey' is not a standard Matplotlib API for axes. Consider removing it or using an alternative approach (e.g. ax2.set_yticks(ax.get_yticks())) to synchronize the y-axis.
```suggestion
    ax2.set_yticks(ax.get_yticks())  # Synchronize y-ticks with ax
    ax2.set_ylim(ax.get_ylim())  # Synchronize y-axis limits with ax
```
created_at: 2025-05-24T19:22:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3460,20 +3427,37 @@ def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
             if pol:
                 # fill in every other time
                 ax.fill_between(X, Y, Y1, facecolor='black')
-    ax.plot([0, 1, 1, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
-    plt.yticks(np.arange(agemin, agemax+1, 1))
+    ax.plot([0, column_bnd, column_bnd, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
+    max_y_tick = agemin + np.floor((agemax-agemin)/step)*step
+    total_step = np.rint(((max_y_tick-agemin)/step)+1).astype(int)
+    plt.yticks(np.linspace(agemin, max_y_tick, total_step))
+    ax.set_ylim(agemin, agemax)
     if ylabel != "":
         ax.set_ylabel(ylabel)
     ax2 = ax.twinx()
+    ax2.sharey(ax)
     ax2.axis('off')
+    # fix courtesy of aluthfian
+    within_range = [(age[1]>=agemin)&(age[1]<=agemax) for age in Chrons]
+    ticker_num = 0
     for k in range(len(Chrons)-1):
         c = Chrons[k]
         cnext = Chrons[k+1]
-        d = cnext[1]-(cnext[1]-c[1])/3.
-        if d >= agemin and d < agemax:
+        d_plot = (c[1] + cnext[1]) / 2
+        if (d_plot >= agemin) and (d_plot < agemax):
             # make the Chron boundary tick
-            ax2.plot([1, 1.5], [c[1], c[1]], 'k-')
-            ax2.text(1.05, d, c[0])
+            ax2.plot([column_bnd, 1.5], [c[1], c[1]], 'k-')
+        if ((within_range[k]==False)&(within_range[k+1]==True))&(ticker_num == 0):
+            d_txt = agemin + 0.5*np.abs(cnext[1]-agemin)
+            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
+            ticker_num += 1
+        elif ((within_range[k]==True)&(within_range[k+1]==False))&(ticker_num == 1):
+            d_txt = agemax - 0.5*np.abs(agemax-c[1])
+            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
+            ticker_num += 1
+        elif (within_range[k]==True)&(within_range[k+1]==True):
path: pmagpy/pmagplotlib.py
user: login: Copilot
body: [nitpick] Using the bitwise '&' operator for boolean logic can be less clear. It is recommended to use the 'and' operator for improved readability and to avoid potential pitfalls with non-boolean operands.
```suggestion
        if ((within_range[k]==False) and (within_range[k+1]==True)) and (ticker_num == 0):
            d_txt = agemin + 0.5*np.abs(cnext[1]-agemin)
            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
            ticker_num += 1
        elif ((within_range[k]==True) and (within_range[k+1]==False)) and (ticker_num == 1):
            d_txt = agemax - 0.5*np.abs(agemax-c[1])
            ax2.text(column_bnd+0.05, d_txt, c[0], verticalalignment='center')
            ticker_num += 1
        elif (within_range[k]==True) and (within_range[k+1]==True):
```
created_at: 2025-05-24T19:22:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3460,20 +3427,37 @@ def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
             if pol:
                 # fill in every other time
                 ax.fill_between(X, Y, Y1, facecolor='black')
-    ax.plot([0, 1, 1, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
-    plt.yticks(np.arange(agemin, agemax+1, 1))
+    ax.plot([0, column_bnd, column_bnd, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
+    max_y_tick = agemin + np.floor((agemax-agemin)/step)*step
+    total_step = np.rint(((max_y_tick-agemin)/step)+1).astype(int)
+    plt.yticks(np.linspace(agemin, max_y_tick, total_step))
path: pmagpy/pmagplotlib.py
user: login: Copilot
body: [nitpick] Since an axes object is already available (ax), consider using 'ax.set_yticks(...)' instead of the pyplot 'plt.yticks' to make the tick settings more explicit and consistent.
```suggestion
    ax.set_yticks(np.linspace(agemin, max_y_tick, total_step))
```
created_at: 2025-05-24T19:22:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3460,20 +3427,37 @@ def plot_ts(ax, agemin, agemax, timescale='gts12', ylabel="Age (Ma)"):
             if pol:
                 # fill in every other time
                 ax.fill_between(X, Y, Y1, facecolor='black')
-    ax.plot([0, 1, 1, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
-    plt.yticks(np.arange(agemin, agemax+1, 1))
+    ax.plot([0, column_bnd, column_bnd, 0, 0], [agemin, agemin, agemax, agemax, agemin], 'k-')
+    max_y_tick = agemin + np.floor((agemax-agemin)/step)*step
+    total_step = np.rint(((max_y_tick-agemin)/step)+1).astype(int)
+    plt.yticks(np.linspace(agemin, max_y_tick, total_step))
path: pmagpy/pmagplotlib.py
user: login: Swanson-Hysell
body: change applied
created_at: 2025-05-24T19:27:48Z
author_association: MEMBER

PR comments:
user: login: apivarunas
created_at: 2025-05-24T18:47:13Z
body: Alright, the three issues in `pmagplotlib.py` got addressed, as did the issue in `strip_magic.py`!
author_association: COLLABORATOR
user: login: Swanson-Hysell
created_at: 2025-05-24T19:36:46Z
body: Thanks @aluthfian and @apivarunas! This fix has been merged in.
author_association: MEMBER