<jupyter_start><jupyter_text>RAPIDS + Plotly Dash on Paperspace ML-Driven Web Apps - Tutorial 1 IntroIn this tutorial, we will go through a basic set up where we send a RAPIDS cuDF dataframe to Plot.ly dash on local or paperspace instance and demos some interactions.  This uses a small, preprocessed dataset.  In future tutorials, we'll build upon this foundation to process a large, 1.5GB dataset in real time within the webapp itself. Install Extra LibrariesThe RAPIDSAI container is our starting point.  However, to build this dashboard, we need some extra libraries from plot.ly and jupyter.  You'll only need to install this once per container.  Leave uncommented if this is your first run.  Otherwise comment the `!pip install` lines to save time running.<jupyter_code>!pip install jupyter-dash tables plotly
!pip install dash dash-bootstrap-components dash-html-components matplotlib plotly<jupyter_output><empty_output><jupyter_text>Imports<jupyter_code>import json

import cudf
import cupy as cp
import cupy
import plotly.graph_objects as go

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

from flask import request
from jupyter_dash import JupyterDash
from IPython.display import display, HTML<jupyter_output><empty_output><jupyter_text>In case you need to share your dashboard with someone else, you should get your Jupyter Lab's Token  Import the DataWe need to be preprocess the data for Plot.ly Dash to render it. This data is a pre-processed UMAP emebbeding of 65k+ cells and their gene expressions.  The pre-processing notebook can be found in https://github.com/clara-parabricks/rapids-single-cell-examples<jupyter_code>tdf = cudf.read_csv("embeddings.csv")
tdf
tdf.count()<jupyter_output><empty_output><jupyter_text>Data DescriptionThis represents a returned UMAP dataframe from raw cell data.  We're sharing post processed data for now because the exercise is on visualizaing, not running.- 'x' and 'y' are the 2D coorindates of the UMAP embeddings- 'labels' are actually clusters computed using KMeans- ACE2, TMPRSS2 and EPCAM are the gene expressions of interest- Each Gene expression has its own cluster  Let's make scatter plot!The following method uses plotly to create a [scatter plot](https://plotly.com/python/line-and-scatter/). This plot will have the following properties:- Each cell is rendered as a point at the location(x and y) computed using UMAP- Color each cell using the 'label' generated using KMeans- Set cluster label as the text to be displayed on mouse over**Pro tip**: if you need GPU acceleration on the client side, use `gl` after the hart type, like `scatter` to `scattergl`<jupyter_code>def plot_main_figure(df):
    fig = go.Figure()
    for i in range(0, len(df['labels'].unique())):
        si = str(i)
        query = 'labels == ' + si
        gdf = df.query(query)
        fig=fig.add_trace(
            go.Scattergl({
            'x': gdf['x'].to_array(),
            'y': gdf['y'].to_array(),
            'text': gdf['labels'].to_array(),
            'customdata': gdf['ACE2'].to_array(),
            'name': 'Label ' + si,
            'mode': 'markers',
            'marker': {'size': 12}
        }))
    # fig.update_layout(showlegend=True, clickmode='event+select')
    fig.update_layout(showlegend=True, clickmode='event', dragmode='select')
    return fig<jupyter_output><empty_output><jupyter_text>Let's get that view onNow lets add the scatter plot to an application and create additional [dash components](https://dash.plotly.com/) and add to the view. Dash components and plotly components can communicate with each other using events/callbacks.In this code block following actions are coded:- Create a Dash application with a default stylesheet- Set the application layout - Add dash component to the layout. Make sure to set 'id' for important components.- Define 'callback' method to handle UI events using **@app.callback** annotation. Component id's are used to correlate UI components and the callback method.    - To defind a callback following details are required:        - Dash Components that will trigger the callback        - Dash Components whose states are required to process the callback        - Dash Components that needs to be updated by the callback method (Only one callback can update a dash component)    - Following type of events are used in this application        - Hover over points in scatter plot        - Click on points        - Point Selection        - Button Click        Finally, start the application. Make sure to use a free port.<jupyter_code># Stylesheet for the application
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
# Please change the proxy_port if the port is already in use.
proxy_port = 8080

# Define the application.
# use this for when you are on more secured instances
app = JupyterDash(__name__,
                  external_stylesheets=external_stylesheets,
                  requests_pathname_prefix='/proxy/' + str(proxy_port) + '/')

# use this is for when you control all the ports
# app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

# Styles tobe used
styles = {
    'pre': {
        'border': 'thin lightgrey solid',
        'overflowX': 'scroll'
    }
}

# Create the scatter plot
fig = plot_main_figure(tdf)

#let's make your view!
app.layout = html.Div(id='site_body', children=[
        dcc.Graph(id='basic-interactions',figure=fig),
        html.Div(className='row', children=[
            html.Div([
                dcc.Markdown("""
                    **Hover Data**

                    Mouse over values in the graph.
                """),
                html.Pre(id='hover-data', style=styles['pre'])
            ], className='three columns'),


            html.Div([
                dcc.Markdown("""
                    **Click Data**

                    Click on points in the graph.
                """),
                html.Pre(id='click-data', style=styles['pre']),
            ], className='three columns'),


            html.Div([
                dcc.Markdown("""
                    **Selection Data**

                    Choose the lasso or rectangle tool in the graph's menu
                    bar and then select points in the graph.

                    Note that if `layout.clickmode = 'event+select'`, selection data also 
                    accumulates (or un-accumulates) selected data if you hold down the shift
                    button while clicking.
                """),
                html.Pre(id='selected-data', style=styles['pre']),
            ], className='three columns'),

            html.Div([
                dcc.Markdown("""
                    **Zoom and Relayout Data**

                    Click and drag on the graph to zoom or click on the zoom
                    buttons in the graph's menu bar.
                    Clicking on legend items will also fire
                    this event.
                """),
                html.Pre(id='relayout-data', style=styles['pre']),
            ], className='three columns'),
            html.Div([
                html.Button('Shutdown Dash', id='bt_close', n_clicks=0), #tried using a button
                html.Div(id='close')
            ]),
        ])
])

@app.callback(
    Output('hover-data', 'children'),
    [Input('basic-interactions', 'hoverData')])
def display_hover_data(hoverData):
    '''
    Handles onHover event on data points in the scatter plot.
    '''
    return json.dumps(hoverData, indent=2)

@app.callback(
    Output('click-data', 'children'),
    [Input('basic-interactions', 'clickData')])
def display_click_data(clickData):
    '''
    Handles onClick event on a data point in the scatter plot.
    '''
    return json.dumps(clickData, indent=2)

@app.callback(
    Output('selected-data', 'children'),
    [Input('basic-interactions', 'selectedData')])
def display_selected_data(selectedData):
    '''
    Handles onSelect event on scatter plot.
    '''
    return json.dumps(selectedData, indent=2)

@app.callback(
    Output('relayout-data', 'children'),
    [Input('basic-interactions', 'relayoutData')])
def display_relayout_data(relayoutData):
    return json.dumps(relayoutData, indent=2)

@app.callback(
    Output("site_body", "children"), 
    Input('bt_close', 'n_clicks'))
def export_current_df(export_clicks): 
    '''
    Handles a button's onClick event
    '''
    if not dash.callback_context.triggered:
        raise dash.exceptions.PreventUpdate
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()
    return 'Closed'

js = "<b style='color: red'>Please click on <a href='/proxy/" + str(proxy_port) + "/' target='_blank'>here</a> to open the dash</b>"
display(HTML(js))

app.run_server(debug=True, use_reloader=False, port=proxy_port)
## This is a sharable version of the link (for Paperspace)
# import os
# nid = os.environ['PAPERSPACE_NOTEBOOK_ID']
# cid = os.environ['PAPERSPACE_CLUSTER_ID']
# print('https://'+nid+'.'+cid+'.paperspacegradient.com/proxy/80/')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09945945945945946, 'fraction_numerical': 0.0024864864864864865, 'mean_word_length': 3.0326939843068876, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '209570', 'license': 'Apache-2.0', 'n_tokens_mistral': 2638, 'n_tokens_neox': 2540, 'n_words': 909, 'path': 'rapids-plotly-paperspace-tutorial-1.ipynb', 'repo_name': 'gradient-ai/rapids-plotly-webapps', 'size': '9250'}