Skip to content

Instantly share code, notes, and snippets.

@olgabot
Created August 25, 2017 22:11
Show Gist options
  • Save olgabot/ee95e00fe4133d9cc87cd0bfc6a677d6 to your computer and use it in GitHub Desktop.
Save olgabot/ee95e00fe4133d9cc87cd0bfc6a677d6 to your computer and use it in GitHub Desktop.
Attempt at modular dash app
# -*- coding: utf-8 -*-
import locale
import sys
import click
import dash
from dash.dependencies import Output, Input
import dash_html_components as html
import dash_core_components as dcc
import numpy as np
import pandas as pd
import plotly.graph_objs as go
from .gene_vs_gene import GeneVsGeneBlock
# Use commas to separate thousands
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
def run_singlecell_dash(cell_metadata, counts, group_col):
app = dash.Dash()
# Necessary with a modular layout since we add callbacks without adding a
# layout
app.config.supress_callback_exceptions = True
# creating a new MyBlock will register all callbacks
block = GeneVsGeneBlock(app, cell_metadata, counts, group_col)
import pdb; pdb.set_trace()
# now insert this component into the app's layout
app.layout = html.Div([html.H1('My App'), block.layout],
className='ten columns offset-by-one')
return app
import click
from singlecell_dash.common import TenX_Runs
from singlecell_dash.app import run_singlecell_dash
@click.command()
@click.option('--data-folder', default='data', help='Location of data files')
@click.option('--metadata', default='data/plate_metadata.csv',
help='Full path of metadata file describing each plate')
@click.option('--genes-to-drop', default='Rn45s',
help='Gene symbols to remove from the counts matrix for '
'calculating TSNE. If more than one, must be '
'comma-separated ')
@click.option('--verbose', help="Print the filenames as they are being read",
is_flag=True)
@click.option('--port', help="Changes the port where the app is being "
"hosted from", default=8050, type=int)
@click.option('--host', help="Changes the host from which the app is being "
"hosted (i.e. global or local host). Default is "
"None (localhost). Change to '0.0.0.0' for "
"global host", default=None)
@click.option('--javascript',
help="Location of an arbitrary javacsript file you want to add"
" to the Dash app", default=None)
@click.option('--debug', help="Run the Dash server in debug mode",
is_flag=True)
@click.version_option(version='v0.1.0')
def cli(data_folder, metadata, genes_to_drop, verbose, port, host, javascript,
debug):
"""Run a dashboard showing sequencing QC of single-cell RNA-seq plates"""
# plates = Plates(data_folder, metadata, genes_to_drop=genes_to_drop,
# verbose=verbose)
tenx_runs = TenX_Runs(data_folder, genes_to_drop=genes_to_drop,
verbose=verbose)
app = run_singlecell_dash(tenx_runs.cell_metadata,
tenx_runs.counts_per_million,
tenx_runs.SAMPLE_MAPPING)
# this is where the magic happens
app.run_server(host=host, debug=debug, port=port)
if __name__ == '__main__':
cli()
from dash.dependencies import Output, Input
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
import plotly.graph_objs as go
from .base import BaseBlock, CONFIG_DICT
class GeneVsGeneBlock(BaseBlock):
def __init__(self, app, cell_metadata, counts, group_col, gene_x='Actb',
gene_y='Eef1a1'):
self.cell_metadata = cell_metadata
self.counts = counts
self.group_col = group_col
self.genes = counts.columns
self.metadata_grouped = self.cell_metadata.groupby(self.group_col)
self.layout = html.Div([ # Gene v Gene plots
html.H4('Gene vs gene comparison', className='row',
style={'padding-top': '20px'}),
html.Div([
html.Div([ # gene selector 1
dcc.Dropdown(
id='xaxis-column',
options=[{'label': i, 'value': i} for i in
self.genes],
value=gene_x
),
dcc.RadioItems(
id='xaxis-type',
options=[{'label': i, 'value': i} for i in
['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
],
className='six columns'
),
html.Div([ # gene selector 2
dcc.Dropdown(
id='yaxis-column',
options=[{'label': i, 'value': i} for i in
self.genes],
value=gene_y
),
dcc.RadioItems(
id='yaxis-type',
options=[{'label': i, 'value': i} for i in
['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
],
className='six columns'
),
],
className='row'
),
# gene v gene plot
dcc.Graph(id='gene_gene_plot',
config=CONFIG_DICT.copy()),
],
className='six columns'
),
super().__init__(app)
def callbacks(self, app):
@app.callback(
Output('gene_gene_plot', 'figure'),
[Input('plate_name', 'value'),
Input('xaxis-column', 'value'),
Input('yaxis-column', 'value'),
Input('xaxis-type', 'value'),
Input('yaxis-type', 'value'),
Input('single_plate_reads_vs_genes', 'selectedData')])
def update_gene_vs_gene_scatter(group_name, xaxis_col,
yaxis_col,
xaxis_type, yaxis_type,
selectedData=None):
"""Update the gene vs gene scatter plot"""
group_barcodes = self.metadata_grouped.groups[group_name]
group_counts = self.counts.loc[group_barcodes]
alpha = pd.Series(1.0, index=group_counts.index)
hovertext = group_counts[[xaxis_col, yaxis_col]].apply(
lambda x: '{}: {}, {}'.format(x.name, x[0], x[1]), 1
)
if selectedData and selectedData['points']:
barcodes = {d['customdata'] for d in selectedData['points']}
alpha.loc[~alpha.index.isin(barcodes)] = 0.1
hovertext[~hovertext.index.isin(barcodes)] = ''
return {
'data': [
go.Scatter(x=group_counts[xaxis_col],
y=group_counts[yaxis_col],
marker={'opacity': alpha},
mode='markers',
hoverinfo='text',
text=hovertext.values)
],
'layout': go.Layout(
xaxis={
'title': xaxis_col,
'type': ('log', 'linear')[xaxis_type == 'Linear'],
},
yaxis={
'title': yaxis_col,
'type': ('log', 'linear')[
yaxis_type == 'Linear'],
'scaleanchor': 'x'
},
margin={'l': 40, 'b': 40, 't': 10, 'r': 0},
hovermode='closest')
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment