Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is matplotlib plot produced from ipython notebook slightly different from terminal version?

I have a strange issue. Using IPython Notebook, I created a quite extensive script using pandas and matplotlib to create a number of charts. When my tinkering was finished, I copied (and cleaned) the code into a standalone python script (so that I can push it into the svn and my paper co-authors can create the charts as well).

For convenience, I import the standalone python script into the notebook again and create a number of charts:

import create_charts as cc
df = cc.read_csv_files("./data")
cc.chart_1(df, 'fig_chart1.pdf')
...

Strange enough, the .pdf file I get using the above method is slightly different from the .pdf file I get when I run my standalone python script from my Windows 7 terminal. The most notable difference is that in a particular chart the legend is located in the upper corner instead of the lower corner. But there are other small diferences as well (bounding box size, font seems slightly different)

What could be the cause of this. And how can I troubleshoot it? (I already shut down my notebook and restarted it, to reimport my create_charts script and rule out any unsaved changes) My terminal reports I am using Python 2.7.2, and pip freeze | grep ipython reports ipython 0.13.1

like image 687
Rabarberski Avatar asked Jun 03 '13 20:06

Rabarberski


3 Answers

To complete Joe answer, the inlinebackend (IPython/kernel/zmq/pylab/backend_inline.py) have some default matplotlib parameters :

# The typical default figure size is too large for inline use,
# so we shrink the figure size to 6x4, and tweak fonts to
# make that fit.
rc = Dict({'figure.figsize': (6.0,4.0),
    # play nicely with white background in the Qt and notebook frontend
    'figure.facecolor': 'white',
    'figure.edgecolor': 'white',
    # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
    'font.size': 10,
    # 72 dpi matches SVG/qtconsole
    # this only affects PNG export, as SVG has no dpi setting
    'savefig.dpi': 72,
    # 10pt still needs a little more room on the xlabel:
    'figure.subplot.bottom' : .125
    }, config=True,
    help="""Subset of matplotlib rcParams that should be different for the
    inline backend."""
)

As this is not obvious to everyone, you can set it in config through c.InlineBackend.rc.

[Edit] precise info about configurability.

IPython have the particularity that most of the classes have properties which default values can be configured. Those are often refered as Configurable (uppercase C), those property can easily be recognize in the code as they are declared like so before __init__:

property = A_Type( <default_value>, config=True , help="a string")

You can overwrite those properties in IPython configuration files (which one depends on what you want to do) by doing

c.ClassName.propertie_name = value

Here as it is a dict you could do

#put your favorite matplotlib config here.
c.InlineBackend.rc = {'figure.facecolor': 'black'}  

I guess an empty dict would allow inline backend to use matplotlib defaults.

like image 157
Matt Avatar answered Oct 16 '22 12:10

Matt


Extending Matt's answer (lots of credit to him, but I think the answers can be less complex), this is how I eventually solved it.

(a) I looked up ipython's default matplotlib settings in C:\Python27\Lib\site-packages\IPython\zmq\pylab\backend_inline.py (see Matt's answer).

(b) and overwrote them with the values as set in the terminal version (I used print mpl.rcParams['figure.figsize'] etc. to find out) by inserting the following code in my script:

import matplotlib as mpl

#To make sure we have always the same matplotlib settings
#(the ones in comments are the ipython notebook settings)

mpl.rcParams['figure.figsize']=(8.0,6.0)    #(6.0,4.0)
mpl.rcParams['font.size']=12                #10 
mpl.rcParams['savefig.dpi']=100             #72 
mpl.rcParams['figure.subplot.bottom']=.1    #.125
like image 35
Rabarberski Avatar answered Oct 16 '22 13:10

Rabarberski


The font size issues are due to differences in the dpi. I'd guess the slightly different size of the figure (in pixels) changes the "best" location for the legend, as well.

The default dpi a figure is displayed at is 80, while savefig defaults to 100. This means that by default, matplotlib figures will look slightly different when saved compared to what's displayed on the screen.

I don't know for sure, but I'm guessing that ipython notebooks set the dpi to something other than 100 (most likely 80) and use that when saving figures.

Try doing savefig('filename.pdf', dpi=80) in your standalone script.

like image 30
Joe Kington Avatar answered Oct 16 '22 12:10

Joe Kington