2

I'm building a wrapper to generate plots in Matplotlib, and I want to be able to optionally specify the axes in which to build the plot.

For example, I have:

def plotContourf(thing, *argv, **kwargs):
    return plt.tricontourf(thing[0], thing[1], thing[2], *argv, **kwargs)

def plotScatter(thing, *argv, **kwargs )
    return plt.scatter(thing[0], thing[1], *argv, **kwargs)

fig, ((ax0,ax1),(ax2,ax3)) = plt.subplots(2,2)

plotContourf(some_thing, axes=ax0)
plotScatter(some_thing, axes=ax2)

Which runs, but everything gets plotted on the very last axes (ax3) and not in the axes specified via the axes kwargument. (No errors here, it just appears on the wrong axes)

For the curious, the reason I want to do this is so that the user can either set an axes, or for the lazy people, they can just call plotContourf() with no specified axes and still get something that they can plt.show()

On the other hand, I tried

def plotContourf(thing, axes=None, *argv, **kwargs):
    if axes is None:
        fig, axes = plt.subplots()

    return axes.tricontourf(thing[0], thing[1], thing[2], *argv, **kwargs)

But then I get:

TypeError: plotContourf() got multiple values for keyword argument 'axes'

I understand that this error is due to 'axes' already being a keyword argument. I know I can use a different keyword but then what's the use of the axes kwarg?

Thanks!

EDIT: Full traceback (for second option described above):

Traceback (most recent call last):
  File "mods.py", line 51, in <module>
    adcirc.plotContourf(hsofs_mesh, -1*hsofs_mesh['depth'], axes=ax0)
TypeError: plotContourf() got multiple values for keyword argument 'axes'

And the actual wrapper:

def plotContourf(grid, axes=None, *argv, **kwargs): 
    if axes is None:
        fig, axes = plt.subplot()
    return axes.tricontourf(grid['lon'], grid['lat'], grid['Elements']-1, *argv, **kwargs)
Reniel Calzada
  • 95
  • 1
  • 11
  • The error isn't due to `axes` being a keyword arg. You are effectively calling `plotContourf(axes=something, exes=somethingelse)`. – Jim Wright Aug 15 '17 at 17:46
  • Can you show the logic where you are getting the error? – Jim Wright Aug 15 '17 at 17:48
  • Apologies, I just noticed a few typos in my question, namely that I had **argv in the return instead of *argv. I just edited to fix it. However, the problem still stands. Can you help me understand better what you mean by your first comment? Also, the only error message I get is TypeError: plotContourf() got multiple values for keyword argument 'axes' and that's only when using my second option. I would like to find the reason why using axes=ax_instance is not working... – Reniel Calzada Aug 15 '17 at 17:57
  • Put the actual stacktrace in the question. – Jim Wright Aug 15 '17 at 17:59
  • Not a solution, but you should use `if axes is None:` or `if not axes:` – Cory Madden Aug 15 '17 at 17:59
  • No stacktrace other than: TypeError: plotContourf() got multiple values for keyword argument 'axes' when using the second option, while the fisrt actually plots, but everything on the last axes (ax3)... @CoryMadden, Yes sorry, that's another typo here, I edited. – Reniel Calzada Aug 15 '17 at 18:04
  • @RenielCalzada Your error should be accompanied by a traceback like in this post: https://stackoverflow.com/questions/45697169/not-able-to-fetch-data-from-quandl-in-python – Cory Madden Aug 15 '17 at 18:08
  • @CoryMadden, I added the Full traceback + the actual function exactly as I have it written in my module. Thanks! – Reniel Calzada Aug 15 '17 at 18:22

2 Answers2

1

The problem is that you're calling the function with -1*hsofs_mesh['depth'] in place of axes and then adding the axes keyword argument again at the end.

In [10]: def fun(a, x=1, *args, **kwargs):
    ...:     print(x)
    ...:     

In [11]: fun(1, 3, x=4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-31b5e42fb4be> in <module>()
----> 1 fun(1, 3, x=4)

TypeError: fun() got multiple values for keyword argument 'x'

See in this example it's reading 3 as x and then I'm passing x=4. Causing the error you are experiencing.

A solution would be to add another argument to your function like this:

def plotContourf(thing, other_thing=None, axes=None, *argv, **kwargs):
Cory Madden
  • 5,026
  • 24
  • 37
1

You might very well keep the axes as a keyword argument, which saves you the trouble of thinking about the order of other arguments.

import matplotlib.pyplot as plt
import numpy as np

def plotContourf(thing, *argv, **kwargs):
    axes = kwargs.pop("axes", None)
    if not axes:
        fig, axes = plt.subplots()

    return axes.tricontourf(thing[0], thing[1], thing[2], *argv, **kwargs)

a = [np.random.rand(10) for i in range(3)]
plotContourf(a) 

#or

fig, ax = plt.subplots()
plotContourf(a, axes=ax)

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • This is actually a very valid point too. In this case I chose the other answer as accepted answer because on my particular case, there is some logic behind the order in which we pass these arguments. I think Cory Madden saw what I was trying to accomplish when he suggested to use other_thing=None. However I do have many other functions where there is no particular logical order in the input arguments so this method will come in handy for those cases. Thanks a lot, I learned something from your suggestion too. – Reniel Calzada Aug 16 '17 at 15:44
  • Sure, the other answer also has some more explanation in it. I added this one, focussing on what the title asked for, i.e. passing the axes as kwarg. But you're completely free to chose which answer to accept. You may still upvote every answer that you find useful (votes will be stored and counted once you have some more reputation). – ImportanceOfBeingErnest Aug 16 '17 at 15:50