How to show a pandas dataframe into a existing flask html table?
Asked Answered
A

6

58

This may sound a noob question, but I'm stuck with it as Python is not one of my best languages.

I have a html page with a table inside it, and I would like to show a pandas dataframe in it. What is the best way to do it? Use pandasdataframe.to_html?

py

from flask import Flask;
import pandas as pd;
from pandas import DataFrame, read_csv;

file = r'C:\Users\myuser\Desktop\Test.csv'
df = pd.read_csv(file)
df.to_html(header="true", table_id="table")

html

<div class="table_entrances" style="overflow-x: auto;">

  <table id="table">

    <thead></thead> 
    <tr></tr>

  </table>

</div>
Alvy answered 4/10, 2018 at 10:6 Comment(0)
H
80

working example:

python code:

from flask import Flask, request, render_template, session, redirect
import numpy as np
import pandas as pd


app = Flask(__name__)

df = pd.DataFrame({'A': [0, 1, 2, 3, 4],
                   'B': [5, 6, 7, 8, 9],
                   'C': ['a', 'b', 'c--', 'd', 'e']})


@app.route('/', methods=("POST", "GET"))
def html_table():

    return render_template('simple.html',  tables=[df.to_html(classes='data')], titles=df.columns.values)



if __name__ == '__main__':
    app.run(host='0.0.0.0')

html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{% for table in tables %}
            {{titles[loop.index]}}
            {{ table|safe }}
{% endfor %}
</body>
</html>

or else use

return render_template('simple.html',  tables=[df.to_html(classes='data', header="true")])

and remove {{titles[loop.index]}} line from html

if you inspect element on html

<html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="">


            <table border="1" class="dataframe data">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>A</th>
      <th>B</th>
      <th>C</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
      <td>5</td>
      <td>a</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1</td>
      <td>6</td>
      <td>b</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2</td>
      <td>7</td>
      <td>c--</td>
    </tr>
    <tr>
      <th>3</th>
      <td>3</td>
      <td>8</td>
      <td>d</td>
    </tr>
    <tr>
      <th>4</th>
      <td>4</td>
      <td>9</td>
      <td>e</td>
    </tr>
  </tbody>
</table>


</body></html>

as you can see it has tbody and thead with in table html. so you can easily apply css.

Holter answered 4/10, 2018 at 10:35 Comment(8)
Thanks, Nihal, working through a python webapp course and we were told to experiment with our first build. The first thing that came to mind was to display a dataframe on a website. Worked wonderfully (although ugly without css!)Gamaliel
Any idea on how to not display the row indices?Sturgill
from df drop index before rendering it.Holter
use df.to_html(index = False)Holter
@Nihal, the html you posted is truncating some columns' contents. How do I work around this problem?Disjointed
@user1783739 can you please give an example of your problem? or ask question and post like here.Holter
any idea why do we use 'safe'? what does 'table|safe' mean & what does it do?Treaty
@DavidGladson check this linkHolter
T
38

In case anyone finds this helpful. I have gone with an alternative because I needed more customization, including the ability to add buttons in the table that performed actions. I also really don't like the standard table formatting as it is very ugly IMHO.

...

df = pd.DataFrame({'Patient Name': ["Some name", "Another name"],
                       "Patient ID": [123, 456],
                       "Misc Data Point": [8, 53]})
...

# link_column is the column that I want to add a button to
return render_template("patient_list.html", column_names=df.columns.values, row_data=list(df.values.tolist()),
                           link_column="Patient ID", zip=zip)

HTML Code: This Dynamically Converts any DF into a customize-able HTML table

<table>
    <tr>
        {% for col in column_names %}
        <th>{{col}}</th>
        {% endfor %}
    </tr>
    {% for row in row_data %}
    <tr>
        {% for col, row_ in zip(column_names, row) %}
        {% if col == link_column %}
        <td>
            <button type="submit" value={{ row_ }} name="person_id" form="patient_form" class="patient_button">
                {{ row_ }}
            </button>
        </td>
        {% else %}
        <td>{{row_}}</td>
        {% endif %}
        {% endfor %}
    </tr>
    {% endfor %}

</table>

CSS Code

table {
    font-family: arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
}

td, th {
    border: 1px solid #dddddd;
    text-align: left;
    padding: 8px;
}

tr:nth-child(even) {
    background-color: #dddddd;
}

It performs very well and it looks WAY better than the .to_html output.

Trevelyan answered 24/5, 2019 at 16:32 Comment(4)
where would I put the css? file name?Briareus
@Briareus css and js typically go into a static folder, i.e. static -> css and static -> js folders inside your application folder. And source at top of html template like. <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">Auer
@BrianWiley An important note. Thanks I was looking how to do this.Tag
Amazing!!! This is so much better than "to_html"...and it dynamically creates the table, just as advertised. Very beautiful and customizable.Manure
A
16
# Declare table
class SomeTable(Table):
    status = Col('Customer')
    city = Col('City')
    product_price = Col('Country')    

# Convert the pandas Dataframe into dictionary structure
output_dict = output.to_dict(orient='records')  

# Populate the table
table = SomeTable(output_dict)

return (table.__html__())

or as pandas return static HTML file you can render it as page using Flask

@app.route('/<string:filename>/')
def render_static(filename):
    return render_template('%s.html' % filename)

It's the Idea of how we can do it in Flask. Hope you can understand this and let me know if it's not helping!

Update:

import pandas as pd

df = pd.DataFrame({'col1': ['abc', 'def', 'tre'],
                   'col2': ['foo', 'bar', 'stuff']})


from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return df.to_html(header="true", table_id="table")

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

Output

But I'd go with Flask HTML feature rather than DataFrame to HTML (due to styling)

Achromatism answered 4/10, 2018 at 10:11 Comment(3)
what about the html code? should I not give him an Id in order to load this table at a correct position of my webpage? Or even to update it when a new dataframe come?Alvy
You can leave the HTML code just make a function that return df.to_html another function which will render the html in the web.Achromatism
If we use HTML code, where do we need to put it? Next to dockerfile?Park
L
6

For those looking for a simple/succinct example of taking a Pandas df and turning into a Flask/Jinja table (which is the reason I ended up on this question):

APP.PY -- APPLICATION FACTORY:

## YOUR DF LOGIC HERE 

@app.route("/")
def home():
    return render_template('index.html' column_names=df.columns.values, row_data=list(df.values.tolist()), zip=zip)

Your static template (e.g. index.html)

 <table>
        <thead>

          <tr>
            {% for col in column_names %}
            <th>
            
              {{col}}
             
            </th>
            {% endfor %}
          </tr>

        </thead>
        <tbody>
          {% for row in row_data %}
          <tr>
            {% for col, row_ in zip(column_names, row) %}
            <td>{{row_}}</td>
            {% endfor %}
          </tr>
          {% endfor %}

         
        </tbody>
  
      </table>
   
Lilybel answered 17/1, 2021 at 19:9 Comment(0)
T
4

For me using Jinja's for loop

{% for table in tables %}
            {{titles[loop.index]}}
            {{ table|safe }}
{% endfor %}

didnt work as it simply printed each character 1 by 1. I simply had to use

{{ table|safe }}
Tripping answered 20/5, 2019 at 21:46 Comment(2)
any idea why do we use 'safe'? what does 'table|safe' mean & what does it do?Treaty
@DavidGladson The safe filter explicitly marks a string as "safe", i.e., it should not be automatically-escaped if auto-escaping is enabled. More on that in docs jinja.palletsprojects.com/en/2.10.x/templates/… and this SO https://mcmap.net/q/331592/-jinja-2-safe-keywordTripping
D
0

If you've invested time and effort in mastering Panda's dataframe styling (perhaps in the context of Jupyter), then you may want to preserve as much of that as possible when you present a dataframe in Flask.

If so, here are the relevant bits of code:

In your controller:

someDataTable = [styledDataFrame.to_html(classes='data', header="true")]

So let's say styledDataFrame was something like this:

styledDataFrame = someDF.style.set_table_styles([dict(selector='th', props=[('text-align', 'center')])])\
        .hide(axis='index')\
        .format(precision=1, thousands=",")

Okay, okay, maybe not the most amazing layout - but let's assume we like it, and want Flask to display basically what we see in Jupyter.

So then (still in the controller):

return render_template(
    'dataViewer.html',
    someNiceMetaData=someNiceMetaData,
    sampleDataTable=sampleDataTable
)

And then in your template (which in this scenario would be called dataViewer.html):

{% block sampledata %}
    <h3>Sample Data</h3>

     {% for table in sampleDataTable %}
        {{ table | safe }}
    {% endfor %}

{% endblock %}

In this way you can continue to enrich your dataframe styler in Jupyter, and simply pass your best ideas through to Flask.

This approach is particularly compelling if, like me, you know way less about CSS than you do about Pandas, and you actually like the Pandas styler! (just me? Come on, the Pandas styler is really cool!)

Dniester answered 12/7, 2022 at 22:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.