Created
August 25, 2017 22:11
-
-
Save olgabot/ee95e00fe4133d9cc87cd0bfc6a677d6 to your computer and use it in GitHub Desktop.
Attempt at modular dash app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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