internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: wright-group/WrightTools

Issue:
datasets demonstrating transform capabilities
reminder to-self

1) Darien's near-THG (already distributed, need to create examples and put into table)
2) Nathan's data from http://pubs.acs.org/doi/abs/10.1021/acs.analchem.7b02917



Discussion:
1) has been added already, has it been added to the table?
@ksunden (1) is in the table.
This is a reminder for me to work on this next week.
@neffmallon Any progress?

Resolving patch:
diff --git a/WrightTools/datasets/__init__.py b/WrightTools/datasets/__init__.py
--- a/WrightTools/datasets/__init__.py
+++ b/WrightTools/datasets/__init__.py
@@ -69,6 +69,7 @@ def _from_directory(self, dirname, prefix=''):
 
 KENT = DatasetContainer()
 KENT._from_directory(os.path.join(here, 'KENT', 'LDS821 TRSF'))
+KENT._from_directory(os.path.join(here, 'KENT', 'LDS821 DOVE'))
 KENT._from_directory(os.path.join(here, 'KENT', 'PbSe 2D delay B'))
 
 ocean_optics = DatasetContainer()
diff --git a/examples/DOVE_transform.py b/examples/DOVE_transform.py
new file mode 100644
--- /dev/null
+++ b/examples/DOVE_transform.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+"""
+DOVE transform
+=================
+
+An example of transform on a dataset from a DOVE experiment.
+"""
+
+import matplotlib.pyplot as plt
+
+import WrightTools as wt
+from WrightTools import datasets
+
+p = datasets.KENT.LDS821_DOVE
+data = wt.data.from_KENT(p, ignore=['d1', 'd2', 'wm'], verbose=False)
+
+fig, gs = wt.artists.create_figure(width='double', cols=[1, 1, 'cbar'],
+                                   wspace=0.7)
+
+# as taken
+ax = plt.subplot(gs[0, 0])
+data.transform('w2', 'w1')
+ax.pcolor(data)
+wt.artists.set_ax_labels(xlabel=data.w2.label, ylabel=data.w1.label)
+ax.grid()
+ax.set_title('as taken', fontsize=20)
+
+# transformed
+ax = plt.subplot(gs[0, 1])
+data.transform('w2', 'w1-w2')
+ax.pcolor(data)
+wt.artists.set_ax_labels(xlabel=data.w2.label)
+ax.grid()
+ax.set_title('transformed', fontsize=20)
+
+# colorbar
+cax = plt.subplot(gs[0, -1])
+wt.artists.plot_colorbar(cax, label='Intensity')


Test patch:
Repository: wright-group/WrightTools

Issue:
citation convention
We should have a clear way to cite WrightTools. Will this be done once it is published in the initial paper?


Discussion:
This will certainly be part of the publication, however this is a good point, we should consider adding a section to the README and documentation regarding citation.

We will at some point need to obtain a DOI (This is required by JOSS, as the DOI that joss issues is a Cross-Reference DOI)

We could consider [Zenodo](https://zenodo.org) 
Zenodo also supports versioning of DOIs, such that the project as a whole has one DOI, but then each release has an independent DOI
Also see [Making Your Code Citable](https://guides.github.com/activities/citable-code/).

In think I would prefer to direct to PyPI rather than github, but I'm not too confident in my preference.
[potentially useful convention](https://github.com/chrysn/aiocoap/blob/master/setup.py#L45-L71)

Resolving patch:
diff --git a/WrightTools/__citation__.py b/WrightTools/__citation__.py
new file mode 100644
--- /dev/null
+++ b/WrightTools/__citation__.py
@@ -0,0 +1,11 @@
+"""Define WrightTools citation."""
+
+import pathlib
+
+__all__ = ["__citation__"]
+
+here = pathlib.Path(__file__).parent
+
+with here / "CITATION" as f:
+    # remove extra whitespace
+    __citation__ = " ".join(f.read_text().split())
diff --git a/WrightTools/__init__.py b/WrightTools/__init__.py
--- a/WrightTools/__init__.py
+++ b/WrightTools/__init__.py
@@ -7,6 +7,7 @@
 
 import sys as _sys
 
+from .__citation__ import *
 from .__version__ import *
 from .__wt5_version__ import *
 from . import artists


Test patch:
diff --git a/tests/citation.py b/tests/citation.py
new file mode 100644
--- /dev/null
+++ b/tests/citation.py
@@ -0,0 +1,7 @@
+"""Test citation."""
+
+import WrightTools as wt
+
+
+def test_existence():
+    assert wt.__citation__
Repository: wright-group/WrightTools

Issue:
tree entry point 
entry point from terminal to `print_tree`, accepting a wt5 file as argument `wttree`



Discussion:

Resolving patch:
diff --git a/WrightTools/__main__.py b/WrightTools/__main__.py
new file mode 100644
--- /dev/null
+++ b/WrightTools/__main__.py
@@ -0,0 +1,29 @@
+# --- import --------------------------------------------------------------------------------------
+
+
+import WrightTools as wt
+import argparse
+
+
+# --- define --------------------------------------------------------------------------------------
+
+
+def wt_tree():
+    parser = argparse.ArgumentParser(description="Print a given data tree.")
+
+    # Add arguments
+    parser.add_argument("path", type=str)
+    parser.add_argument("--verbose", "-v", action="store_true", help="Verbose? False by default")
+    parser.add_argument("--depth", "-d", "-L", type=int, default=9, help="depth to print (int)")
+    parser.add_argument("internal_path", nargs="?", default="/")
+    args = parser.parse_args()
+
+    # Create the data/collection object
+    obj = wt.open(args.path, edit_local=True)[args.internal_path]
+
+    # Print the tree
+    # If the object is a data object, it doesn't take depth as a parameter
+    if isinstance(obj, wt.Data):
+        obj.print_tree(verbose=args.verbose)
+    else:
+        obj.print_tree(verbose=args.verbose, depth=args.depth)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -62,6 +62,7 @@ def read(fname):
     license="MIT",
     url="http://wright.tools",
     keywords="spectroscopy science multidimensional visualization",
+    entry_points={"console_scripts": ["wt-tree=WrightTools.__main__:wt_tree"]},
     classifiers=[
         "Development Status :: 5 - Production/Stable",
         "Intended Audience :: Science/Research",


Test patch:
Repository: wright-group/WrightTools

Issue:
`data.variables` list rather than tuple
Documentation reads that it returns tuple, and that is consistent with channel and axis behavior



Discussion:

Resolving patch:
diff --git a/WrightTools/data/_data.py b/WrightTools/data/_data.py
--- a/WrightTools/data/_data.py
+++ b/WrightTools/data/_data.py
@@ -170,7 +170,7 @@ def variables(self):
         except (AssertionError, AttributeError):
             self._variables = [self[n] for n in self.variable_names]
         finally:
-            return self._variables
+            return tuple(self._variables)
 
     @property
     def _leaf(self):
@@ -525,7 +525,7 @@ def create_variable(self, name, values=None, shape=None, units=None, **kwargs):
         id = self.require_dataset(name=name, data=values, shape=shape, dtype=dtype).id
         variable = Variable(self, id, units=units, **kwargs)
         # finish
-        self.variables.append(variable)
+        self._variables = None
         self.attrs["variable_names"] = np.append(self.attrs["variable_names"], name.encode())
         return variable
 


Test patch:
Repository: wright-group/WrightTools

Issue:
CITATION text file not distributed



Discussion:
when installed from wheel, import fails due to lack of CITATION text file

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -18,6 +18,7 @@ def read(fname):
         "datasets/*/*",
         "datasets/*/*/*",
         "datasets/*/*/*/*",
+        "CITATION",
         "VERSION",
         "WT5_VERSION",
     ]


Test patch:
Repository: wright-group/WrightTools

Issue:
Spelling error default
https://github.com/wright-group/WrightTools/blob/6943ce95436f054836a8c6c871957ed8b7698b51/WrightTools/artists/_colors.py#L108


Discussion:

Resolving patch:
diff --git a/WrightTools/artists/_colors.py b/WrightTools/artists/_colors.py
--- a/WrightTools/artists/_colors.py
+++ b/WrightTools/artists/_colors.py
@@ -105,7 +105,7 @@ def make_colormap(seq, name="CustomMap", plot=False):
     name : string (optional)
         A name for the colormap
     plot : boolean (optional)
-        Use to generate a plot of the colormap (Defalut is False).
+        Use to generate a plot of the colormap (Default is False).
 
     Returns
     -------


Test patch:
Repository: wright-group/WrightTools

Issue:
Add simple SVD tool to kit
I find myself doing simple SVDs a lot. We should add a tool to `kit` and then plan to add multidimensional/data support later.

example of what this method might look like:
```
def SVD_doer(arr, i=None):
    u, s, v = np.linalg.svd(arr , full_matrices=False, compute_uv=True)
    u = u.T
    if i == None:
        return u, v, s
    else:
        return u[i], v[i], s[i]
```


Discussion:
See #127 
see #30 

Resolving patch:
diff --git a/WrightTools/kit/_array.py b/WrightTools/kit/_array.py
--- a/WrightTools/kit/_array.py
+++ b/WrightTools/kit/_array.py
@@ -21,6 +21,7 @@
     "remove_nans_1D",
     "share_nans",
     "smooth_1D",
+    "svd",
     "unique",
     "valid_index",
 ]
@@ -215,7 +216,7 @@ def remove_nans_1D(*args):
     vals = np.isnan(args[0])
     for a in args:
         vals |= np.isnan(a)
-    return tuple(np.array(a)[vals == False] for a in args)
+    return tuple(np.array(a)[~vals] for a in args)
 
 
 def share_nans(*arrs):
@@ -254,6 +255,33 @@ def smooth_1D(arr, n=10):
     return arr
 
 
+def svd(a, i=None):
+    """Singular Value Decomposition.
+
+    Factors the matrix `a` as ``u * np.diag(s) * v``, where `u` and `v`
+    are unitary and `s` is a 1D array of `a`'s singular values.
+
+    Parameters
+    ----------
+    a : array_like
+        Input array.
+    i : int or slice (optional)
+        What singular value "slice" to return.
+        Default is None which returns unitary 2D arrays.
+
+    Returns
+    -------
+    tuple
+        Decomposed arrays in order `u`, `v`, `s`
+    """
+    u, s, v = np.linalg.svd(a, full_matrices=False, compute_uv=True)
+    u = u.T
+    if i is None:
+        return u, v, s
+    else:
+        return u[i], v[i], s[i]
+
+
 def unique(arr, tolerance=1e-6):
     """Return unique elements in 1D array, within tolerance.
 


Test patch:
diff --git a/tests/kit/svd.py b/tests/kit/svd.py
new file mode 100644
--- /dev/null
+++ b/tests/kit/svd.py
@@ -0,0 +1,40 @@
+"""Test svd."""
+
+import numpy as np
+import WrightTools as wt
+
+
+def test_int():
+    arr1 = np.linspace(0, 1, 20)
+    arr2 = np.linspace(0, 1, 21)
+    arr = arr1[:, None] * arr2[None, :]
+    u, v, s = wt.kit.svd(arr, i=0)
+    assert u.shape == (20,)
+    assert v.shape == (21,)
+    assert s.shape == ()
+
+
+def test_reconstruction():
+    arr1 = np.linspace(0, 1, 20)
+    arr2 = np.linspace(0, 1, 21)
+    arr = arr1[:, None] * arr2[None, :]
+    U, V, s = wt.kit.svd(arr, i=None)
+    U = U.T
+    S = np.diag(s)
+    assert np.allclose(arr, np.dot(U, np.dot(S, V)))
+
+
+def test_slice():
+    arr1 = np.linspace(0, 1, 20)
+    arr2 = np.linspace(0, 1, 21)
+    arr = arr1[:, None] * arr2[None, :]
+    u, v, s = wt.kit.svd(arr, i=slice(1, 5))
+    assert u.shape == (4, 20)
+    assert v.shape == (4, 21)
+    assert s.shape == (4,)
+
+
+if __name__ == "__main__":
+    test_int()
+    test_reconstruction()
+    test_slice()
Repository: wright-group/WrightTools

Issue:
Label generation inadvertently replacing more characters than desired
https://github.com/wright-group/WrightTools/blob/5ca0358e93698b39f4dfc15bf194211a9c30c94a/WrightTools/data/_axis.py#L96

```python
>>> zero
<WrightTools.Data 'zero' ('t',) at /tmp/p1jv7pfv.wt5::/zero>
>>> zero.t.label
'$\\ma\\tauhsf{\\tau\\,\\left(ps\\right)}$'
```

Thus, if I want a short time axis name (t), the label will fail when passed through LaTeX.

Label building should call replace on a string specific to that axis (without `mathsf`), and probably needs to be even more aware of things like variables which have the same prefix.

P.S. yes I know i should use longer names for my variables, but still, should not be an error.


Discussion:

Resolving patch:
diff --git a/WrightTools/data/_axis.py b/WrightTools/data/_axis.py
--- a/WrightTools/data/_axis.py
+++ b/WrightTools/data/_axis.py
@@ -89,7 +89,7 @@ def identity(self):
     @property
     def label(self):
         symbol = wt_units.get_symbol(self.units)
-        label = r"$\mathsf{" + self.expression
+        label = self.expression
         for v in self.variables:
             vl = "%s_{%s}" % (symbol, v.label)
             vl = vl.replace("_{}", "")  # label can be empty, no empty subscripts
@@ -102,7 +102,7 @@ def label(self):
             label += r"\right)"
         else:
             pass
-        label += r"}$"
+        label = r"$\mathsf{%s}$" % label
         return label
 
     @property


Test patch:
diff --git a/tests/data/labels.py b/tests/data/labels.py
new file mode 100755
--- /dev/null
+++ b/tests/data/labels.py
@@ -0,0 +1,15 @@
+#! /usr/bin/env python3
+
+import WrightTools as wt
+import numpy as np
+
+
+def test_short():
+    data = wt.Data()
+    data.create_variable("t", np.linspace(0, 10, 10), units="fs")
+    data.transform("t")
+    assert data.axes[0].label == r"$\mathsf{\tau\,\left(fs\right)}$"
+
+
+if __name__ == "__main__":
+    test_short()
Repository: wright-group/WrightTools

Issue:
Full support for Axis units of `None`
```
import WrightTools as wt
from WrightTools import datasets

d = wt.data.from_Solis(datasets.Solis.wm_ypos_fluorescence_with_filter)
wt.artists.interact2D(d)
```
Raises a `KeyError` 
```

  File "c:\users\dan\google drive\source\wrighttools\WrightTools\artists\_interact.py", line 216, in interact2D
    verbose=False,

  File "c:\users\dan\google drive\source\wrighttools\WrightTools\data\_data.py", line 369, in chop
    data.axes[j].convert(units)

  File "c:\users\dan\google drive\source\wrighttools\WrightTools\data\_axis.py", line 178, in convert
    valid = list(wt_units.dicts[kind].keys())

KeyError: None
```



Discussion:

Resolving patch:
diff --git a/WrightTools/data/_axis.py b/WrightTools/data/_axis.py
--- a/WrightTools/data/_axis.py
+++ b/WrightTools/data/_axis.py
@@ -170,9 +170,10 @@ def convert(self, destination_units, *, convert_variables=False):
         convert_variables : boolean (optional)
             Toggle conversion of stored arrays. Default is False.
         """
+        if self.units is None and (destination_units is None or destination_units == "None"):
+            return
         if not wt_units.is_valid_conversion(self.units, destination_units):
-            kind = wt_units.kind(self.units)
-            valid = list(wt_units.dicts[kind].keys())
+            valid = wt_units.get_valid_conversions(self.units)
             raise wt_exceptions.UnitsError(valid, destination_units)
         if convert_variables:
             for v in self.variables:
diff --git a/WrightTools/units.py b/WrightTools/units.py
--- a/WrightTools/units.py
+++ b/WrightTools/units.py
@@ -159,7 +159,10 @@ def get_symbol(units):
 
 
 def get_valid_conversions(units):
-    valid = list(dicts[kind(units)])
+    try:
+        valid = list(dicts[kind(units)])
+    except KeyError:
+        return ()
     valid.remove(units)
     return tuple(valid)
 


Test patch:
Repository: wright-group/WrightTools

Issue:
Group is not defined in collection
https://github.com/wright-group/WrightTools/blob/ca056aa600f341501a99d2ea4d11f7d74047bc26/WrightTools/_open.py#L48

Statement will cause an attribute error. Not tested currently


Discussion:
Not all that critical as it is generally recommended to use Collection objects for groups, but would like to not fail in the case that, for instance, data objects are nested within pure hdf groups/structures.

Resolving patch:
diff --git a/WrightTools/_open.py b/WrightTools/_open.py
--- a/WrightTools/_open.py
+++ b/WrightTools/_open.py
@@ -10,6 +10,7 @@
 
 from . import collection as wt_collection
 from . import data as wt_data
+from . import _group as wt_group
 
 
 # --- define -------------------------------------------------------------------------------------
@@ -45,4 +46,4 @@ def open(filepath, edit_local=False):
     elif class_name == "Collection":
         return wt_collection.Collection(filepath=filepath, name=name, edit_local=edit_local)
     else:
-        return wt_collection.Group(filepath=filepath, name=name, edit_local=edit_local)
+        return wt_group.Group(filepath=filepath, name=name, edit_local=edit_local)


Test patch:
Repository: wright-group/WrightTools

Issue:
 MatplotlibDeprecationWarning: The 'box-forced' keyword argument is deprecated since 2.2.
matplotlib.axes.Axes.set_aspect no longer accepts `adjustable = "box-forced"` (as it did in 2.0.0, see https://matplotlib.org/2.0.0/api/_as_gen/matplotlib.axes.Axes.set_aspect.html).

Current documentation is https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.set_aspect.html

Does not appear to be resulting in incorrectly rendered figures at this time.


Discussion:
Note, we call set_adjustable directly
```
[kyle@osmium WrightTools]$ grep -i box-forced -r --include "*.py" -n
artists/_helpers.py:133:    axCorr.set_adjustable("box-forced")
artists/_base.py:139:        ax.set_adjustable("box-forced")
```

https://github.com/matplotlib/matplotlib/pull/10033
This is the PR which made this change, they say just "box" will work as we need it for versions after this point.

I want to look into the changes a little more deeply before making changes. It is not clear when this will be removed from matplotlib (if at all) completely. may be a case of just silencing the warning if it will not be removed.

Also, the two places where this is called are accomplishing the same task, perhaps this is a time to refactor so one calls the other?
My leaning on this particular issue is to push off until after we have tests for artists (see #667).
That way, we can be sure that this change does not cause unintended changes to the artists as a whole.

We now require 2.2 and above only anyway, so this will have no effect except removing the warning by changing to "box"

Resolving patch:
diff --git a/WrightTools/artists/_base.py b/WrightTools/artists/_base.py
--- a/WrightTools/artists/_base.py
+++ b/WrightTools/artists/_base.py
@@ -190,7 +190,7 @@ def add_sideplot(self, along, pad=0, height=0.75, ymin=0, ymax=1.1):
         elif along == "y":
             ax.set_xlim(ymin, ymax)
         ax.autoscale(enable=False)
-        ax.set_adjustable("box-forced")
+        ax.set_adjustable("box")
         ax.is_sideplot = True
         plt.setp(ax.get_xticklabels(), visible=False)
         plt.setp(ax.get_yticklabels(), visible=False)
diff --git a/WrightTools/artists/_helpers.py b/WrightTools/artists/_helpers.py
--- a/WrightTools/artists/_helpers.py
+++ b/WrightTools/artists/_helpers.py
@@ -131,7 +131,7 @@ def add_sideplot(
     elif along == "y":
         axCorr = divider.append_axes("right", height, pad=pad, sharey=ax)
     axCorr.autoscale(False)
-    axCorr.set_adjustable("box-forced")
+    axCorr.set_adjustable("box")
     # bin
     if arrs_to_bin is not None:
         xi, yi, zi = arrs_to_bin


Test patch:
Repository: wright-group/WrightTools

Issue:
Kit leastsq should not except BaseException
https://github.com/wright-group/WrightTools/blob/f22920579f45632b4123661d9832ff0cc1b614c4/WrightTools/kit/_leastsq.py#L74

The exception caught should be limited to those known to be raised inside.


Discussion:
@untzag @darienmorrow Dow we know what exception this _should_ be catching?
(or exceptions)
I have no idea. I think the idea is to handle any wonky values in the covariance matrix (e.g. inf, -inf), but I don't have good reason to think those would pop up. I will note that the original idea/code come from [this stackoverflow thread](https://stackoverflow.com/questions/14581358/getting-standard-errors-on-fitted-parameters-using-the-optimize-leastsq-method-i/21844726#21844726). 

I think it should be probably just be a `try` `except` instead of using `BaseException`. 


Note that just try/except is functionally identical to try/except BaseException. Ideally we know what exceptions may arise from the try block, and only handle those
np.absolute and append should not throw errors, as long as we give absolute numeric data.

The other things that may cause errors have to do with indexing pcov, which is `None` if a singular matrix is encountered according to [scipy docs](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html). Our code will set it to np.inf in that case, then indexing into it will generate an error.

Resolving patch:
diff --git a/WrightTools/kit/_leastsq.py b/WrightTools/kit/_leastsq.py
--- a/WrightTools/kit/_leastsq.py
+++ b/WrightTools/kit/_leastsq.py
@@ -71,7 +71,7 @@ def errfunc(p, x, y):
         for i in range(len(pfit_leastsq)):
             try:
                 error.append(np.absolute(pcov[i][i]) ** 0.5)
-            except BaseException:
+            except IndexError:
                 error.append(0.00)
         perr_leastsq = np.array(error)
     # exit


Test patch:
Repository: wright-group/WrightTools

Issue:
Backport #700 to 2.X_backports (removed variable upstream)
#700 

Current 2.X backports cannot run on matplotlib 3


Discussion:

Resolving patch:
diff --git a/WrightTools/artists.py b/WrightTools/artists.py
--- a/WrightTools/artists.py
+++ b/WrightTools/artists.py
@@ -633,7 +633,7 @@ def add_subplot(self, *args, **kwargs):
         self._axstack.add(key, a)
         self.sca(a)
         if int(matplotlib.__version__.split('.')[0]) > 1:
-            a._remove_method = self.__remove_ax
+            a._remove_method = lambda ax: self.delaxes(ax)
             self.stale = True
             a.stale_callback = matplotlib.figure._stale_figure_callback
         # finish


Test patch:
Repository: wright-group/WrightTools

Issue:
Interact2D fails when passed certain types of transformed data
Error:
```
File "/home/darien/source/WrightTools/WrightTools/artists/_interact.py", line 147, in interact2D
    varis.remove(v.natural_name)
ValueError: list.remove(x): x not in list
```
Examples:

```
import WrightTools as wt
import numpy as np
from WrightTools import datasets

if True:
    # arrays
    x = np.linspace(0,1,10)[:, None]
    y = np.linspace(0,1,11)[None, :]
    z = x * y
    # create data object    
    d = wt.Data(name='example')
    d.create_variable('x', values=x)
    d.create_variable('y', values=y)
    d.create_channel('z', values=z)
    # case 1
    # works
    d.transform('x', 'y')
    wt.artists.interact2D(d, xaxis=-1, yaxis=-2, channel='z', verbose=False)
    wt.artists.quick2D(d)
    # case 2
    d.transform('x', 'x-y')
    wt.artists.quick2D(d)
    # breaks
    wt.artists.interact2D(d, xaxis=-1, yaxis=-2, channel='z', verbose=False)
    
    
if False:
    p = datasets.KENT.LDS821_DOVE
    data = wt.data.from_KENT(p, ignore=["d1", "d2", "wm"], verbose=False)
    # works 
    data.transform("w2", "w1")
    wt.artists.interact2D(data, verbose=False)
    # breaks
    data.transform("w2", "w1-w2")
    wt.artists.interact2D(data, verbose=False)
```


Discussion:
suspicions are things to do with representing as "x-y" vs "x__m__y" or something to do with the fact that "x" appears in two different axis expressions. These are allowed by underlying methods, however, so it is something within interact that is causing the problem

Resolving patch:
diff --git a/WrightTools/artists/_interact.py b/WrightTools/artists/_interact.py
--- a/WrightTools/artists/_interact.py
+++ b/WrightTools/artists/_interact.py
@@ -144,7 +144,10 @@ def interact2D(data, xaxis=0, yaxis=1, channel=0, local=False, verbose=True):
     varis = list(data.variable_names)
     for ax in data.axes:
         for v in ax.variables:
-            varis.remove(v.natural_name)
+            try:
+                varis.remove(v.natural_name)
+            except ValueError:
+                pass  # Already removed, can't double count
     for v in varis:
         data.remove_variable(v, implied=False, verbose=False)
     xaxis, yaxis = get_axes(data, [xaxis, yaxis])


Test patch: