Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib "dictionary changed size during iteration" error when creating subplot

I wrote a function that plots a figure consisting of two subplots of different sizes:

def draw_plot(data, function, sigma_value):

    gs = gridspec.GridSpec(1, 5)
    ax1 = subplot(gs[0, 0:3])  
    ax2 = subplot(gs[0, 3:5], sharey=ax1)                
    gs.update(wspace=0.05)
    ...

I should mention that this is a module-level function, so at the top of that module I make imports

from pylab import *
import matplotlib.gridspec as gridspec

When I run myplot.draw_plot(...), I get RuntimeError. The thing is this behaviour is inconsistent. I can call the function, say, three times, and the first two times I get the error, whereas the third time it runs OK.

The Traceback is

Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "myplot.py", line 105, in draw_plot
    ax1 = subplot(gs[0, 0:3])                 
File "C:\Python32\lib\site-packages\matplotlib\pyplot.py", line 766, in subplot
    a = fig.add_subplot(*args, **kwargs)
File "C:\Python32\lib\site-packages\matplotlib\figure.py", line 779, in add_subplot
    a = subplot_class_factory(projection_class)(self, *args, **kwargs)
File "C:\Python32\lib\site-packages\matplotlib\axes.py", line 8380, in __init__
    self._axes_class.__init__(self, fig, self.figbox, **kwargs)
File "C:\Python32\lib\site-packages\matplotlib\axes.py", line 467, in __init__
    self.cla()
File "C:\Python32\lib\site-packages\matplotlib\axes.py", line 910, in cla
    self._shared_y_axes.clean()
File "C:\Python32\lib\site-packages\matplotlib\cbook.py", line 1493, in clean
    for key, val in mapping.items():
RuntimeError: dictionary changed size during iteration

Thanks for any help!

EDIT

Obviously I've been trying to figure out myself what is going on, so following the Traceback I checked the clean() function in cbook.py.

def clean(self):
    """
    Clean dead weak references from the dictionary
    """
    mapping = self._mapping
    for key, val in mapping.items():
        if key() is None:
            del mapping[key]
            val.remove(key)

In the function I added a line that would print mapping.items() and I noticed that the error occurs when there are entries similar to <weakref at 0480EBA0; dead> among those items. I'm totally unfamiliar with weak references so I'm stuck again.

EDIT 2 It's certainly not a good solution but commenting out the clean() function body helps in my case without producing any new errors.

like image 294
petru Avatar asked Sep 12 '12 20:09

petru


2 Answers

I've just found a very recent post Safely iterating over WeakKeyDictionary and WeakValueDictionary that helps with a very similar issue.

So using answer given by Bakuriu I edited close() function as following

def clean(self):
    """
    Clean dead weak references from the dictionary
    """

    mapping = self._mapping
    for key, val in list(mapping.items()):  # iterate over list now        
        if key() is None:
            del mapping[key]
            val.remove(key)

and it seems to be working just fine!

EDIT

I've just found out that in a new version of matplotlib the function looks like this:

def clean(self):
    """
    Clean dead weak references from the dictionary
    """
    mapping = self._mapping
    to_drop = [key for key in mapping if key() is None]
    for key in to_drop:
        val = mapping.pop(key)
        val.remove(key)

Source:

https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/cbook.py

like image 133
petru Avatar answered Sep 29 '22 10:09

petru


Just a short explanation of what is happening:

You looped over an iterable (list, dict, whatever) like this:

for somevalue in someiterable:
    #do something

And inside the loop you tried to structurally modify the iterable, meaning you added or removed values. This is not allowed because it would mess with the for loop. The solution to this is usually to iterate over a copy of the iterable, leaving you free to altering the original.

like image 34
l4mpi Avatar answered Sep 29 '22 11:09

l4mpi