We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
test kit.smooth_1D
write tests for `wt.kit.smooth_1D`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `WrightTools/kit/_array.py`
Content:
```
1 """Array interaction tools."""
2 
3 
4 # --- import --------------------------------------------------------------------------------------
5 
6 
7 import numpy as np
8 
9 from .. import exceptions as wt_exceptions
10 
11 
12 # --- define --------------------------------------------------------------------------------------
13 
14 
15 __all__ = [
16     "closest_pair",
17     "diff",
18     "fft",
19     "joint_shape",
20     "orthogonal",
21     "remove_nans_1D",
22     "share_nans",
23     "smooth_1D",
24     "svd",
25     "unique",
26     "valid_index",
27     "mask_reduce",
28     "enforce_mask_shape",
29 ]
30 
31 
32 # --- functions -----------------------------------------------------------------------------------
33 
34 
35 def closest_pair(arr, give="indicies"):
36     """Find the pair of indices corresponding to the closest elements in an array.
37 
38     If multiple pairs are equally close, both pairs of indicies are returned.
39     Optionally returns the closest distance itself.
40 
41     I am sure that this could be written as a cheaper operation. I
42     wrote this as a quick and dirty method because I need it now to use on some
43     relatively small arrays. Feel free to refactor if you need this operation
44     done as fast as possible. - Blaise 2016-02-07
45 
46     Parameters
47     ----------
48     arr : numpy.ndarray
49         The array to search.
50     give : {'indicies', 'distance'} (optional)
51         Toggle return behavior. If 'distance', returns a single float - the
52         closest distance itself. Default is indicies.
53 
54     Returns
55     -------
56     list of lists of two tuples
57         List containing lists of two tuples: indicies the nearest pair in the
58         array.
59 
60         >>> arr = np.array([0, 1, 2, 3, 3, 4, 5, 6, 1])
61         >>> closest_pair(arr)
62         [[(1,), (8,)], [(3,), (4,)]]
63 
64     """
65     idxs = [idx for idx in np.ndindex(arr.shape)]
66     outs = []
67     min_dist = arr.max() - arr.min()
68     for idxa in idxs:
69         for idxb in idxs:
70             if idxa == idxb:
71                 continue
72             dist = abs(arr[idxa] - arr[idxb])
73             if dist == min_dist:
74                 if not [idxb, idxa] in outs:
75                     outs.append([idxa, idxb])
76             elif dist < min_dist:
77                 min_dist = dist
78                 outs = [[idxa, idxb]]
79     if give == "indicies":
80         return outs
81     elif give == "distance":
82         return min_dist
83     else:
84         raise KeyError("give not recognized in closest_pair")
85 
86 
87 def diff(xi, yi, order=1) -> np.ndarray:
88     """Take the numerical derivative of a 1D array.
89 
90     Output is mapped onto the original coordinates  using linear interpolation.
91     Expects monotonic xi values.
92 
93     Parameters
94     ----------
95     xi : 1D array-like
96         Coordinates.
97     yi : 1D array-like
98         Values.
99     order : positive integer (optional)
100         Order of differentiation.
101 
102     Returns
103     -------
104     1D numpy array
105         Numerical derivative. Has the same shape as the input arrays.
106     """
107     yi = np.array(yi).copy()
108     flip = False
109     if xi[-1] < xi[0]:
110         xi = np.flipud(xi.copy())
111         yi = np.flipud(yi)
112         flip = True
113     midpoints = (xi[1:] + xi[:-1]) / 2
114     for _ in range(order):
115         d = np.diff(yi)
116         d /= np.diff(xi)
117         yi = np.interp(xi, midpoints, d)
118     if flip:
119         yi = np.flipud(yi)
120     return yi
121 
122 
123 def fft(xi, yi, axis=0) -> tuple:
124     """Take the 1D FFT of an N-dimensional array and return "sensible" properly shifted arrays.
125 
126     Parameters
127     ----------
128     xi : numpy.ndarray
129         1D array over which the points to be FFT'ed are defined
130     yi : numpy.ndarray
131         ND array with values to FFT
132     axis : int
133         axis of yi to perform FFT over
134 
135     Returns
136     -------
137     xi : 1D numpy.ndarray
138         1D array. Conjugate to input xi. Example: if input xi is in the time
139         domain, output xi is in frequency domain.
140     yi : ND numpy.ndarray
141         FFT. Has the same shape as the input array (yi).
142     """
143     # xi must be 1D
144     if xi.ndim != 1:
145         raise wt_exceptions.DimensionalityError(1, xi.ndim)
146     # xi must be evenly spaced
147     spacing = np.diff(xi)
148     if not np.allclose(spacing, spacing.mean()):
149         raise RuntimeError("WrightTools.kit.fft: argument xi must be evenly spaced")
150     # fft
151     yi = np.fft.fft(yi, axis=axis)
152     d = (xi.max() - xi.min()) / (xi.size - 1)
153     xi = np.fft.fftfreq(xi.size, d=d)
154     # shift
155     xi = np.fft.fftshift(xi)
156     yi = np.fft.fftshift(yi, axes=axis)
157     return xi, yi
158 
159 
160 def joint_shape(*args) -> tuple:
161     """Given a set of arrays, return the joint shape.
162 
163     Parameters
164     ----------
165     args : array-likes
166 
167     Returns
168     -------
169     tuple of int
170         Joint shape.
171     """
172     if len(args) == 0:
173         return ()
174     shape = []
175     shapes = [a.shape for a in args]
176     ndim = args[0].ndim
177     for i in range(ndim):
178         shape.append(max([s[i] for s in shapes]))
179     return tuple(shape)
180 
181 
182 def orthogonal(*args) -> bool:
183     """Determine if a set of arrays are orthogonal.
184 
185     Parameters
186     ----------
187     args : array-likes or array shapes
188 
189     Returns
190     -------
191     bool
192         Array orthogonality condition.
193     """
194     for i, arg in enumerate(args):
195         if hasattr(arg, "shape"):
196             args[i] = arg.shape
197     for s in zip(*args):
198         if np.product(s) != max(s):
199             return False
200     return True
201 
202 
203 def remove_nans_1D(*args) -> tuple:
204     """Remove nans in a set of 1D arrays.
205 
206     Removes indicies in all arrays if any array is nan at that index.
207     All input arrays must have the same size.
208 
209     Parameters
210     ----------
211     args : 1D arrays
212 
213     Returns
214     -------
215     tuple
216         Tuple of 1D arrays in same order as given, with nan indicies removed.
217     """
218     vals = np.isnan(args[0])
219     for a in args:
220         vals |= np.isnan(a)
221     return tuple(np.array(a)[~vals] for a in args)
222 
223 
224 def share_nans(*arrs) -> tuple:
225     """Take a list of nD arrays and return a new list of nD arrays.
226 
227     The new list is in the same order as the old list.
228     If one indexed element in an old array is nan then every element for that
229     index in all new arrays in the list is then nan.
230 
231     Parameters
232     ----------
233     *arrs : nD arrays.
234 
235     Returns
236     -------
237     list
238         List of nD arrays in same order as given, with nan indicies syncronized.
239     """
240     nans = np.zeros(joint_shape(*arrs))
241     for arr in arrs:
242         nans *= arr
243     return tuple([a + nans for a in arrs])
244 
245 
246 def smooth_1D(arr, n=10) -> np.ndarray:
247     """Smooth 1D data by 'running average'.
248 
249     Parameters
250     ----------
251     n : int
252         number of points to average
253     """
254     for i in range(n, len(arr) - n):
255         window = arr[i - n : i + n].copy()
256         arr[i] = window.mean()
257     return arr
258 
259 
260 def svd(a, i=None) -> tuple:
261     """Singular Value Decomposition.
262 
263     Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`
264     are unitary and `s` is a 1D array of `a`'s singular values.
265 
266     Parameters
267     ----------
268     a : array_like
269         Input array.
270     i : int or slice (optional)
271         What singular value "slice" to return.
272         Default is None which returns unitary 2D arrays.
273 
274     Returns
275     -------
276     tuple
277         Decomposed arrays in order `u`, `v`, `s`
278     """
279     u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)
280     u = u.T
281     if i is None:
282         return u, v, s
283     else:
284         return u[i], v[i], s[i]
285 
286 
287 def unique(arr, tolerance=1e-6) -> np.ndarray:
288     """Return unique elements in 1D array, within tolerance.
289 
290     Parameters
291     ----------
292     arr : array_like
293         Input array. This will be flattened if it is not already 1D.
294     tolerance : number (optional)
295         The tolerance for uniqueness.
296 
297     Returns
298     -------
299     array
300         The sorted unique values.
301     """
302     arr = sorted(arr.flatten())
303     unique = []
304     while len(arr) > 0:
305         current = arr[0]
306         lis = [xi for xi in arr if np.abs(current - xi) < tolerance]
307         arr = [xi for xi in arr if not np.abs(lis[0] - xi) < tolerance]
308         xi_lis_average = sum(lis) / len(lis)
309         unique.append(xi_lis_average)
310     return np.array(unique)
311 
312 
313 def valid_index(index, shape) -> tuple:
314     """Get a valid index for a broadcastable shape.
315 
316     Parameters
317     ----------
318     index : tuple
319         Given index.
320     shape : tuple of int
321         Shape.
322 
323     Returns
324     -------
325     tuple
326         Valid index.
327     """
328     # append slices to index
329     index = list(index)
330     while len(index) < len(shape):
331         index.append(slice(None))
332     # fill out, in reverse
333     out = []
334     for i, s in zip(index[::-1], shape[::-1]):
335         if s == 1:
336             if isinstance(i, slice):
337                 out.append(slice(None))
338             else:
339                 out.append(0)
340         else:
341             out.append(i)
342     return tuple(out[::-1])
343 
344 
345 def mask_reduce(mask):
346     """Reduce a boolean mask, removing all false slices in any dimension.
347 
348     Parameters
349     ----------
350     mask : ndarray with bool dtype
351         The mask which is to be reduced
352 
353     Returns
354     -------
355         A boolean mask with no all False slices.
356     """
357     mask = mask.copy()
358     for i in range(len(mask.shape)):
359         a = mask.copy()
360         j = list(range(len(mask.shape)))
361         j.remove(i)
362         j = tuple(j)
363         a = a.max(axis=j, keepdims=True)
364         idx = [slice(None)] * len(mask.shape)
365         a = a.flatten()
366         idx[i] = [k for k in range(len(a)) if a[k]]
367         mask = mask[tuple(idx)]
368     return mask
369 
370 
371 def enforce_mask_shape(mask, shape):
372     """Reduce a boolean mask to fit a given shape.
373 
374     Parameters
375     ----------
376     mask : ndarray with bool dtype
377         The mask which is to be reduced
378     shape : tuple of int
379         Shape which broadcasts to the mask shape.
380 
381     Returns
382     -------
383         A boolean mask, collapsed along axes where the shape given has one element.
384     """
385     red = tuple([i for i in range(len(shape)) if shape[i] == 1])
386     return mask.max(axis=red, keepdims=True)
387
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/WrightTools/kit/_array.py b/WrightTools/kit/_array.py
--- a/WrightTools/kit/_array.py
+++ b/WrightTools/kit/_array.py
@@ -243,18 +243,52 @@
     return tuple([a + nans for a in arrs])
 
 
-def smooth_1D(arr, n=10) -> np.ndarray:
-    """Smooth 1D data by 'running average'.
+def smooth_1D(arr, n=10, smooth_type="flat") -> np.ndarray:
+    """Smooth 1D data using a window function.
+    
+    Edge effects will be present. 
 
     Parameters
     ----------
-    n : int
-        number of points to average
+    arr : array_like
+        Input array, 1D.
+    n : int (optional)
+        Window length.
+    smooth_type : {'flat', 'hanning', 'hamming', 'bartlett', 'blackman'} (optional)
+        Type of window function to convolve data with.
+        'flat' window will produce a moving average smoothing.
+        
+    Returns
+    -------
+    array_like
+        Smoothed 1D array.
     """
-    for i in range(n, len(arr) - n):
-        window = arr[i - n : i + n].copy()
-        arr[i] = window.mean()
-    return arr
+
+    # check array input
+    if arr.ndim != 1:
+        raise wt_exceptions.DimensionalityError(1, arr.ndim)
+    if arr.size < n:
+        message = "Input array size must be larger than window size."
+        raise wt_exceptions.ValueError(message)
+    if n < 3:
+        return arr
+    # construct window array
+    if smooth_type == "flat":
+        w = np.ones(n, dtype=arr.dtype)
+    elif smooth_type == "hanning":
+        w = np.hanning(n)
+    elif smooth_type == "hamming":
+        w = np.hamming(n)
+    elif smooth_type == "bartlett":
+        w = np.bartlett(n)
+    elif smooth_type == "blackman":
+        w = np.blackman(n)
+    else:
+        message = "Given smooth_type, {0}, not available.".format(str(smooth_type))
+        raise wt_exceptions.ValueError(message)
+    # convolve reflected array with window function
+    out = np.convolve(w / w.sum(), arr, mode="same")
+    return out
 
 
 def svd(a, i=None) -> tuple:

{"golden_diff": "diff --git a/WrightTools/kit/_array.py b/WrightTools/kit/_array.py\n--- a/WrightTools/kit/_array.py\n+++ b/WrightTools/kit/_array.py\n@@ -243,18 +243,52 @@\n     return tuple([a + nans for a in arrs])\n \n \n-def smooth_1D(arr, n=10) -> np.ndarray:\n-    \"\"\"Smooth 1D data by 'running average'.\n+def smooth_1D(arr, n=10, smooth_type=\"flat\") -> np.ndarray:\n+    \"\"\"Smooth 1D data using a window function.\n+    \n+    Edge effects will be present. \n \n     Parameters\n     ----------\n-    n : int\n-        number of points to average\n+    arr : array_like\n+        Input array, 1D.\n+    n : int (optional)\n+        Window length.\n+    smooth_type : {'flat', 'hanning', 'hamming', 'bartlett', 'blackman'} (optional)\n+        Type of window function to convolve data with.\n+        'flat' window will produce a moving average smoothing.\n+        \n+    Returns\n+    -------\n+    array_like\n+        Smoothed 1D array.\n     \"\"\"\n-    for i in range(n, len(arr) - n):\n-        window = arr[i - n : i + n].copy()\n-        arr[i] = window.mean()\n-    return arr\n+\n+    # check array input\n+    if arr.ndim != 1:\n+        raise wt_exceptions.DimensionalityError(1, arr.ndim)\n+    if arr.size < n:\n+        message = \"Input array size must be larger than window size.\"\n+        raise wt_exceptions.ValueError(message)\n+    if n < 3:\n+        return arr\n+    # construct window array\n+    if smooth_type == \"flat\":\n+        w = np.ones(n, dtype=arr.dtype)\n+    elif smooth_type == \"hanning\":\n+        w = np.hanning(n)\n+    elif smooth_type == \"hamming\":\n+        w = np.hamming(n)\n+    elif smooth_type == \"bartlett\":\n+        w = np.bartlett(n)\n+    elif smooth_type == \"blackman\":\n+        w = np.blackman(n)\n+    else:\n+        message = \"Given smooth_type, {0}, not available.\".format(str(smooth_type))\n+        raise wt_exceptions.ValueError(message)\n+    # convolve reflected array with window function\n+    out = np.convolve(w / w.sum(), arr, mode=\"same\")\n+    return out\n \n \n def svd(a, i=None) -> tuple:\n", "issue": "test kit.smooth_1D\nwrite tests for `wt.kit.smooth_1D`\n", "before_files": [{"content": "\"\"\"Array interaction tools.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nfrom .. import exceptions as wt_exceptions\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\n__all__ = [\n    \"closest_pair\",\n    \"diff\",\n    \"fft\",\n    \"joint_shape\",\n    \"orthogonal\",\n    \"remove_nans_1D\",\n    \"share_nans\",\n    \"smooth_1D\",\n    \"svd\",\n    \"unique\",\n    \"valid_index\",\n    \"mask_reduce\",\n    \"enforce_mask_shape\",\n]\n\n\n# --- functions -----------------------------------------------------------------------------------\n\n\ndef closest_pair(arr, give=\"indicies\"):\n    \"\"\"Find the pair of indices corresponding to the closest elements in an array.\n\n    If multiple pairs are equally close, both pairs of indicies are returned.\n    Optionally returns the closest distance itself.\n\n    I am sure that this could be written as a cheaper operation. I\n    wrote this as a quick and dirty method because I need it now to use on some\n    relatively small arrays. Feel free to refactor if you need this operation\n    done as fast as possible. - Blaise 2016-02-07\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The array to search.\n    give : {'indicies', 'distance'} (optional)\n        Toggle return behavior. If 'distance', returns a single float - the\n        closest distance itself. Default is indicies.\n\n    Returns\n    -------\n    list of lists of two tuples\n        List containing lists of two tuples: indicies the nearest pair in the\n        array.\n\n        >>> arr = np.array([0, 1, 2, 3, 3, 4, 5, 6, 1])\n        >>> closest_pair(arr)\n        [[(1,), (8,)], [(3,), (4,)]]\n\n    \"\"\"\n    idxs = [idx for idx in np.ndindex(arr.shape)]\n    outs = []\n    min_dist = arr.max() - arr.min()\n    for idxa in idxs:\n        for idxb in idxs:\n            if idxa == idxb:\n                continue\n            dist = abs(arr[idxa] - arr[idxb])\n            if dist == min_dist:\n                if not [idxb, idxa] in outs:\n                    outs.append([idxa, idxb])\n            elif dist < min_dist:\n                min_dist = dist\n                outs = [[idxa, idxb]]\n    if give == \"indicies\":\n        return outs\n    elif give == \"distance\":\n        return min_dist\n    else:\n        raise KeyError(\"give not recognized in closest_pair\")\n\n\ndef diff(xi, yi, order=1) -> np.ndarray:\n    \"\"\"Take the numerical derivative of a 1D array.\n\n    Output is mapped onto the original coordinates  using linear interpolation.\n    Expects monotonic xi values.\n\n    Parameters\n    ----------\n    xi : 1D array-like\n        Coordinates.\n    yi : 1D array-like\n        Values.\n    order : positive integer (optional)\n        Order of differentiation.\n\n    Returns\n    -------\n    1D numpy array\n        Numerical derivative. Has the same shape as the input arrays.\n    \"\"\"\n    yi = np.array(yi).copy()\n    flip = False\n    if xi[-1] < xi[0]:\n        xi = np.flipud(xi.copy())\n        yi = np.flipud(yi)\n        flip = True\n    midpoints = (xi[1:] + xi[:-1]) / 2\n    for _ in range(order):\n        d = np.diff(yi)\n        d /= np.diff(xi)\n        yi = np.interp(xi, midpoints, d)\n    if flip:\n        yi = np.flipud(yi)\n    return yi\n\n\ndef fft(xi, yi, axis=0) -> tuple:\n    \"\"\"Take the 1D FFT of an N-dimensional array and return \"sensible\" properly shifted arrays.\n\n    Parameters\n    ----------\n    xi : numpy.ndarray\n        1D array over which the points to be FFT'ed are defined\n    yi : numpy.ndarray\n        ND array with values to FFT\n    axis : int\n        axis of yi to perform FFT over\n\n    Returns\n    -------\n    xi : 1D numpy.ndarray\n        1D array. Conjugate to input xi. Example: if input xi is in the time\n        domain, output xi is in frequency domain.\n    yi : ND numpy.ndarray\n        FFT. Has the same shape as the input array (yi).\n    \"\"\"\n    # xi must be 1D\n    if xi.ndim != 1:\n        raise wt_exceptions.DimensionalityError(1, xi.ndim)\n    # xi must be evenly spaced\n    spacing = np.diff(xi)\n    if not np.allclose(spacing, spacing.mean()):\n        raise RuntimeError(\"WrightTools.kit.fft: argument xi must be evenly spaced\")\n    # fft\n    yi = np.fft.fft(yi, axis=axis)\n    d = (xi.max() - xi.min()) / (xi.size - 1)\n    xi = np.fft.fftfreq(xi.size, d=d)\n    # shift\n    xi = np.fft.fftshift(xi)\n    yi = np.fft.fftshift(yi, axes=axis)\n    return xi, yi\n\n\ndef joint_shape(*args) -> tuple:\n    \"\"\"Given a set of arrays, return the joint shape.\n\n    Parameters\n    ----------\n    args : array-likes\n\n    Returns\n    -------\n    tuple of int\n        Joint shape.\n    \"\"\"\n    if len(args) == 0:\n        return ()\n    shape = []\n    shapes = [a.shape for a in args]\n    ndim = args[0].ndim\n    for i in range(ndim):\n        shape.append(max([s[i] for s in shapes]))\n    return tuple(shape)\n\n\ndef orthogonal(*args) -> bool:\n    \"\"\"Determine if a set of arrays are orthogonal.\n\n    Parameters\n    ----------\n    args : array-likes or array shapes\n\n    Returns\n    -------\n    bool\n        Array orthogonality condition.\n    \"\"\"\n    for i, arg in enumerate(args):\n        if hasattr(arg, \"shape\"):\n            args[i] = arg.shape\n    for s in zip(*args):\n        if np.product(s) != max(s):\n            return False\n    return True\n\n\ndef remove_nans_1D(*args) -> tuple:\n    \"\"\"Remove nans in a set of 1D arrays.\n\n    Removes indicies in all arrays if any array is nan at that index.\n    All input arrays must have the same size.\n\n    Parameters\n    ----------\n    args : 1D arrays\n\n    Returns\n    -------\n    tuple\n        Tuple of 1D arrays in same order as given, with nan indicies removed.\n    \"\"\"\n    vals = np.isnan(args[0])\n    for a in args:\n        vals |= np.isnan(a)\n    return tuple(np.array(a)[~vals] for a in args)\n\n\ndef share_nans(*arrs) -> tuple:\n    \"\"\"Take a list of nD arrays and return a new list of nD arrays.\n\n    The new list is in the same order as the old list.\n    If one indexed element in an old array is nan then every element for that\n    index in all new arrays in the list is then nan.\n\n    Parameters\n    ----------\n    *arrs : nD arrays.\n\n    Returns\n    -------\n    list\n        List of nD arrays in same order as given, with nan indicies syncronized.\n    \"\"\"\n    nans = np.zeros(joint_shape(*arrs))\n    for arr in arrs:\n        nans *= arr\n    return tuple([a + nans for a in arrs])\n\n\ndef smooth_1D(arr, n=10) -> np.ndarray:\n    \"\"\"Smooth 1D data by 'running average'.\n\n    Parameters\n    ----------\n    n : int\n        number of points to average\n    \"\"\"\n    for i in range(n, len(arr) - n):\n        window = arr[i - n : i + n].copy()\n        arr[i] = window.mean()\n    return arr\n\n\ndef svd(a, i=None) -> tuple:\n    \"\"\"Singular Value Decomposition.\n\n    Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`\n    are unitary and `s` is a 1D array of `a`'s singular values.\n\n    Parameters\n    ----------\n    a : array_like\n        Input array.\n    i : int or slice (optional)\n        What singular value \"slice\" to return.\n        Default is None which returns unitary 2D arrays.\n\n    Returns\n    -------\n    tuple\n        Decomposed arrays in order `u`, `v`, `s`\n    \"\"\"\n    u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)\n    u = u.T\n    if i is None:\n        return u, v, s\n    else:\n        return u[i], v[i], s[i]\n\n\ndef unique(arr, tolerance=1e-6) -> np.ndarray:\n    \"\"\"Return unique elements in 1D array, within tolerance.\n\n    Parameters\n    ----------\n    arr : array_like\n        Input array. This will be flattened if it is not already 1D.\n    tolerance : number (optional)\n        The tolerance for uniqueness.\n\n    Returns\n    -------\n    array\n        The sorted unique values.\n    \"\"\"\n    arr = sorted(arr.flatten())\n    unique = []\n    while len(arr) > 0:\n        current = arr[0]\n        lis = [xi for xi in arr if np.abs(current - xi) < tolerance]\n        arr = [xi for xi in arr if not np.abs(lis[0] - xi) < tolerance]\n        xi_lis_average = sum(lis) / len(lis)\n        unique.append(xi_lis_average)\n    return np.array(unique)\n\n\ndef valid_index(index, shape) -> tuple:\n    \"\"\"Get a valid index for a broadcastable shape.\n\n    Parameters\n    ----------\n    index : tuple\n        Given index.\n    shape : tuple of int\n        Shape.\n\n    Returns\n    -------\n    tuple\n        Valid index.\n    \"\"\"\n    # append slices to index\n    index = list(index)\n    while len(index) < len(shape):\n        index.append(slice(None))\n    # fill out, in reverse\n    out = []\n    for i, s in zip(index[::-1], shape[::-1]):\n        if s == 1:\n            if isinstance(i, slice):\n                out.append(slice(None))\n            else:\n                out.append(0)\n        else:\n            out.append(i)\n    return tuple(out[::-1])\n\n\ndef mask_reduce(mask):\n    \"\"\"Reduce a boolean mask, removing all false slices in any dimension.\n\n    Parameters\n    ----------\n    mask : ndarray with bool dtype\n        The mask which is to be reduced\n\n    Returns\n    -------\n        A boolean mask with no all False slices.\n    \"\"\"\n    mask = mask.copy()\n    for i in range(len(mask.shape)):\n        a = mask.copy()\n        j = list(range(len(mask.shape)))\n        j.remove(i)\n        j = tuple(j)\n        a = a.max(axis=j, keepdims=True)\n        idx = [slice(None)] * len(mask.shape)\n        a = a.flatten()\n        idx[i] = [k for k in range(len(a)) if a[k]]\n        mask = mask[tuple(idx)]\n    return mask\n\n\ndef enforce_mask_shape(mask, shape):\n    \"\"\"Reduce a boolean mask to fit a given shape.\n\n    Parameters\n    ----------\n    mask : ndarray with bool dtype\n        The mask which is to be reduced\n    shape : tuple of int\n        Shape which broadcasts to the mask shape.\n\n    Returns\n    -------\n        A boolean mask, collapsed along axes where the shape given has one element.\n    \"\"\"\n    red = tuple([i for i in range(len(shape)) if shape[i] == 1])\n    return mask.max(axis=red, keepdims=True)\n", "path": "WrightTools/kit/_array.py"}], "after_files": [{"content": "\"\"\"Array interaction tools.\"\"\"\n\n\n# --- import --------------------------------------------------------------------------------------\n\n\nimport numpy as np\n\nfrom .. import exceptions as wt_exceptions\n\n\n# --- define --------------------------------------------------------------------------------------\n\n\n__all__ = [\n    \"closest_pair\",\n    \"diff\",\n    \"fft\",\n    \"joint_shape\",\n    \"orthogonal\",\n    \"remove_nans_1D\",\n    \"share_nans\",\n    \"smooth_1D\",\n    \"svd\",\n    \"unique\",\n    \"valid_index\",\n    \"mask_reduce\",\n    \"enforce_mask_shape\",\n]\n\n\n# --- functions -----------------------------------------------------------------------------------\n\n\ndef closest_pair(arr, give=\"indicies\"):\n    \"\"\"Find the pair of indices corresponding to the closest elements in an array.\n\n    If multiple pairs are equally close, both pairs of indicies are returned.\n    Optionally returns the closest distance itself.\n\n    I am sure that this could be written as a cheaper operation. I\n    wrote this as a quick and dirty method because I need it now to use on some\n    relatively small arrays. Feel free to refactor if you need this operation\n    done as fast as possible. - Blaise 2016-02-07\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The array to search.\n    give : {'indicies', 'distance'} (optional)\n        Toggle return behavior. If 'distance', returns a single float - the\n        closest distance itself. Default is indicies.\n\n    Returns\n    -------\n    list of lists of two tuples\n        List containing lists of two tuples: indicies the nearest pair in the\n        array.\n\n        >>> arr = np.array([0, 1, 2, 3, 3, 4, 5, 6, 1])\n        >>> closest_pair(arr)\n        [[(1,), (8,)], [(3,), (4,)]]\n\n    \"\"\"\n    idxs = [idx for idx in np.ndindex(arr.shape)]\n    outs = []\n    min_dist = arr.max() - arr.min()\n    for idxa in idxs:\n        for idxb in idxs:\n            if idxa == idxb:\n                continue\n            dist = abs(arr[idxa] - arr[idxb])\n            if dist == min_dist:\n                if not [idxb, idxa] in outs:\n                    outs.append([idxa, idxb])\n            elif dist < min_dist:\n                min_dist = dist\n                outs = [[idxa, idxb]]\n    if give == \"indicies\":\n        return outs\n    elif give == \"distance\":\n        return min_dist\n    else:\n        raise KeyError(\"give not recognized in closest_pair\")\n\n\ndef diff(xi, yi, order=1) -> np.ndarray:\n    \"\"\"Take the numerical derivative of a 1D array.\n\n    Output is mapped onto the original coordinates  using linear interpolation.\n    Expects monotonic xi values.\n\n    Parameters\n    ----------\n    xi : 1D array-like\n        Coordinates.\n    yi : 1D array-like\n        Values.\n    order : positive integer (optional)\n        Order of differentiation.\n\n    Returns\n    -------\n    1D numpy array\n        Numerical derivative. Has the same shape as the input arrays.\n    \"\"\"\n    yi = np.array(yi).copy()\n    flip = False\n    if xi[-1] < xi[0]:\n        xi = np.flipud(xi.copy())\n        yi = np.flipud(yi)\n        flip = True\n    midpoints = (xi[1:] + xi[:-1]) / 2\n    for _ in range(order):\n        d = np.diff(yi)\n        d /= np.diff(xi)\n        yi = np.interp(xi, midpoints, d)\n    if flip:\n        yi = np.flipud(yi)\n    return yi\n\n\ndef fft(xi, yi, axis=0) -> tuple:\n    \"\"\"Take the 1D FFT of an N-dimensional array and return \"sensible\" properly shifted arrays.\n\n    Parameters\n    ----------\n    xi : numpy.ndarray\n        1D array over which the points to be FFT'ed are defined\n    yi : numpy.ndarray\n        ND array with values to FFT\n    axis : int\n        axis of yi to perform FFT over\n\n    Returns\n    -------\n    xi : 1D numpy.ndarray\n        1D array. Conjugate to input xi. Example: if input xi is in the time\n        domain, output xi is in frequency domain.\n    yi : ND numpy.ndarray\n        FFT. Has the same shape as the input array (yi).\n    \"\"\"\n    # xi must be 1D\n    if xi.ndim != 1:\n        raise wt_exceptions.DimensionalityError(1, xi.ndim)\n    # xi must be evenly spaced\n    spacing = np.diff(xi)\n    if not np.allclose(spacing, spacing.mean()):\n        raise RuntimeError(\"WrightTools.kit.fft: argument xi must be evenly spaced\")\n    # fft\n    yi = np.fft.fft(yi, axis=axis)\n    d = (xi.max() - xi.min()) / (xi.size - 1)\n    xi = np.fft.fftfreq(xi.size, d=d)\n    # shift\n    xi = np.fft.fftshift(xi)\n    yi = np.fft.fftshift(yi, axes=axis)\n    return xi, yi\n\n\ndef joint_shape(*args) -> tuple:\n    \"\"\"Given a set of arrays, return the joint shape.\n\n    Parameters\n    ----------\n    args : array-likes\n\n    Returns\n    -------\n    tuple of int\n        Joint shape.\n    \"\"\"\n    if len(args) == 0:\n        return ()\n    shape = []\n    shapes = [a.shape for a in args]\n    ndim = args[0].ndim\n    for i in range(ndim):\n        shape.append(max([s[i] for s in shapes]))\n    return tuple(shape)\n\n\ndef orthogonal(*args) -> bool:\n    \"\"\"Determine if a set of arrays are orthogonal.\n\n    Parameters\n    ----------\n    args : array-likes or array shapes\n\n    Returns\n    -------\n    bool\n        Array orthogonality condition.\n    \"\"\"\n    for i, arg in enumerate(args):\n        if hasattr(arg, \"shape\"):\n            args[i] = arg.shape\n    for s in zip(*args):\n        if np.product(s) != max(s):\n            return False\n    return True\n\n\ndef remove_nans_1D(*args) -> tuple:\n    \"\"\"Remove nans in a set of 1D arrays.\n\n    Removes indicies in all arrays if any array is nan at that index.\n    All input arrays must have the same size.\n\n    Parameters\n    ----------\n    args : 1D arrays\n\n    Returns\n    -------\n    tuple\n        Tuple of 1D arrays in same order as given, with nan indicies removed.\n    \"\"\"\n    vals = np.isnan(args[0])\n    for a in args:\n        vals |= np.isnan(a)\n    return tuple(np.array(a)[~vals] for a in args)\n\n\ndef share_nans(*arrs) -> tuple:\n    \"\"\"Take a list of nD arrays and return a new list of nD arrays.\n\n    The new list is in the same order as the old list.\n    If one indexed element in an old array is nan then every element for that\n    index in all new arrays in the list is then nan.\n\n    Parameters\n    ----------\n    *arrs : nD arrays.\n\n    Returns\n    -------\n    list\n        List of nD arrays in same order as given, with nan indicies syncronized.\n    \"\"\"\n    nans = np.zeros(joint_shape(*arrs))\n    for arr in arrs:\n        nans *= arr\n    return tuple([a + nans for a in arrs])\n\n\ndef smooth_1D(arr, n=10, smooth_type=\"flat\") -> np.ndarray:\n    \"\"\"Smooth 1D data using a window function.\n    \n    Edge effects will be present. \n\n    Parameters\n    ----------\n    arr : array_like\n        Input array, 1D.\n    n : int (optional)\n        Window length.\n    smooth_type : {'flat', 'hanning', 'hamming', 'bartlett', 'blackman'} (optional)\n        Type of window function to convolve data with.\n        'flat' window will produce a moving average smoothing.\n        \n    Returns\n    -------\n    array_like\n        Smoothed 1D array.\n    \"\"\"\n\n    # check array input\n    if arr.ndim != 1:\n        raise wt_exceptions.DimensionalityError(1, arr.ndim)\n    if arr.size < n:\n        message = \"Input array size must be larger than window size.\"\n        raise wt_exceptions.ValueError(message)\n    if n < 3:\n        return arr\n    # construct window array\n    if smooth_type == \"flat\":\n        w = np.ones(n, dtype=arr.dtype)\n    elif smooth_type == \"hanning\":\n        w = np.hanning(n)\n    elif smooth_type == \"hamming\":\n        w = np.hamming(n)\n    elif smooth_type == \"bartlett\":\n        w = np.bartlett(n)\n    elif smooth_type == \"blackman\":\n        w = np.blackman(n)\n    else:\n        message = \"Given smooth_type, {0}, not available.\".format(str(smooth_type))\n        raise wt_exceptions.ValueError(message)\n    # convolve reflected array with window function\n    out = np.convolve(w / w.sum(), arr, mode=\"same\")\n    return out\n\n\ndef svd(a, i=None) -> tuple:\n    \"\"\"Singular Value Decomposition.\n\n    Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`\n    are unitary and `s` is a 1D array of `a`'s singular values.\n\n    Parameters\n    ----------\n    a : array_like\n        Input array.\n    i : int or slice (optional)\n        What singular value \"slice\" to return.\n        Default is None which returns unitary 2D arrays.\n\n    Returns\n    -------\n    tuple\n        Decomposed arrays in order `u`, `v`, `s`\n    \"\"\"\n    u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)\n    u = u.T\n    if i is None:\n        return u, v, s\n    else:\n        return u[i], v[i], s[i]\n\n\ndef unique(arr, tolerance=1e-6) -> np.ndarray:\n    \"\"\"Return unique elements in 1D array, within tolerance.\n\n    Parameters\n    ----------\n    arr : array_like\n        Input array. This will be flattened if it is not already 1D.\n    tolerance : number (optional)\n        The tolerance for uniqueness.\n\n    Returns\n    -------\n    array\n        The sorted unique values.\n    \"\"\"\n    arr = sorted(arr.flatten())\n    unique = []\n    while len(arr) > 0:\n        current = arr[0]\n        lis = [xi for xi in arr if np.abs(current - xi) < tolerance]\n        arr = [xi for xi in arr if not np.abs(lis[0] - xi) < tolerance]\n        xi_lis_average = sum(lis) / len(lis)\n        unique.append(xi_lis_average)\n    return np.array(unique)\n\n\ndef valid_index(index, shape) -> tuple:\n    \"\"\"Get a valid index for a broadcastable shape.\n\n    Parameters\n    ----------\n    index : tuple\n        Given index.\n    shape : tuple of int\n        Shape.\n\n    Returns\n    -------\n    tuple\n        Valid index.\n    \"\"\"\n    # append slices to index\n    index = list(index)\n    while len(index) < len(shape):\n        index.append(slice(None))\n    # fill out, in reverse\n    out = []\n    for i, s in zip(index[::-1], shape[::-1]):\n        if s == 1:\n            if isinstance(i, slice):\n                out.append(slice(None))\n            else:\n                out.append(0)\n        else:\n            out.append(i)\n    return tuple(out[::-1])\n\n\ndef mask_reduce(mask):\n    \"\"\"Reduce a boolean mask, removing all false slices in any dimension.\n\n    Parameters\n    ----------\n    mask : ndarray with bool dtype\n        The mask which is to be reduced\n\n    Returns\n    -------\n        A boolean mask with no all False slices.\n    \"\"\"\n    mask = mask.copy()\n    for i in range(len(mask.shape)):\n        a = mask.copy()\n        j = list(range(len(mask.shape)))\n        j.remove(i)\n        j = tuple(j)\n        a = a.max(axis=j, keepdims=True)\n        idx = [slice(None)] * len(mask.shape)\n        a = a.flatten()\n        idx[i] = [k for k in range(len(a)) if a[k]]\n        mask = mask[tuple(idx)]\n    return mask\n\n\ndef enforce_mask_shape(mask, shape):\n    \"\"\"Reduce a boolean mask to fit a given shape.\n\n    Parameters\n    ----------\n    mask : ndarray with bool dtype\n        The mask which is to be reduced\n    shape : tuple of int\n        Shape which broadcasts to the mask shape.\n\n    Returns\n    -------\n        A boolean mask, collapsed along axes where the shape given has one element.\n    \"\"\"\n    red = tuple([i for i in range(len(shape)) if shape[i] == 1])\n    return mask.max(axis=red, keepdims=True)\n", "path": "WrightTools/kit/_array.py"}]}