Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug in option to pass an axis to axelrod.Plot.boxplot #896

Closed
drvinceknight opened this issue Mar 11, 2017 · 5 comments
Closed

Bug in option to pass an axis to axelrod.Plot.boxplot #896

drvinceknight opened this issue Mar 11, 2017 · 5 comments
Labels

Comments

@drvinceknight
Copy link
Member

drvinceknight commented Mar 11, 2017

(EDIT: Fix on issue-984 branch. Commit hash: 90be7ca)

For a given tournament:

>>> import axelrod as axl
>>> axl.seed(0)
>>> players = [s() for s in axl.demo_strategies]
>>> tournament = axl.Tournament(players)
>>> results = tournament.play()

We can obtain a plot using:

>>> plot = axl.Plot(results)
>>> plot.boxplot()

We get:

download

We have the option to pass a Matplotlib axes object so we could theoretically plot multiple things together. For example here's a plot of a straight line and a plot of the tournament results:

>>> import matplotlib.pyplot as plt
>>> fig, axarr = plt.subplots(2, 2)
>>> plot.boxplot(ax=axarr[1, 1])
>>> axarr[0, 1].plot([1, 2, 3], [1, 2, 3])

download 2

If we change that slightly to move the plots around:

>>> fig, axarr = plt.subplots(2, 2)
>>> plot.boxplot(ax=axarr[0, 0])  # the violonplots SHOULD be in top left subplot
>>> axarr[1, 0].plot([1, 2, 3], [1, 2, 3])

download 3

We see that the line plot has moved as expected but that the boxplot has not. This is due to the following lines in axelrod.plot.Plot:

>> 49     def _violinplot(                                                                                                                            
>> 50         self, data: dataType, names: namesType, title: titleType = None,                                                                        
>> 51         ax: matplotlib.axes.SubplotBase = None                                                                                                  
   52     ) -> matplotlib.figure.Figure:                                                                                                              
   53         """For making violinplots."""                                                                                                           
   54         if not self.matplotlib_installed:  # pragma: no cover                                                                                   
   55             return None                                                                                                                         
   56                                                                                                                                                 
   57         if ax is None:                                                                                                                          
   58             _, ax = plt.subplots()                                                                                                              
   59         else:                                                                                                                                   
   60             ax = ax                                                                                                                             
   61                                                                                                                                                 
   62         figure = ax.get_figure()                                                                                                                
   63         width = max(self.nplayers / 3, 12)                                                                                                      
   64         height = width / 2                                                                                                                      
   65         spacing = 4                                                                                                                             
   66         positions = spacing * arange(1, self.nplayers + 1, 1)                                                                                   
   67         figure.set_size_inches(width, height)                                                                                                   
   68         plt.violinplot(data, positions=positions, widths=spacing / 2,                                                                           
   69                        showmedians=True, showextrema=False)                                                                                     
   70         plt.xticks(positions, names, rotation=90)                                                                                               
   71         plt.xlim(0, spacing * (self.nplayers + 1))                                                                                              
   72         plt.tick_params(axis='both', which='both', labelsize=8)                                                                                 
   73         if title:                                                                                                                               
   74             plt.title(title)                                                                                                                    
   75         return figure   

We are picking up the axes properly but we're then plotting on plt (see line 68 etc) and not on the axis in question.

plt which is an imported alias for matplotlib.pyplot is a wrapper for the fig, ax api and if used in conjunction with it, plots (always) on the last subfigure.

It is not as simple as simply changing all plts to ax (because plt is a wrapper). This is the closest I came to fixing this:

>> 49     def _violinplot(                                                                                                                            
>> 50         self, data: dataType, names: namesType, title: titleType = None,                                                                        
>> 51         ax: matplotlib.axes.SubplotBase = None                                                                                                  
   52     ) -> matplotlib.figure.Figure:                                                                                                              
   53         """For making violinplots."""                                                                                                           
   54         if not self.matplotlib_installed:  # pragma: no cover                                                                                   
   55             return None                                                                                                                         
   56                                                                                                                                                 
   57         if ax is None:                                                                                                                          
   58             _, ax = plt.subplots()                                                                                                              
   59         else:                                                                                                                                   
   60             ax = ax                                                                                                                             
   61                                                                                                                                                 
   62         figure = ax.get_figure()                                                                                                                
   63         width = max(self.nplayers / 3, 12)                                                                                                      
   64         height = width / 2                                                                                                                      
   65         spacing = 4                                                                                                                             
   66         positions = spacing * arange(1, self.nplayers + 1, 1)                                                                                   
   67         figure.set_size_inches(width, height)                                                                                                   
~  68         ax.violinplot(data, positions=positions, widths=spacing / 2,                                                                            
>> 69                        showmedians=True, showextrema=False)                                                                                     
~  70         ax.set_xticks(positions, names)                                                                                                         
~  71         ax.set_xticklabels(names, rotation=90)                                                                                                  
~  72         ax.set_xlim(0, spacing * (self.nplayers + 1))                                                                                           
+  73         ax.tick_params(axis='both', which='both', labelsize=8)                                                                                  
   74         if title:                                                                                                                               
~  75             ax.title(title)                                                                                                                     
   76         return figure  

but that's not quite right:

>>> fig, axarr = plt.subplots(2, 2)
>>> plot.boxplot(ax=axarr[1, 0])
>>> axarr[0, 1].plot([1, 2, 3], [1, 2, 3])

gives:

download 4

so the plots are moving properly now but the axes labels are off (or the spacing or something). And this also now happens even when we just want a single plot:

>>> plot.boxplot();

download 5

In order to test this (relevant to coverage #894), we can use something based on this:

>>> fig, axarr = plt.subplots(2, 2)
>>> plot.boxplot(ax=axarr[1, 0])
>>> axarr[0, 0].get_ylim(), axarr[1, 0].get_ylim()

which will give different y limits for the two subgraphs:

((0.0, 1.0), (1.7702499999999999, 2.5897500000000004))
@drvinceknight
Copy link
Member Author

I expect this is up @marcharper's street but I'm also tagging in @geraintpalmer who might have an idea :)

@drvinceknight
Copy link
Member Author

I've got a fix, pushing it to issue-984 :) Damn differences between pyplot and axes...

@drvinceknight
Copy link
Member Author

drvinceknight commented Mar 11, 2017

I have pushed 90be7ca.

EDIT (for info):

>>> fig, axarr = plt.subplots(2, 2)
>>> plot.boxplot(ax=axarr[1, 0], title="Scores")
>>> plot.stackplot(evo, ax=axarr[0, 1], title="Evo plot", logscale=False)

download 6

@marcharper
Copy link
Member

Thanks for looking into this. In my ternary plotting library I use the AxesSubplot as you've done now for the same reason, so as far as I know this is the right way to do it.

@drvinceknight
Copy link
Member Author

It was fun: I think my matplotlibfoo up levelled a fair bit doing it!

Thanks for confirming it's the way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants