diff --git a/CHANGELOG.md b/CHANGELOG.md index 0833b051..ddbe2835 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 c4c13c49..691c8919 100644 --- a/WrightTools/kit/_array.py +++ b/WrightTools/kit/_array.py @@ -27,6 +27,7 @@ "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 diff --git a/tests/kit/signed.py b/tests/kit/signed.py new file mode 100644 index 00000000..f56f52d0 --- /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)