Friday, January 1, 2016

Serving a matplotlib plot that follows good design practices using Flask

It can be useful at times to serve images that are generated using matplotlib within a Flask application context. This enables some lightweight, non-dynamic visualization generation.

Unfortunately, the defaults for matplotlib are terrible. Ugh!


Enter prettyplotlib -- this library applies some of the work of several key influencers in the visual design space (Tufte, Brewer) to matplotlib. We eliminate chartjunk, add some better fonts, remove some redundant axes, add some better colours, and get the following:



Much better! Now how do we go about serving this type of a plot via Flask? We will have to have two views, one that creates the image, and another one that serves the template that will contain the image. Our template, contained at templates/image.html should be as follows:


<html>
  <head>
    <title>{{ title }} - image</title>
  </head>
  <body>
    <img src="/fig/" alt="Image Placeholder">
  </body>
</html>

and our main application file, titled run.py, should contain the following:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from io import BytesIO
from flask import Flask, render_template, send_file, make_response
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
import prettyplotlib as ppl
import matplotlib.pyplot as plt


app = Flask(__name__)

@app.route('/fig/')
def fig():
    fig, ax = plt.subplots(1)
    ppl.bar(ax, np.arange(10), np.abs(np.random.randn(10)))
    canvas = FigureCanvas(fig)
    img = BytesIO()
    fig.savefig(img)
    img.seek(0)
    return send_file(img, mimetype='image/png')


@app.route('/image/')
def images():
    return render_template("image.html")


if __name__ == '__main__':
    app.run(debug=True)

The key is line 14 -- rather than calling matplotlib's plt.bar, we call prettyplotlib's plt.bar . Just like that, our output is much more visually appealing and easier to read!

For further reading, check out the following Stack Overflow thread about serving matplotlib files via Flask, this Gist about plotting a PNG using matplotlib (uses Python 2 -- you'll have to adapt it to use the io library if you'd like to try something similar in Python 3) and the official prettyplotlib documentation.

1 comment:

  1. Unfortunately, prettyplotlib is dead at this point. The author recommends using seaborn instead. Simply importing seaborn `import seaborn as sns` will automatically change the styling.

    Alternatively, the newer matplotlib provides styling with `matplotlib.style.use`. There is not currently an exact duplicate of prettyplotlib's style that I can see, but some are similar. You could also create your own style, post it in a gist and use the URL.

    ReplyDelete

Sign up to receive data viz talk tips and updates via email. We're low traffic and take privacy very seriously.