TypeError: '<=' not supported between instances of 'int' and 'NoneType' - and controller not sending changes to other functions

@ianhi (hope the ping is not an issue)

I am trying to create a scatter plot that changes the visible points based on a slider (based on the example in the documentation). This is within a wider plot where as the slider changes, I also want only the name of the visible data points to appear and another scatter plot to be affected by the slider value changes.

Thing is, I managed to make this work once. But after that, probably due to changes in the environment or versions, it stopped working in the following ways:

  • I can make the scatter plot that generates the controller change with the slider. But, even though the changes occur, the terminal raises an error every time the slider changes: “TypeError: ‘<=’ not supported between instances of ‘int’ and ‘NoneType’” — this seems related to the alpha setting of the matplotlib artist by looking at the error chain.
  • Now changes in the controller are not sent to the other functions that generate the text or the other scatter plot. So only the original scatter plot changes, but the names don’t change from the ones that first appear, and the other scatter plot is unaffected.

Here is the relevant code

def threshold_sn(x, y, score):
    sn_colors = np.array([list(color) for color in colours_sn])
    sn_colors[:, -1] = 1
    deltas = np.abs(scores_sn - score)
    idx = deltas < 0.01
    deltas /= deltas.max()
    sn_colors[~idx, -1] = np.clip(0.6 - deltas[~idx], 0, 1)
    return sn_colors

def threshold_sl(x, y, score):
    sl_colors = np.array([list(color) for color in colours_sl])
    sl_colors[:, -1] = 1
    deltas = np.abs(scores_sl - score)
    idx = deltas < 0.01
    deltas /= deltas.max()
    sl_colors[~idx, -1] = np.clip(0.6 - deltas[~idx], 0, 1)
    return sl_colors

controls = iplt.scatter(x=sn[:, 0],
                        y=sn[:, 1],
                        s=14,
                        linewidth=0.3,
                        edgecolors='gray',
                        score=(0, 1, 100),
                        c=threshold_sn)

# The below generates the second scatter plot, but it does not change with the slider.
iplt.scatter(x=sl[:, 0], y=sl[:, 1],  s=14, c=threshold_sl, edgecolors='gray', linewidth=0.3, controls=controls) 

scores_sl and scores_sn are lists contaning the scores of each individual data point. I don’t think the issue is there because it has worked before (and still works on the controller scatter plot.

This is in JupyterLab (MacOS), and here is the version number of (maybe) relevant packages:
ipympl = 0.9.3
ipywidgets = 8.0.6
jupyterlab-widgets = 3.0.7
matplotlib = 3.7.1
mpl-interactions = 0.23.0
numpy = 1.25.0
pandas = 2.0.2
python = 3.9
jupyter lab = 4.0.2

Thanks in advance!

@amt can you please provides values (even a small dummy set) for each of the variables:

colours_sn
scores_sn
scores_sl
sn
sl

in order to be able to debug this I need to be able to run the code.

Could you also please post the entire error text that you get?

@ianhi

Here is the complete error code, which appears in the JupyterLab log everytime I move the slider:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/ipywidgets/widgets/widget.py:773, in Widget._handle_msg(self, msg)
    771         if 'buffer_paths' in data:
    772             _put_buffers(state, data['buffer_paths'], msg['buffers'])
--> 773         self.set_state(state)
    775 # Handle a state request.
    776 elif method == 'request_state':

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/ipywidgets/widgets/widget.py:655, in Widget.set_state(self, sync_data)
    652 if name in self.keys:
    653     from_json = self.trait_metadata(name, 'from_json',
    654                                     self._trait_from_json)
--> 655     self.set_trait(name, from_json(sync_data[name], self))

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/contextlib.py:126, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
    124 if typ is None:
    125     try:
--> 126         next(self.gen)
    127     except StopIteration:
    128         return False

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/traitlets/traitlets.py:1502, in HasTraits.hold_trait_notifications(self)
   1500 for changes in cache.values():
   1501     for change in changes:
-> 1502         self.notify_change(change)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/ipywidgets/widgets/widget.py:701, in Widget.notify_change(self, change)
    698     if name in self.keys and self._should_send_property(name, getattr(self, name)):
    699         # Send new state to front-end
    700         self.send_state(key=name)
--> 701 super().notify_change(change)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/traitlets/traitlets.py:1517, in HasTraits.notify_change(self, change)
   1515 def notify_change(self, change):
   1516     """Notify observers of a change event"""
-> 1517     return self._notify_observers(change)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/traitlets/traitlets.py:1564, in HasTraits._notify_observers(self, event)
   1561 elif isinstance(c, EventHandler) and c.name is not None:
   1562     c = getattr(self, c.name)
-> 1564 c(event)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/mpl_interactions/controller.py:213, in Controls.slider_updated(self, change, key, values)
    204 def slider_updated(self, change, key, values):
    205     """Call the slider updated function special casing vmin/vmax.
    206 
    207     Not sure why this is public - users should NOT call this directly.
   (...)
    211     generalize this to any range slider with an underscore in the name?.
    212     """
--> 213     self._slider_updated(change, key, values)
    214     if key == "vmin_vmax":
    215         self._slider_updated({"new": change["new"][0]}, "vmin", values)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/mpl_interactions/controller.py:197, in Controls._slider_updated(self, change, key, values)
    195         ps[k] = self.params[k]
    196         idxs[k] = self.indices[k]
--> 197     f(params=ps, indices=idxs, cache=cache)
    198 for f, params in self._user_callbacks[key]:
    199     f(**{key: self.params[key] for key in params})

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/mpl_interactions/pyplot.py:599, in interactive_scatter.<locals>.update(params, indices, cache)
    597     scatter.set_sizes(s_)
    598 if a_ is not None:
--> 599     scatter.set_alpha(a_)
    600 if isinstance(vmin, Callable):
    601     scatter.norm.vmin = callable_else_value(vmin, param_excluder(params, "vmin"), cache)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/matplotlib/collections.py:815, in Collection.set_alpha(self, alpha)
    803 def set_alpha(self, alpha):
    804     """
    805     Set the transparency of the collection.
    806 
   (...)
    813         supported.
    814     """
--> 815     artist.Artist._set_alpha_for_array(self, alpha)
    816     self._set_facecolor(self._original_facecolor)
    817     self._set_edgecolor(self._original_edgecolor)

File ~/opt/anaconda3/envs/Multi_viz/lib/python3.9/site-packages/matplotlib/artist.py:1041, in Artist._set_alpha_for_array(self, alpha)
   1039     return
   1040 alpha = np.asarray(alpha)
-> 1041 if not (0 <= alpha.min() and alpha.max() <= 1):
   1042     raise ValueError('alpha must be between 0 and 1, inclusive, '
   1043                      f'but min is {alpha.min()}, max is {alpha.max()}')
   1044 self._alpha = alpha

TypeError: '<=' not supported between instances of 'int' and 'NoneType'

And here are 5 values for each variable:

# Colours_sn and colours_sl are lists of RGBA tuples generated through cmap.
colours_sn = [(0.9862, 0.0, 0.0, 1.0),
 (0.9, 0.0, 0.0, 1.0),
 (0.5, 0.0, 0.0, 1.0),
 (0.5, 0.0, 0.0, 1.0),
 (1.0, 0.4, 0.4, 1.0)]

colours_sl = [(0.0, 0.0, 0.4, 1.0),
 (0.6, 0.0, 0.0, 1.0),
 (1.0, 0.9, 0.9, 1.0),
 (0.8, 0.8, 1.0, 1.0),
 (0.9, 0.9, 1.0, 1.0)]

# sn and sl are numpy arrays of the coordinates of the points.
sn = array([[  7.06, -12.97  ],
       [  8.00, -10.20  ],
       [  9.00, -11.21   ],
       [  8.40, -11.73  ],
       [  7.01, -14.48 ]])

sl = array([[ 2.84 , 10.31  ],
       [ 8.27  , 10.42   ],
       [ 1.81  , 12.05   ],
       [ 4.94   , 10.06   ],
       [ 0.97, 12.49  ]])

# scores_sn and scores_sl are lists of values from 0-1 for each point

scores_sn = [0.7, 0.7, 0.9, 0.9, 0.6]
scores_sl = [0.04, 0.9, 0.5, 0.4, 0.4]

Thanks!

I tried running the following complete example and didn’t run into any issues. Can you copy paste exactly the below and run it (after restarting the kernel) to see if it runs without errors for you?

%matplotlib ipympl
import ipywidgets as widgets
import matplotlib.pyplot as plt
import mpl_interactions.ipyplot as iplt
import numpy as np

# Colours_sn and colours_sl are lists of RGBA tuples generated through cmap.
colours_sn = [
    (0.9862, 0.0, 0.0, 1.0),
    (0.9, 0.0, 0.0, 1.0),
    (0.5, 0.0, 0.0, 1.0),
    (0.5, 0.0, 0.0, 1.0),
    (1.0, 0.4, 0.4, 1.0),
]

colours_sl = [
    (0.0, 0.0, 0.4, 1.0),
    (0.6, 0.0, 0.0, 1.0),
    (1.0, 0.9, 0.9, 1.0),
    (0.8, 0.8, 1.0, 1.0),
    (0.9, 0.9, 1.0, 1.0),
]

# sn and sl are numpy arrays of the coordinates of the points.
sn = np.array([[7.06, -12.97], [8.00, -10.20], [9.00, -11.21], [8.40, -11.73], [7.01, -14.48]])

sl = np.array([[2.84, 10.31], [8.27, 10.42], [1.81, 12.05], [4.94, 10.06], [0.97, 12.49]])

# scores_sn and scores_sl are lists of values from 0-1 for each point

scores_sn = [0.7, 0.7, 0.9, 0.9, 0.6]
scores_sl = [0.04, 0.9, 0.5, 0.4, 0.4]


def threshold_sn(x, y, score):
    sn_colors = np.array([list(color) for color in colours_sn])
    sn_colors[:, -1] = 1
    deltas = np.abs(scores_sn - score)
    idx = deltas < 0.01
    deltas /= deltas.max()
    sn_colors[~idx, -1] = np.clip(0.6 - deltas[~idx], 0, 1)
    return sn_colors


def threshold_sl(x, y, score):
    sl_colors = np.array([list(color) for color in colours_sl])
    sl_colors[:, -1] = 1
    deltas = np.abs(scores_sl - score)
    idx = deltas < 0.01
    deltas /= deltas.max()
    sl_colors[~idx, -1] = np.clip(0.6 - deltas[~idx], 0, 1)
    return sl_colors


plt.figure()
controls = iplt.scatter(
    x=sn[:, 0],
    y=sn[:, 1],
    s=14,
    linewidth=0.3,
    edgecolors="gray",
    score=(0, 1, 100),
    c=threshold_sn,
)
# The below generates the second scatter plot, but it does not change with the slider.
iplt.scatter(
    x=sl[:, 0],
    y=sl[:, 1],
    s=14,
    c=threshold_sl,
    edgecolors="gray",
    linewidth=0.3,
    controls=controls,
);

Just tried and run into the same issues…

I do think it has to do with versions of packages or the environment. In fact, I was unable to install mpl using this pip install mpl_interactions["jupyter"] as it says “no matches found” so it might be that dependencies are out of date in my machine? Which are the relevant dependencies and their correct versions?

Also, in case that is important (although again I was able to run this at some point), this is using a Mac M1. I also tried running this in a new environment using Pycharm and run into the same issue with this code, and copying the code snippet from the documentation that does the same thing. However, the basic examples that don’t require color or alpha changes work fine (like those in the documentation changing beta and tau)

Thank you for taking the time again btw

@ianhi
Also it raises the same error even when using code that does not change colours or alpha values — like using the scatter selector widget. It might be something that only affects scatter plots? As soon as I click on a point in the scatter plot, it raises the exact same type error and reference to alpha values.Because line plots examples from the work fine.

After some testing, I found that the issue is resolved when I downgrade mpl-interactions to version 0.22.1.

Hi @amt thank you for sticking with this and debugging. You’ve uncovered a bug!

tldr: fixed in version 0.23.1

After some testing, I found that the issue is resolved when I downgrade mpl-interactions to version 0.22.1.

This was very helpful thank you!

My earlier test of your code worked for me because I had (unwittingly) tested it off an earlier branch rather that latest release. In Fix `vmin_vmax` behavior + add it to scatter by ianhi ¡ Pull Request #274 ¡ mpl-extensions/mpl-interactions ¡ GitHub I must have accidentally formatted something poorly which resulted in this line always being a tuple: https://github.com/mpl-extensions/mpl-interactions/blob/f0c0cd333a570771a64f8b70f921fc89b7b2c517/mpl_interactions/pyplot.py#L569

which seems to have caused all these problems.

I just released version 0.23.1 which works on your example.

Just tested it with the new version and it all works! Thank you very much for your help, the package is really helpful!

1 Like