I am trying to render boxplot to html page using python flask. In the code i have imported excel sheet .xls to a data frame. On this dataframe have used pandas boxplot method to get box plot successfully. But when i am trying to render this box plot to html page using flask, i am able to see only box plot grid in html page and not actual box plot.
I have tried to save the box plot image first and then give its image source path in html page. but still unable to render it successfully to html page.
Python code:
from flask import Flask, render_template, send_file, make_response
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from mpl_toolkits.mplot3d import Axes3D
from io import BytesIO
import pandas as pd
plt.style.use('ggplot')
app = Flask(__name__)
#app.route('/do_box_plot/')
def do_box_plot():
df = pd.read_excel('Path\\sfile_name.xls', sheet_name='Sheet1')
df.boxplot(by = 'Programming_Language', column =['Entry_ProgrammingScore'], grid = True)
bytes_image = io.BytesIO()
plt.savefig(bytes_image, format='png')
bytes_image.seek(0)
return send_file(bytes_image, mimetype='image/png')
#app.route('/')
def index():
return render_template("index2.html")
if __name__ == '__main__':
app.run(debug = True)
HTML code:
<!doctype html>
<html>
<body>
<h1>BOX PLOT VISUALIZATIONS FOR TEST SCORES</h1>
<p>BOX PLOTS</p>
<img src='/do_box_plot/' alt="Box Plot" height="442" width="442">
</body>
</html>`enter code here`
Expect to render generated box plot to html page using flask. Could anyone help, thanks.
Related
I'd like to create a small app in Flask which:
1. Allows the user to upload data, in csv
2. Does stuff to the data (in this example, nothing will be done)
3. Plot results
4. Allow the user to download a plot of the results as pdf.
Code so far:
app.py
from flask import Flask, Response, request, render_template
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.backends.backend_svg import FigureCanvasSVG
import io
import csv
import pandas as pd
app = Flask(__name__)
#app.route("/", methods=['POST', 'GET'])
def index():
return render_template("index.html")
#app.route('/transform', methods=["GET", "POST"])
def transform_view():
""" process data and render plot on the fly.
"""
# GET input csv
f = request.files['input_file']
if not f:
return "Main input file not found!"
# Read uploaded data as a Stream into a dataframe.
stream = io.StringIO(f.stream.read().decode("UTF8"), newline=None)
csv_input = csv.reader(stream)
contents = []
for row in csv_input:
contents.append(row)
d0 = pd.DataFrame(data=contents[1:], columns=contents[0])
# Change to numeric columns
d0[["col1", "col2"]] = d0[["col1", "col2"]].apply(pd.to_numeric)
# Plot
fig, ax = plt.subplots()
bplot = ax.boxplot([d0["col1"], d0["col2"]])
# Print out plot as pdf?
#output = io.BytesIO()
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="/transform" method="post" enctype="multipart/form-data">
<h3>Upload input file (csv)</h3>
<input type="file" name="input_file" id="input_file">
<br>
<input type="submit" value="Process & Download" name="submit">
</form>
</body>
</html>
sample_input.csv
col1,col2
1,5
2,6
3,7
9,10
6,11
So I can generate the pdf with matplotlib, but I'm not sure how to return this as a pdf to the user (the last step). I'm not married to matplotlib; if there are other plotting libraries which can return plots as pdf, I'm open to that as well.
Not tested (I don't have flask installed on this workstation) but you can save the figure as a pdf by giving the correct extension
fig.savefig('/tmp/some_unique_string.pdf')
and then serve the file with send_from_directory
from flask import send_from_directory
send_from_directory('/tmp', 'some_unique_string.pdf')
Some unsolicited advice:
You can read a csv with pandas.read_csv. This works with a file path or an IO stream and will change numeric columns to an appropriate numeric type automatically.
If you want to switch to a different backend, you need to do so before importing pyplot.
Specifically:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
I am trying to render a image from my sqlite3 database, by using this library:
#!/usr/bin/python3
from flask import Flask, render_template, request, send_file
from flask_sqlalchemy import SQLAlchemy
from io import BytesIO
import base64
#app.route('/show/')
def show():
file_data = FileContents.query.filter_by(id=3).first()
image = b64encode(file_data.data)
return render_template('display.html', image = image)
and putting this variable in the html:
<html>
<body>
<img src="data:image;base64,{{image}}"/>
</body>
</html>
But I can't show the image (I am using python3 and google chorme) can someone help to get it?
You can add the extension. Example: <img src="data:image/png;base64,{{ image }}"\>
And decode image to utf-8 return render_template('display.html', image = image.decode('utf8'))
I wish to create dynamic plots based on user input on a flask app. However I am getting the following error:
string argument expected, got 'bytes'
If I use io.BytesIO(), I am not getting this error, but I am not getting the plot on test.html
from flask import Flask
from flask import render_template
import matplotlib.pyplot as plt
import io
import base64
app = Flask(__name__)
#app.route('/plot')
def build_plot():
img = io.StringIO()
y = [1,2,3,4,5]
x = [0,2,1,3,4]
plt.plot(x,y)
plt.savefig(img, format='png')
img.seek(0)
plot_url = base64.b64encode(img.getvalue())
return render_template('test.html', plot_url=plot_url)
if __name__ == '__main__':
app.debug = True
app.run()
Test.html
<!DOCTYPE html>
<html>
<title> Plot</title>
<body>
<img src="data:image/png;base64, {{ plot_url }}">
</body>
</html>
Use BytesIO and later decode()
Working example
from flask import Flask
#from flask import render_template
import matplotlib.pyplot as plt
import io
import base64
app = Flask(__name__)
#app.route('/plot')
def build_plot():
img = io.BytesIO()
y = [1,2,3,4,5]
x = [0,2,1,3,4]
plt.plot(x,y)
plt.savefig(img, format='png')
img.seek(0)
plot_url = base64.b64encode(img.getvalue()).decode()
return '<img src="data:image/png;base64,{}">'.format(plot_url)
if __name__ == '__main__':
app.debug = True
app.run()
I am working on a Flask app which generates a dynamic plot and displays it through a jinja template. I would like to use the template to call a function in the Flask app which returns png data, and then embed the response in a data uri.
This gist is very close to my goal, except I would like to avoid using url_for (and thus routes). Instead, I would like to just display the image data inline using a data uri (img src="data:image/png;base64,...)
Instead of sending the output back as an image with a response, take the output and encode it to base64:
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
from base64 import b64encode
from io import BytesIO
png_output = BytesIO()
canvas.print_png(png_output)
data = b64encode(png_output.getvalue()).decode('ascii')
data_url = 'data:image/png;base64,{}'.format(quote(data))
This has the canvas write to an in-memory file, and the resulting PNG data is then encoded to base64 and interpolated in a data URL.
Complete solution based on your example, tested and working:
from flask import Flask, render_template
import urllib
app = Flask(__name__)
#app.route("/simple.png")
def simple():
import datetime
import StringIO
import random
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.dates import DateFormatter
fig=Figure()
ax=fig.add_subplot(111)
x=[]
y=[]
now=datetime.datetime.now()
delta=datetime.timedelta(days=1)
for i in range(10):
x.append(now)
now+=delta
y.append(random.randint(0, 1000))
ax.plot_date(x, y, '-')
ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
fig.autofmt_xdate()
canvas=FigureCanvas(fig)
png_output = StringIO.StringIO()
canvas.print_png(png_output)
png_output = png_output.getvalue().encode("base64")
return render_template("test.html", img_data=urllib.quote(png_output.rstrip('\n')))
if __name__ == "__main__":
app.run()
Template:
<img src="data:image/png;base64,{{img_data}}"/>
The following code will of course create a PNG named test and save it on the server:
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig = Figure(figsize=[4,4])
ax = fig.add_axes([.1,.1,.8,.8])
ax.scatter([1,2], [3,4])
canvas = FigureCanvasAgg(fig)
canvas.print_figure("test.png")
Then to view the image in the browser, we have to go to example.com/test.png. This means we have to call the page with the Python code first to create the test.png file, then go to the PNG file. Is there a way to draw the PNG and output from the Python page that creates the image? Thanks!
First you need a page to load a url from the webserver controller which generates the image:
<img src="/matplot/makegraph?arg1=foo" />
Then, embed the matplotlib code into the makegraph controller. You just need to capture the canvas rendered PNG in a memory buffer, then create an HTTP response and write the bytes back to the browser:
import cStringIO
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig = Figure(figsize=[4,4])
ax = fig.add_axes([.1,.1,.8,.8])
ax.scatter([1,2], [3,4])
canvas = FigureCanvasAgg(fig)
# write image data to a string buffer and get the PNG image bytes
buf = cStringIO.StringIO()
canvas.print_png(buf)
data = buf.getvalue()
# pseudo-code for generating the http response from your
# webserver, and writing the bytes back to the browser.
# replace this with corresponding code for your web framework
headers = {
'Content-Type': 'image/png',
'Content-Length': len(data)
}
response.write(200, 'OK', headers, data)
Note: you may want to add caching for these if they're frequently generated with the same arguments, e.g. construct a key from the args and write the image data to memcache, then check memcache before regenerating the graph.
Just to update for python3
The StringIO and cStringIO modules are gone. Instead, import the io
module and use io.StringIO
https://docs.python.org/3.5/whatsnew/3.0.html?highlight=cstringio
So now would be something like:
import io
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
fig = Figure(figsize=[4,4])
ax = fig.add_axes([.1,.1,.8,.8])
ax.scatter([1,2], [3,4])
buf = io.BytesIO()
fig.savefig(buf, format='png')
plt.close(fig)
data=buf.getvalue()
# In my case I would have used Django for the webpage
response = HttpResponse(data, content_type='image/png')
return response