Repository: wright-group/WrightTools

Issue:
kit function for guessing array plotting params
To be included in our workflow for plotting completed acquisitions, but as a kit function, it is available for e.g. "from" methods and other niche cases.  Analysis should determine guesses for signed/unsigned boolean, perhaps null, limits.

Heuristic based, using information like min/max, 95% window, or similar.



Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0833b05..ddbe283 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
 ## [Unreleased]
 
 ### Added
+- `kit.guess_signed` for empirically guessing channel sign (useful for automated workflows)
 - `Data.squeeze`: squeezing the data object to the shape of the axes.
 
 ### Fixed
diff --git a/WrightTools/kit/_array.py b/WrightTools/kit/_array.py
index c4c13c4..691c891 100644
--- a/WrightTools/kit/_array.py
+++ b/WrightTools/kit/_array.py
@@ -27,6 +27,7 @@ __all__ = [
     "valid_index",
     "mask_reduce",
     "enforce_mask_shape",
+    "guess_signed",
 ]
 
 
@@ -432,3 +433,38 @@ def enforce_mask_shape(mask, shape):
     """
     red = tuple([i for i in range(len(shape)) if shape[i] == 1])
     return mask.max(axis=red, keepdims=True)
+
+
+def guess_signed(chan, tol=1e-1):
+    """guess whether or not a channel is signed by examining min and max values.
+
+    Parameters
+    -------------
+    chan : array-like
+        Input channel or array
+    tol : float (optional)
+        Tolerance value used to judge signed. `tol` should be much less than 1. To prefer signed guesses, use negative tolerance values.
+
+    Returns
+    -------
+    guess : bool
+        True if the data seems signed and False otherwise.
+    """
+
+    maxc = chan.max()
+    minc = chan.min()
+    from ..data import Channel
+
+    if isinstance(chan, Channel):
+        null = chan.null
+    else:
+        null = 0
+
+    # avoid zero division for comparison
+    bottom = np.abs(maxc - null) + np.abs(minc - null)
+    if not bottom:  # (maxc-null)=-(minc-null)
+        return True
+
+    comparison = np.abs(maxc + minc - 2 * null) / bottom
+    # should be < 1 if signed
+    return comparison < 1 - tol


Test patch:
diff --git a/tests/kit/signed.py b/tests/kit/signed.py
new file mode 100644
index 0000000..f56f52d
--- /dev/null
+++ b/tests/kit/signed.py
@@ -0,0 +1,30 @@
+"""Test guess_signed"""
+
+# --- import -------------------------------------------------------------------------------------
+
+
+import numpy as np
+import WrightTools as wt
+
+
+# --- test ---------------------------------------------------------------------------------------
+
+
+def test_many_ranges():
+    for minmax, signed in [
+        ([-0.05, 1], False),
+        ([-1, 0.05], False),
+        ([-1, 1], True),
+        ([0, 0], True),
+        ([0, -1], False),
+        ([-1, 0.5], True),
+    ]:
+        assert wt.kit.guess_signed(np.array(minmax)) == signed
+
+
+def test_channel():
+    d = wt.Data()
+    chan = d.create_channel("chan", values=np.linspace(3, 4, 16).reshape(4, 4))
+    assert wt.kit.guess_signed(chan) == False
+    chan.null = 3.5
+    assert wt.kit.guess_signed(chan)