optimus_prime
optimus_prime

Reputation: 827

Passing matplotlib subplots generated from a pandas dataframe to HTML for display using flask

I have my dataframe that looks like this:

    travel_spending
state   ethinicity  
CA  Asian   233.404580
MA  Asian   748.117647
NY  Asian   350.880000
CA  Black   146.898148
MA  Black   99.849057
NY  Black   125.206897
CA  Chinese 387.398601
MA  Chinese 119.636364
NY  Chinese 263.245283
CA  Hispanic    131.156484
MA  Hispanic    200.220859
NY  Hispanic    175.738589
CA  American Indian 36.500000
MA  American Indian 67.500000
NY  American Indian 81.800000
CA  Japanese    365.029703
MA  Japanese    28.666667
NY  Japanese    241.500000
CA  Other   257.953356
MA  Other   208.178174
NY  Other   255.144436
CA  Portuguese  26.000000
MA  Portuguese  22.000000
CA  White   222.322485
MA  White   167.293194
NY  White   140.080838

From this dataframe, I generate subplots as follows:

import matplotlib.pyplot as plt
plt.style.use('ggplot')
plt.ion()
plt.rcParams['xtick.color']='k'
plt.rcParams['xtick.labelsize']='x-large'

travel_df_new.unstack(level=0).plot(kind='bar', subplots=True, legend=False)

subplots

Now I am trying to pass them to a HTML page using flask:

import sqlite3
import pandas as pd
from flask import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from io import BytesIO,StringIO
@app.route("/fig/")
def new_fig():
   connection=sqlite3.connect('test.db')
   query = "some sql query"
   travel_df = pd.read_sql(query,connection)
   plt.style.use('ggplot')
   plt.ion()
   plt.rcParams['xtick.color']='k'
   plt.rcParams['xtick.labelsize']='x-large'
   travel_df_new.unstack(level=0).plot(kind='bar', subplots=True, legend=False)
   fig=plt.figure()
   canvas = FigureCanvas(fig)
   img = StringIO()
   fig.savefig(img)
   img.seek(0)
   return send_file(img, mimetype='image/png')
@app.route('/image/')
def images():
   return render_template("image.html")

Below is the image.html

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

Using the matplotlib.figure.Figure i.e fig=plt.figure() object and StringIO i.e. img = StringIO() and fig.savefig(img) seems to be a standard way of accomplishing this. But I don't see any image on the HTML page.

Also, will using the plt.figure() object help to display all the subgraphs?

Upvotes: 0

Views: 1065

Answers (1)

Geotob
Geotob

Reputation: 2945

I think there are two main issues here, see my comments in the code:

travel_df_new.unstack(level=0).plot(kind='bar', subplots=True, legend=False)

# This creates a new, empty Figure. Saving this should just be an empty plot.
fig=plt.figure()

# Now you want to save the empty Figure into your img
# This won't work, because savefig works with BytesIO
canvas = FigureCanvas(fig)
img = StringIO()
fig.savefig(img)
img.seek(0)

Try something like this instead:

travel_df_new.unstack(level=0).plot(kind='bar', subplots=True, legend=False)

buff = BytesIO()
plt.savefig(buff, format='png', dpi=180)
buff.seek(0)

Note this uses io.BytesIO instead of io. StringIO, calls plt.savefig and not fig.savefig, and is a little more verbose in the savefig arguments.

I'm also not quite sure why you call plt.ion() in your route, you won't need interactive mode when run like this.

Upvotes: 3

Related Questions