60

Is there a way to make matplotlib behave identically to R, or almost like R, in terms of plotting defaults? For example R treats its axes pretty differently from matplotlib. The following histogram enter image description here

has "floating axes" with outward ticks, such that there are no inner ticks (unlike matplotlib) and the axes do not cross "near" the origin. Also, the histogram can "spillover" to values that are not marked by the tick - e.g. the x-axis ends at 3 but the histograms extends slightly beyond it. How can this be achieved automatically for all histograms in matplotlib?

Related question: scatter plots and line plots have different default axes settings in R, for example: enter image description here

There no inner ticks again and the ticks face outward. Also, the ticks start slightly after the origin point (where the y and x axes cross at the bottom left of the axes) and the ticks end slightly before the axes end. This way the labels of the lowest x-axis tick and lowest y-axis tick can't really cross, because there's a space between them and this gives the plots a very elegant clean look. Note that there's also considerably more space between the axes ticklabels and the ticks themselves.

Also, by default there are no ticks on the non-labeled x or y axes, meaning the y-axis on the left that is parallel to the labeled y-axis on the right has no ticks, and same for the x-axis, again removing clutter from the plots.

Is there a way to make matplotlib look like this? And in general to look by default as much as default R plots? I like matplotlib a lot but I think the R defaults / out-of-the-box plotting behavior really have gotten things right and its default settings rarely lead to overlapping tick labels, clutter or squished data, so I would like the defaults to be as much like that as possible.

  • 4
    considering matplotlib can do [XKCD-style](http://jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotlib/), this ought to be a walk in the park for someone! :) – Andy Hayden Jan 24 '13 at 01:40
  • @AndyHayden: I agree, but I don't think there's any template yet provided that just does R style plots! The examples provided here only mimic one or two plots but do not address the problem generally. The default matplotlib settings simply do not work. And if I may add, this would be orders of magnitude more useful (and probably easier to implement!) than XKCD-style plots. –  Jan 26 '13 at 17:53
  • A global setting to "R style" would be very useful, it's a shame that 300rep seems not to be advertisement enough to find someone to do it. Best of luck. – Andy Hayden Jan 26 '13 at 18:50
  • @AndyHayden: To be fair it's a tall order on my part but I don't think 1000 rep points would have made the difference... not sure how to do this. It's either more difficult than I think or people are just not that interested in it (I'm pretty sure it's the latter) –  Jan 28 '13 at 18:30
  • Is this what you want? https://github.com/matplotlib/matplotlib/pull/2236 – endolith Apr 12 '14 at 15:03

8 Answers8

45

Edit 1 year later:

With seaborn, the example below becomes:

import numpy as np
import matplotlib.pyplot as plt
import seaborn
seaborn.set(style='ticks')
# Data to be represented
X = np.random.randn(256)

# Actual plotting
fig = plt.figure(figsize=(8,6), dpi=72, facecolor="white")
axes = plt.subplot(111)
heights, positions, patches = axes.hist(X, color='white')
seaborn.despine(ax=axes, offset=10, trim=True)
fig.tight_layout()
plt.show()

Pretty dang easy.

Original post:

This blog post is the best I've seen so far. http://messymind.net/making-matplotlib-look-like-ggplot/

It doesn't focus on your standard R plots like you see in most of the "getting started"-type examples. Instead it tries to emulate the style of ggplot2, which seems to be nearly universally heralded as stylish and well-designed.

To get the axis spines like you see the in bar plot, try to follow one of the first few examples here: http://www.loria.fr/~rougier/coding/gallery/

Lastly, to get the axis tick marks pointing outward, you can edit your matplotlibrc files to say xtick.direction : out and ytick.direction : out.

Combining these concepts together we get something like this:

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# Data to be represented
X = np.random.randn(256)

# Actual plotting
fig = plt.figure(figsize=(8,6), dpi=72, facecolor="white")
axes = plt.subplot(111)
heights, positions, patches = axes.hist(X, color='white')

axes.spines['right'].set_color('none')
axes.spines['top'].set_color('none')
axes.xaxis.set_ticks_position('bottom')

# was: axes.spines['bottom'].set_position(('data',1.1*X.min()))
axes.spines['bottom'].set_position(('axes', -0.05))
axes.yaxis.set_ticks_position('left')
axes.spines['left'].set_position(('axes', -0.05))

axes.set_xlim([np.floor(positions.min()), np.ceil(positions.max())])
axes.set_ylim([0,70])
axes.xaxis.grid(False)
axes.yaxis.grid(False)
fig.tight_layout()
plt.show()

The position of the spines can be specified a number of ways. If you run the code above in IPython, you can then do axes.spines['bottom'].set_position? to see all of your options.

R-style bar plot in python

So yeah. It's not exactly trivial, but you can get close.

Community
  • 1
  • 1
Paul H
  • 65,268
  • 20
  • 159
  • 136
  • That does the grid but pretty much doesn't do any of the axes things outlines above... its ticks also look funny. –  Jan 16 '13 at 01:01
  • 1
    The 3 folks I know who use R, make figures using ggplot2, so it seems relevant to me. To get the spines down, check out the first few examples here: http://www.loria.fr/~rougier/coding/gallery/. They should at least set you down the right path. – Paul H Jan 16 '13 at 01:09
  • ggplot2 is nice but I am referring mainly to the R base. While ggplot2 looks stylish, you can argue that this background colored grids lead to a large waste of ink when printed, etc. Also, many people who use R regularly use only the R base without ggplot2, so my goal is to make matplotlib look like R base which more universal than ggplot2. –  Jan 16 '13 at 14:07
  • The example http://www.loria.fr/~rougier/coding/gallery/spine/spines-5.py from the page you cite is closest to the bar graph axes, but still very different from it... it looks chopped up and not like the nice default floating axes of the histogram plot in R. Not trying to be picky, just saying that even with the great customization capacities, I'm still unclear how to make axes that look like those axes in the histogram, which is the default behavior in R. –  Jan 16 '13 at 14:11
  • Thanks, but these ``set_position`` calls depend on the data limits. Is there a way to automate it for any arbitrary data? –  Jan 16 '13 at 22:44
  • Yes, of course. See the line that says `axes.spines['bottom'].set_position(('data',1.1*X.min()))`. However, I don't think that the best way. Ideally, you should probably specify the positions of the spines relative to the axes itself. I've edited the response to reflect this. – Paul H Jan 16 '13 at 23:28
  • The link to messymind's blogpost is broken. Use this instead - http://messymind.net/making-matplotlib-look-like-ggplot/ – Jaidev Deshpande May 25 '14 at 17:01
35

matplotlib >= 1.4 suports styles (and ggplot-style is build in):

In [1]: import matplotlib as mpl

In [2]: import matplotlib.pyplot as plt

In [3]: import numpy as np

In [4]: mpl.style.available
Out[4]: [u'dark_background', u'grayscale', u'ggplot']

In [5]: mpl.style.use('ggplot')

In [6]: plt.hist(np.random.randn(100000))
Out[6]: 
...

enter image description here

bmu
  • 35,119
  • 13
  • 91
  • 108
  • 2
    It has changed slightly I suppose. See here: https://github.com/matplotlib/matplotlib/blob/master/doc/users/style_sheets.rst The `.style.use()` has to be called on the `matplotlib.pyplot`-import. – tim Jun 12 '14 at 09:47
  • 1
    @tim; You can call it either by `matplotlib.pyplot.style.use()` or `matplotlib.style.use()`. Both will work. – haccks Jun 09 '15 at 18:53
28

# # # # # #

EDIT 10/14/2013: For information, ggplot has now been implemented for python (built on matplotlib).

See this blog or go directly to the github page of the project for more information and examples.

# # # # # #

To my knowledge, there is no built-in solution in matplotlib that will directly give to your figures a similar look than the ones made with R.

Some packages, like mpltools, adds support for stylesheets using Matplotlib’s rc-parameters, and can help you to obtain a ggplot look (see the ggplot style for an example).

However, since everything can be tweaked in matplotlib, it might be easier for you to directly develop your own functions to achieve exactly what you want. As an example, below is a snippet that will allow you to easily customize the axes of any matplotlib plot.

def customaxis(ax, c_left='k', c_bottom='k', c_right='none', c_top='none',
               lw=3, size=20, pad=8):

    for c_spine, spine in zip([c_left, c_bottom, c_right, c_top],
                              ['left', 'bottom', 'right', 'top']):
        if c_spine != 'none':
            ax.spines[spine].set_color(c_spine)
            ax.spines[spine].set_linewidth(lw)
        else:
            ax.spines[spine].set_color('none')
    if (c_bottom == 'none') & (c_top == 'none'): # no bottom and no top
        ax.xaxis.set_ticks_position('none')
    elif (c_bottom != 'none') & (c_top != 'none'): # bottom and top
        ax.tick_params(axis='x', direction='out', width=lw, length=7,
                      color=c_bottom, labelsize=size, pad=pad)
    elif (c_bottom != 'none') & (c_top == 'none'): # bottom but not top
        ax.xaxis.set_ticks_position('bottom')
        ax.tick_params(axis='x', direction='out', width=lw, length=7,
                       color=c_bottom, labelsize=size, pad=pad)
    elif (c_bottom == 'none') & (c_top != 'none'): # no bottom but top
        ax.xaxis.set_ticks_position('top')
        ax.tick_params(axis='x', direction='out', width=lw, length=7,
                       color=c_top, labelsize=size, pad=pad)
    if (c_left == 'none') & (c_right == 'none'): # no left and no right
        ax.yaxis.set_ticks_position('none')
    elif (c_left != 'none') & (c_right != 'none'): # left and right
        ax.tick_params(axis='y', direction='out', width=lw, length=7,
                       color=c_left, labelsize=size, pad=pad)
    elif (c_left != 'none') & (c_right == 'none'): # left but not right
        ax.yaxis.set_ticks_position('left')
        ax.tick_params(axis='y', direction='out', width=lw, length=7,
                       color=c_left, labelsize=size, pad=pad)
    elif (c_left == 'none') & (c_right != 'none'): # no left but right
        ax.yaxis.set_ticks_position('right')
        ax.tick_params(axis='y', direction='out', width=lw, length=7,
                       color=c_right, labelsize=size, pad=pad)

EDIT: for non touching spines, see the function below which induces a 10 pts displacement of the spines (taken from this example on the matplotlib website).

def adjust_spines(ax,spines):
    for loc, spine in ax.spines.items():
        if loc in spines:
            spine.set_position(('outward',10)) # outward by 10 points
            spine.set_smart_bounds(True)
        else:
            spine.set_color('none') # don't draw spine

For example, the code and the two plots below show you the default output from matplotib (on the left), and the output when the functions are called (on the right):

import numpy as np
import matplotlib.pyplot as plt

fig,(ax1,ax2) = plt.subplots(figsize=(8,5), ncols=2)
ax1.plot(np.random.rand(20), np.random.rand(20), 'ok')
ax2.plot(np.random.rand(20), np.random.rand(20), 'ok')

customaxis(ax2) # remove top and right spines, ticks out
adjust_spines(ax2, ['left', 'bottom']) # non touching spines

plt.show()

image

Of course, it will take time for you to figure out which parameters have to be tweaked in matplotlib to make your plots look exactly like the R ones, but I am not sure there are other options right now.

gcalmettes
  • 8,474
  • 1
  • 32
  • 28
  • How would you get "floating" axes though that do not touch, as in the histogram example? The thing on the right is virtually never produced by R –  Jan 16 '13 at 14:08
  • see [this example](http://matplotlib.org/examples/pylab_examples/spine_placement_demo.html) from the matplotlib website for non touching spines in matplotlib – gcalmettes Jan 16 '13 at 14:52
  • 1
    @user248237 I have edited the response to include the function for non touching spines. See the updated figure too. Hope that helps. – gcalmettes Jan 16 '13 at 15:10
10

I would check out Bokeh which aims to "provide a compelling Python equivalent of ggplot in R". Example here

EDIT: Also check out Seaborn, which attempts to reproduce the visual style and syntax of ggplot2.

Brian Keegan
  • 2,208
  • 4
  • 24
  • 31
  • 1
    I don't see any examples in that link you showed? But even without examples, the Grammar of Graphics based on the R book looks considerably more complex than any of the simple bread and butter plots I mentioned above –  Jan 16 '13 at 01:02
4

Here's a blog post you may be interested to read:

Plotting for Pandas GSoC2012

http://pandasplotting.blogspot.com/

Decided to try to implement a ggplot2 type plotting interface...Not yet sure how much of the ggplot2 functionality to implement...

The author forked pandas and built what looks like quite a lot of ggplot2-style grammar for pandas.

Density Plots

plot = rplot.RPlot(tips_data, x='total_bill', y='tip')
plot.add(rplot.TrellisGrid(['sex', 'smoker']))
plot.add(rplot.GeomHistogram())
plot.render(plt.gcf())

The pandas fork is here: https://github.com/orbitfold/pandas

Seems like meat of the code to make the R-influenced graphics is in a file called rplot.py which can be found in a branch in the repo.

class GeomScatter(Layer):
    """
    An efficient scatter plot, use this instead of GeomPoint for speed.
    """

class GeomHistogram(Layer):
    """
    An efficient histogram, use this instead of GeomBar for speed.
    """

Link to the branch:

https://github.com/orbitfold/pandas/blob/rplot/pandas/tools/rplot.py

I thought this was really cool, but I can't figure out if this project is being maintained or not. The last commit was a while ago.

hernamesbarbara
  • 6,850
  • 3
  • 26
  • 25
2

Setting spines in matplotlibrc explains why it is not possible to simply edit Matplotlib defaults to produce R-style histograms. For scatter plots, R style data-axis buffer in matplotlib and In matplotlib, how do you draw R-style axis ticks that point outward from the axes? show some defaults that can be changed to give a more R-ish look. Building off some of the other answers, the following function does a decent job of mimicking R's histogram style, assuming you've called hist() on your Axes instance with facecolor='none'.

def Rify(axes):
    '''
    Produce R-style Axes properties
    '''
    xticks = axes.get_xticks() 
    yticks = axes.get_yticks()

    #remove right and upper spines
    axes.spines['right'].set_color('none') 
    axes.spines['top'].set_color('none')

    #make the background transparent
    axes.set_axis_bgcolor('none')

    #allow space between bottom and left spines and Axes
    axes.spines['bottom'].set_position(('axes', -0.05))
    axes.spines['left'].set_position(('axes', -0.05))

    #allow plot to extend beyond spines
    axes.spines['bottom'].set_bounds(xticks[0], xticks[-2])
    axes.spines['left'].set_bounds(yticks[0], yticks[-2])

    #set tick parameters to be more R-like
    axes.tick_params(direction='out', top=False, right=False, length=10, pad=12, width=1, labelsize='medium')

    #set x and y ticks to include all but the last tick
    axes.set_xticks(xticks[:-1])
    axes.set_yticks(yticks[:-1])

    return axes
Community
  • 1
  • 1
JaminSore
  • 3,758
  • 1
  • 25
  • 21
1

The Seaborn visualisation library can do that. For example, to reproduce the style of the R histogram use:

sns.despine(offset=10, trim=True)

as in https://seaborn.pydata.org/tutorial/aesthetics.html#removing-axes-spines

enter image description here

To reproduce the style of the R scatter plot use:

sns.set_style("ticks")

as shown in https://seaborn.pydata.org/tutorial/aesthetics.html#seaborn-figure-styles

enter image description here

altroware
  • 940
  • 3
  • 13
  • 22
0

import matplotlib.pyplot as plt plt.style.use('ggplot')

do something plot here, and enjoy it