Dash - Plotly

From Sinfronteras
Revision as of 00:39, 28 December 2019 by Adelo Vieira (talk | contribs) (Word cloud)
Jump to: navigation, search

Dash

https://plot.ly/dash/

https://dash.plot.ly/


Material from the Udemy's course I'm doing: https://www.udemy.com/course/interactive-python-dashboards-with-plotly-and-dash/


Dash apps consist of a Flask server that communicates with front-end React components using JSON packets over HTTP requests. https://www.tutorialspoint.com/python_web_development_libraries/python_web_development_libraries_dash_framework.htm



Installation

https://dash.plot.ly/installation

pip install dash==1.7.0

# Note: starting with dash 0.37.0, dash automatically installs dash-renderer, dash-core-components, dash-html-components, and dash-table, using known-compatible versions of each. You need not and should not install these separately any longer, only dash itself.


A quick note on checking your versions and on upgrading. These docs are run using the versions listed above and these versions should be the latest versions available. To check which version that you have installed, you can run e.g:

>>> import dash_core_components
>>> print(dash_core_components.__version__)


To see the latest changes of any package, check the GitHub repo's CHANGELOG.md file:

All of these packages adhere to semver.



Examples

Dash App Gallery: https://dash-gallery.plotly.host/Portal/


Dash Core Components Gallery: https://dash.plot.ly/dash-core-components



Example 2

app.py
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

df = pd.read_csv(
    'https://gist.githubusercontent.com/chriddyp/'
    'cb5392c35661370d95f300086accea51/raw/'
    '8e0768211f6b747c0db42a9ce9a0937dafcbd8b2/'
    'indicators.csv')

available_indicators = df['Indicator Name'].unique()

app.layout = html.Div([
    html.Div([

        html.Div([
            dcc.Dropdown(
                id='crossfilter-xaxis-column',
                options=[{'label': i, 'value': i} for i in available_indicators],
                value='Fertility rate, total (births per woman)'
            ),
            dcc.RadioItems(
                id='crossfilter-xaxis-type',
                options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
                value='Linear',
                labelStyle={'display': 'inline-block'}
            )
        ],
        style={'width': '49%', 'display': 'inline-block'}),

        html.Div([
            dcc.Dropdown(
                id='crossfilter-yaxis-column',
                options=[{'label': i, 'value': i} for i in available_indicators],
                value='Life expectancy at birth, total (years)'
            ),
            dcc.RadioItems(
                id='crossfilter-yaxis-type',
                options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
                value='Linear',
                labelStyle={'display': 'inline-block'}
            )
        ], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
    ], style={
        'borderBottom': 'thin lightgrey solid',
        'backgroundColor': 'rgb(250, 250, 250)',
        'padding': '10px 5px'
    }),

    html.Div([
        dcc.Graph(
            id='crossfilter-indicator-scatter',
            hoverData={'points': [{'customdata': 'Japan'}]}
        )
    ], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
    html.Div([
        dcc.Graph(id='x-time-series'),
        dcc.Graph(id='y-time-series'),
    ], style={'display': 'inline-block', 'width': '49%'}),

    html.Div(dcc.Slider(
        id='crossfilter-year--slider',
        min=df['Year'].min(),
        max=df['Year'].max(),
        value=df['Year'].max(),
        marks={str(year): str(year) for year in df['Year'].unique()}
    ), style={'width': '49%', 'padding': '0px 20px 20px 20px'})
])


@app.callback(
    dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),
    [dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
     dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
     dash.dependencies.Input('crossfilter-xaxis-type', 'value'),
     dash.dependencies.Input('crossfilter-yaxis-type', 'value'),
     dash.dependencies.Input('crossfilter-year--slider', 'value')])
def update_graph(xaxis_column_name, yaxis_column_name,
                 xaxis_type, yaxis_type,
                 year_value):
    dff = df[df['Year'] == year_value]

    return {
        'data': [go.Scatter(
            x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
            y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
            text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],
            customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],
            mode='markers',
            marker={
                'size': 15,
                'opacity': 0.5,
                'line': {'width': 0.5, 'color': 'white'}
            }
        )],
        'layout': go.Layout(
            xaxis={
                'title': xaxis_column_name,
                'type': 'linear' if xaxis_type == 'Linear' else 'log'
            },
            yaxis={
                'title': yaxis_column_name,
                'type': 'linear' if yaxis_type == 'Linear' else 'log'
            },
            margin={'l': 40, 'b': 30, 't': 10, 'r': 0},
            height=450,
            hovermode='closest'
        )
    }


def create_time_series(dff, axis_type, title):
    return {
        'data': [go.Scatter(
            x=dff['Year'],
            y=dff['Value'],
            mode='lines+markers'
        )],
        'layout': {
            'height': 225,
            'margin': {'l': 20, 'b': 30, 'r': 10, 't': 10},
            'annotations': [{
                'x': 0, 'y': 0.85, 'xanchor': 'left', 'yanchor': 'bottom',
                'xref': 'paper', 'yref': 'paper', 'showarrow': False,
                'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
                'text': title
            }],
            'yaxis': {'type': 'linear' if axis_type == 'Linear' else 'log'},
            'xaxis': {'showgrid': False}
        }
    }


@app.callback(
    dash.dependencies.Output('x-time-series', 'figure'),
    [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
     dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
     dash.dependencies.Input('crossfilter-xaxis-type', 'value')])
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
    country_name = hoverData['points'][0]['customdata']
    dff = df[df['Country Name'] == country_name]
    dff = dff[dff['Indicator Name'] == xaxis_column_name]
    title = '<b>{}</b><br>{}'.format(country_name, xaxis_column_name)
    return create_time_series(dff, axis_type, title)


@app.callback(
    dash.dependencies.Output('y-time-series', 'figure'),
    [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
     dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
     dash.dependencies.Input('crossfilter-yaxis-type', 'value')])
def update_x_timeseries(hoverData, yaxis_column_name, axis_type):
    dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]
    dff = dff[dff['Indicator Name'] == yaxis_column_name]
    return create_time_series(dff, axis_type, yaxis_column_name)


if __name__ == '__main__':
    app.run_server(debug=True, port=8051)


To run the app:

python app.py


Dash example2.png



Deploying Dash Apps

https://dash.plot.ly/deployment


Dash uses Flask under the hood. This makes deployment easy: you can deploy a Dash app just like you would deploy a Flask app. Almost every cloud server provider has a guide for deploying Flask apps. There is also a Dash Deployment Server, but is not free (commercial).

  • Flask Deployment
  • Dash Deployment Server (commercial)



Flask Deployment

https://flask.palletsprojects.com/en/1.1.x/deploying/



Gunicorn

https://flask.palletsprojects.com/en/1.1.x/deploying/wsgi-standalone/#gunicorn

https://gunicorn.org/



Installation:

https://anaconda.org/conda-forge/gunicorn

conda install -c conda-forge gunicorn

or

pip install gunicorn



Gunicorn «Green Unicorn» is a WSGI HTTP Server for UNIX. It's a pre-fork worker model ported from Ruby's Unicorn project. It supports both eventlet and greenlet. Running a Flask application on this server is quite simple:

gunicorn myproject:app


Gunicorn provides many command-line options (see gunicorn -h). For example, to run a Flask application with 4 worker processes (-w 4) binding to localhost port 4000 (-b 127.0.0.1:4000):

gunicorn -w 4 -b 127.0.0.1:4000 myproject:app


The gunicorn command expects the names of your application module or package and the application instance within the module. If you use the application factory pattern, you can pass a call to that:

gunicorn "myproject:create_app()"



First example:

def app(environ, start_response):
        data = b"Hello, World!\n"
        start_response("200 OK", [
            ("Content-Type", "text/plain"),
            ("Content-Length", str(len(data)))
        ])
        return iter([data])

To run the server:

gunicorn -w 4 myapp:app

Executing the above command will only run the development server. In the next section we will explain how to deploy a Gunicorn



Deploying a Gunicorn server

This is the official page. It doesn't explain well how to do it:

http://docs.gunicorn.org/en/latest/deploy.html


This tutorial explain well hot to do deploy a Flask Applications with Gunicorn and Nginx:

https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-16-04


Now, when using Dash, we have to make a few changes with respect to the above tutorial. The following posts helped me to find the solution:

https://community.plot.ly/t/error-with-gunicorn/8247
https://community.plot.ly/t/failed-to-find-application-object-server-in-app/13723



Example - Deploying a Dash aplications with Gunicorn and Nginx on Ubuntu 16.04 (based on https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-16-04)


  • Create and activate a Python Virtual Environment :
sudo pip3 install virtualenv

mkdir ~/myproject
cd ~/myproject

virtualenv myprojectenv  # This will install a local copy of Python and pip into a directory called myprojectenv

source myprojectenv/bin/activate


Your prompt will change to indicate that you are now operating within the virtual environment. It will look something like this:
(myprojectenv)user@host:~/myproject$.


  • Install Flask, Dash and Gunicorn inside the virtual environment:
pip install gunicorn flask

ver «Dash» installation
ver «gunicorn» installation


  • Create a Sample App:
import os
import dash
import dash_core_components as dcc
import dash_html_components as html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

server = app.server

app.layout = html.Div(children=[
    html.H1(children='Hello Dash'),

    html.Div(children='''
        Dash: A web application framework for Python.
    '''),

    dcc.Graph(
        id='example-graph',
        figure={
            'data': [
                {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
                {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
            ],
            'layout': {
                'title': 'Dash Data Visualization'
            }
        }
    )
])

if __name__ == '__main__':
    app.run_server(debug=True, host='0.0.0.0')
Notice that we have included: server = app.server.


  • Now, you can test your Dash app by typing:
(myprojectenv)$ python myproject.py
Visit your server's domain name or IP address followed by :port in your web browser to verify your App is working.


  • Create the WSGI Entry Point: We'll create a file that will serve as the entry point for our application. This will tell our Gunicorn server how to interact with the application:
(myprojectenv)$ vi ~/myproject/wsgi.py
from myproject import server

if __name__ == "__main__":
    server.run()
Notice that we have import the variable server from myproject.py
This is the different with respect to a pure Flask application, where you would import App instead of server. In Dash, we require app.server, which is in the server variable we have created. So if we were deploying a pure flak App, it would be:
from myproject import app

if __name__ == "__main__":
    app.run()


  • Testing Gunicorn's Ability to Serve the Project:
(myprojectenv)$ cd ~/myproject
(myprojectenv)$ gunicorn --bind 0.0.0.0:5000 wsgi:server
For a pure Flask application, would be wsgi:App.
Visit your server's domain name or IP address with :port appended to the end in your web browser again.


  • We're now done with our virtual environment, so we can deactivate it:
(myprojectenv)$ deactivate
Any Python commands will now use the system’s Python environment again.


  • Create a systemd Unit File:
$ vi /etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/root/myproject
Environment="PATH=/root/myproject/myprojectenv/bin"
ExecStart=/root/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:server

[Install]
WantedBy=multi-user.target


  • We can now start the Gunicorn service we created and enable it so that it starts at boot:
$ sudo systemctl start myproject
$ sudo systemctl enable myproject


  • Configuring Nginx to Proxy Requests:
$ vi /etc/nginx/sites-available/default


server {
    listen 80;
    server_name gofaaaz.sinfronteras.ws;

    location / {
        include proxy_params;
        proxy_pass http://unix:/root/myproject/myproject.sock;
    }
}


  • Finally, we restart the Nginx process:
sudo systemctl restart nginx.service
You should now be able to go to your server's domain name or IP address in your web browser and see your App.



Dash Layout

https://dash.plot.ly/getting-started

Dash apps are composed of two parts:

  • The first part is' the "layout" of the app and it describes what the application looks like.
  • The second part describes the interactivity of the application and will be covered in the next chapter.

Dash provides Python classes for all of the visual components of the application. We maintain a set of components in the dash_core_components and the dash_html_components library but you can also build your own with JavaScript and React.js.

To get started, create a file named 'app.py' with the following code:

import dash
import dash_core_components as dcc
import dash_html_components as html


app = dash.Dash(__name__)

app.layout = html.Div(children=[
    html.H1(children='Hello Dash'),
    html.Div(children='Dash: A web application framework for Python')
])


if __name__ == '__main__':
    app.run_server(debug=True, port=8551)

Es importante utilizar un port que no esté ocupado por otro proceso.


Including a Graph and style:

import dash 
import dash_core_components as dcc
import dash_html_components as html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__,external_stylesheets=external_stylesheets)

colors = {
    'backgroud': '#111111',
    'text': '#7FDBFF'
}

app.layout = html.Div(
    style={
        'backgroundColor': colors['backgroud']
    },
    children=[
        html.H1(
            style={
                'textAlign': 'center',
                'color': colors['text']
            },
            children='Hello Dash'
        ),
        html.Div(
            style={
                'textAlign': 'center',
                'color': colors['text']
            },
            children='Dash: A web application framework for Python'
        ),
        dcc.Graph(
            id='example-graph',
            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualisation',
                    'plot_bgcolor': colors['backgroud'],
                    'paper_bgcolor': colors['backgroud'],
                    'font': {
                        'color': colors['text']
                    }
                }
            }
        )
    ]
)

if __name__ == '__main__':
    app.run_server(debug=True, port=8551)


Reusable Components: By writing our markup in Python, we can create complex reusable components like tables without switching contexts or languages. Here's a quick example that generates a 'Table' from a Pandas dataframe:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
df = pd.read_csv('https://gist.githubusercontent.com/chriddyp/c78bf172206ce24f77d6363a2d754b59/raw/c353e8ef842413cae56ae3920b8fd78468aa4cb2/usa-agricultural-exports-2011.csv')


def generate_table(dataframe, max_rows=10):
    return html.Table(
        # Header
        [html.Tr([html.Th(col) for col in dataframe.columns])] +

        # Body
        [html.Tr([
            html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
        ]) for i in range(min(len(dataframe), max_rows))]
    )


app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(children=[
    html.H4(children='US agriculture Exports (2011)'),
    generate_table(df)
])


if __name__ == '__main__':
    app.run_server(debug=True, port=8551)


The dash_core_components library includes a component called Graph.

Graph renders interactive data visualizations using the open source plotly.js JavaScript graphing library. Plotly.js supports over 35 chart types and renders charts in both vector-quality SVG and high-performance WebGL.

The figure argument in the dash_core_components.Graph component is the same figure argument that is used by plotly.py, Plotly's open source Python graphing library. Check out the plotly.py documentation and gallery to learn more: https://plot.ly/python/