Arts-of-coding/testazurectrl
0
1# Dash app to visualize scRNA-seq data quality control metrics from scanpy objects2# Shoutout to Coding-with-Adam for the initial template of the project: 3# https://github.com/Coding-with-Adam/Dash-by-Plotly/blob/master/Dash%20Components/Graph/dash-graph.py4 5import dash6from dash import dcc, html, Output, Input7import plotly.express as px8import dash_callback_chain9import yaml10import polars as pl11import os12pl.enable_string_cache(False)13 14# Set custom resolution for plots:15config_fig = {16 'toImageButtonOptions': {17 'format': 'svg',18 'filename': 'custom_image',19 'height': 600,20 'width': 700,21 'scale': 1,22 }23}24from adlfs import AzureBlobFileSystem25mountpount=os.environ['AZURE_MOUNT_POINT'],26AZURE_STORAGE_ACCESS_KEY=os.getenv('AZURE_STORAGE_ACCESS_KEY')27AZURE_STORAGE_ACCOUNT=os.getenv('AZURE_STORAGE_ACCOUNT')28 29# Load in config file30config_path = "./data/config.yaml"31 32# Add the read-in data from the yaml file33def read_config(filename):34 with open(filename, 'r') as yaml_file:35 config = yaml.safe_load(yaml_file)36 return config37 38config = read_config(config_path)39path_parquet = config.get("path_parquet")40conditions = config.get("conditions")41col_features = config.get("col_features")42col_counts = config.get("col_counts")43col_mt = config.get("col_mt")44 45filepath = f"az://{path_parquet}"46 47storage_options={'account_name': AZURE_STORAGE_ACCOUNT, 'account_key': AZURE_STORAGE_ACCESS_KEY,'anon': False}48#azfs = AzureBlobFileSystem(**storage_options )49 50df = pl.read_parquet(filepath,storage_options=storage_options)51#abfs = AzureBlobFileSystem(account_name=accountname,account_key=accountkey)52 53#df = df.rename({"__index_level_0__": "Unnamed: 0"})54 55# Setup the app56external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']57app = dash.Dash(__name__, external_stylesheets=external_stylesheets) #, requests_pathname_prefix='/dashboard1/'58 59min_value = df[col_features].min()60max_value = df[col_features].max()61 62min_value_2 = df[col_counts].min()63min_value_2 = round(min_value_2)64max_value_2 = df[col_counts].max()65max_value_2 = round(max_value_2)66 67min_value_3 = df[col_mt].min()68min_value_3 = round(min_value_3, 1)69max_value_3 = df[col_mt].max()70max_value_3 = round(max_value_3, 1)71 72# Loads in the conditions specified in the yaml file73 74# Note: Future version perhaps all values from a column in the dataframe of the parquet file75# Note 2: This could also be a tsv of the categories and own specified colors76 77# Create the first tab content78# Add Sliders for three QC params: N genes by counts, total amount of reads and pct MT reads79 80tab1_content = html.Div([81 dcc.Dropdown(id='dpdn2', value=conditions, multi=True,82 options=conditions),83 html.Label("N Genes by Counts"),84 dcc.RangeSlider(85 id='range-slider-1',86 step=250,87 value=[min_value, max_value],88 marks={i: str(i) for i in range(min_value, max_value + 1, 250)},89 ),90 dcc.Input(id='min-slider-1', type='number', value=min_value, debounce=True),91 dcc.Input(id='max-slider-1', type='number', value=max_value, debounce=True),92 html.Label("Total Counts"),93 dcc.RangeSlider(94 id='range-slider-2',95 step=7500,96 value=[min_value_2, max_value_2],97 marks={i: str(i) for i in range(min_value_2, max_value_2 + 1, 7500)},98 ),99 dcc.Input(id='min-slider-2', type='number', value=min_value_2, debounce=True),100 dcc.Input(id='max-slider-2', type='number', value=max_value_2, debounce=True),101 html.Label("Percent Mitochondrial Genes"),102 dcc.RangeSlider(103 id='range-slider-3',104 step=5,105 min=0,106 max=100,107 value=[min_value_3, max_value_3],108 ),109 dcc.Input(id='min-slider-3', type='number', value=min_value_3, debounce=True),110 dcc.Input(id='max-slider-3', type='number', value=max_value_3, debounce=True),111 html.Div([112 dcc.Graph(id='pie-graph', figure={}, className='four columns',config=config_fig),113 dcc.Graph(id='my-graph', figure={}, clickData=None, hoverData=None,114 className='four columns',config=config_fig115 ),116 dcc.Graph(id='scatter-plot', figure={}, className='four columns',config=config_fig)117 ]),118 html.Div([119 dcc.Graph(id='scatter-plot-2', figure={}, className='four columns',config=config_fig)120 ]),121 html.Div([122 dcc.Graph(id='scatter-plot-3', figure={}, className='four columns',config=config_fig)123 ]),124 html.Div([125 dcc.Graph(id='scatter-plot-4', figure={}, className='four columns',config=config_fig)126 ]),127])128 129# Create the second tab content with scatter-plot-5 and scatter-plot-6130tab2_content = html.Div([131 html.Div([132 html.Label("S-cycle genes"),133 dcc.Dropdown(id='dpdn3', value="Cdc45", multi=False,134 options=[135 "Cdc45",136 "Uhrf1",137 "Mcm2",138 "Slbp",139 "Mcm5",140 "Pola1",141 "Gmnn",142 "Cdc6",143 "Rrm2",144 "Atad2",145 "Dscc1",146 "Mcm4",147 "Chaf1b",148 "Rfc2",149 "Msh2",150 "Fen1",151 "Hells",152 "Prim1",153 "Tyms",154 "Mcm6",155 "Wdr76",156 "Rad51",157 "Pcna",158 "Ccne2",159 "Casp8ap2",160 "Usp1",161 "Nasp",162 "Rpa2",163 "Ung",164 "Rad51ap1",165 "Blm",166 "Pold3",167 "Rrm1",168 "Cenpu",169 "Gins2",170 "Tipin",171 "Brip1",172 "Dtl",173 "Exo1",174 "Ubr7",175 "Clspn",176 "E2f8",177 "Cdca7"178]),179 html.Label("G2M-cycle genes"),180 dcc.Dropdown(id='dpdn4', value="Top2a", multi=False,181 options=[182 "Ube2c",183 "Lbr",184 "Ctcf",185 "Cdc20",186 "Cbx5",187 "Kif11",188 "Anp32e",189 "Birc5",190 "Cdk1",191 "Tmpo",192 "Hmmr",193 "Pimreg",194 "Aurkb",195 "Top2a",196 "Gtse1",197 "Rangap1",198 "Cdca3",199 "Ndc80",200 "Kif20b",201 "Cenpf",202 "Nek2",203 "Nuf2",204 "Nusap1",205 "Bub1",206 "Tpx2",207 "Aurka",208 "Ect2",209 "Cks1b",210 "Kif2c",211 "Cdca8",212 "Cenpa",213 "Mki67",214 "Ccnb2",215 "Kif23",216 "Smc4",217 "G2e3",218 "Tubb4b",219 "Anln",220 "Tacc3",221 "Dlgap5",222 "Ckap2",223 "Ncapd2",224 "Ttk",225 "Ckap5",226 "Cdc25c",227 "Hjurp",228 "Cenpe",229 "Ckap2l",230 "Cdca2",231 "Hmgb2",232 "Cks2",233 "Psrc1",234 "Gas2l3"235]),236 ]),237 html.Div([238 dcc.Graph(id='scatter-plot-5', figure={}, className='three columns',config=config_fig)239 ]),240 html.Div([241 dcc.Graph(id='scatter-plot-6', figure={}, className='three columns',config=config_fig)242 ]),243 html.Div([244 dcc.Graph(id='scatter-plot-7', figure={}, className='three columns',config=config_fig)245 ]),246 html.Div([247 dcc.Graph(id='scatter-plot-8', figure={}, className='three columns',config=config_fig)248 ]),249])250 251# Create the second tab content with scatter-plot-5 and scatter-plot-6252tab3_content = html.Div([253 html.Div([254 html.Label("UMAP condition 1"),255 dcc.Dropdown(id='dpdn5', value="batch", multi=False,256 options=df.columns),257 html.Label("UMAP condition 2"),258 dcc.Dropdown(id='dpdn6', value="n_genes_by_counts", multi=False,259 options=df.columns),260 html.Label("Multi gene"),261 dcc.Dropdown(id='dpdn7', value=["Cdc45","Mcm2","Mcm5"], multi=True,262 options=[263 "Cdc45",264 "Uhrf1",265 "Mcm2",266 "Slbp",267 "Mcm5",268 "Pola1",269 "Gmnn",270 "Cdc6",271 "Rrm2",272 "Atad2"]),273 ]),274 html.Div([275 dcc.Graph(id='scatter-plot-9', figure={}, className='four columns',config=config_fig)276 ]),277 html.Div([278 dcc.Graph(id='scatter-plot-10', figure={}, className='four columns',config=config_fig)279 ]),280 html.Div([281 dcc.Graph(id='scatter-plot-11', figure={}, className='four columns',config=config_fig)282 ]),283 html.Div([284 dcc.Graph(id='my-graph2', figure={}, clickData=None, hoverData=None,285 className='four columns',config=config_fig286 )287 ]),288 html.Div([289 dcc.Graph(id='scatter-plot-12', figure={}, className='four columns',config=config_fig)290 ]),291])292 293# Define the tabs layout294app.layout = html.Div([295 dcc.Tabs(id='tabs', style= {'width': 400,296 'font-size': '100%',297 'height': 50}, value='tab1',children=[298 dcc.Tab(label='QC', value='tab1', children=tab1_content),299 dcc.Tab(label='Cell cycle', value='tab2', children=tab2_content),300 dcc.Tab(label='Custom', value='tab3', children=tab3_content),301 ]),302])303 304# Define the circular callback305@app.callback(306 Output("min-slider-1", "value"),307 Output("max-slider-1", "value"),308 Output("min-slider-2", "value"),309 Output("max-slider-2", "value"),310 Output("min-slider-3", "value"),311 Output("max-slider-3", "value"),312 Input("min-slider-1", "value"),313 Input("max-slider-1", "value"),314 Input("min-slider-2", "value"),315 Input("max-slider-2", "value"),316 Input("min-slider-3", "value"),317 Input("max-slider-3", "value"),318)319def circular_callback(min_1, max_1, min_2, max_2, min_3, max_3):320 return min_1, max_1, min_2, max_2, min_3, max_3321 322@app.callback(323 Output('range-slider-1', 'value'),324 Output('range-slider-2', 'value'),325 Output('range-slider-3', 'value'),326 Input('min-slider-1', 'value'),327 Input('max-slider-1', 'value'),328 Input('min-slider-2', 'value'),329 Input('max-slider-2', 'value'),330 Input('min-slider-3', 'value'),331 Input('max-slider-3', 'value'),332)333def update_slider_values(min_1, max_1, min_2, max_2, min_3, max_3):334 return [min_1, max_1], [min_2, max_2], [min_3, max_3]335 336@app.callback(337 Output(component_id='my-graph', component_property='figure'),338 Output(component_id='pie-graph', component_property='figure'),339 Output(component_id='scatter-plot', component_property='figure'),340 Output(component_id='scatter-plot-2', component_property='figure'),341 Output(component_id='scatter-plot-3', component_property='figure'),342 Output(component_id='scatter-plot-4', component_property='figure'), # Add this new scatter plot343 Output(component_id='scatter-plot-5', component_property='figure'),344 Output(component_id='scatter-plot-6', component_property='figure'),345 Output(component_id='scatter-plot-7', component_property='figure'),346 Output(component_id='scatter-plot-8', component_property='figure'),347 Output(component_id='scatter-plot-9', component_property='figure'),348 Output(component_id='scatter-plot-10', component_property='figure'),349 Output(component_id='scatter-plot-11', component_property='figure'),350 Output(component_id='scatter-plot-12', component_property='figure'),351 Output(component_id='my-graph2', component_property='figure'),352 Input(component_id='dpdn2', component_property='value'),353 Input(component_id='dpdn3', component_property='value'),354 Input(component_id='dpdn4', component_property='value'),355 Input(component_id='dpdn5', component_property='value'),356 Input(component_id='dpdn6', component_property='value'),357 Input(component_id='dpdn7', component_property='value'),358 Input(component_id='range-slider-1', component_property='value'),359 Input(component_id='range-slider-2', component_property='value'),360 Input(component_id='range-slider-3', component_property='value')361)362 363def update_graph_and_pie_chart(batch_chosen, s_chosen, g2m_chosen, condition1_chosen, condition2_chosen, condition3_chosen, range_value_1, range_value_2, range_value_3):364 dff = df.filter(365 (pl.col('batch').cast(str).is_in(batch_chosen)) &366 (pl.col(col_features) >= range_value_1[0]) &367 (pl.col(col_features) <= range_value_1[1]) &368 (pl.col(col_counts) >= range_value_2[0]) &369 (pl.col(col_counts) <= range_value_2[1]) &370 (pl.col(col_mt) >= range_value_3[0]) &371 (pl.col(col_mt) <= range_value_3[1])372)373 374 #Drop categories that are not in the filtered data375 dff = dff.with_columns(dff['batch'].cast(pl.Categorical))376 377 # Plot figures378 fig_violin = px.violin(data_frame=dff, x='batch', y=col_features, box=True, points="all",379 color='batch', hover_name='batch',template="seaborn")380 381 # Cache commonly used subexpressions382 total_count = pl.lit(len(dff))383 category_counts = dff.group_by("batch").agg(pl.col("batch").count().alias("count"))384 category_counts = category_counts.with_columns(((pl.col("count") / total_count * 100).round(decimals=2)).alias("normalized_count"))385 386 # Display the result387 labels = category_counts["batch"].to_list()388 values = category_counts["normalized_count"].to_list()389 390 total_cells = total_count # Calculate total number of cells391 pie_title = f'Percentage of Total Cells: {total_cells}' # Include total cells in the title392 393 # Calculate the mean expression394 395 # Melt wide format DataFrame into long format396 # Specify batch column as string type and gene columns as float type397 list_conds = condition3_chosen398 list_conds += ["batch"]399 dff_pre = dff.select(list_conds)400 401 # Melt wide format DataFrame into long format402 dff_long = dff_pre.melt(id_vars="batch", variable_name="Gene", value_name="Mean expression")403 404 # Calculate the mean expression levels for each gene in each region405 expression_means = dff_long.lazy().group_by(["batch", "Gene"]).agg(pl.mean("Mean expression")).collect()406 407 # Calculate the percentage total expressed408 dff_long1 = dff_pre.melt(id_vars="batch", variable_name="Gene")#.group_by(pl.all()).agg(pl.len())409 count = 1410 dff_long2 = dff_long1.with_columns(pl.lit(count).alias("len"))411 dff_long3 = dff_long2.filter(pl.col("value") > 0).group_by(["batch", "Gene"]).agg(pl.sum("len").alias("len"))412 dff_long4 = dff_long2.group_by(["batch", "Gene"]).agg(pl.sum("len").alias("total"))413 dff_5 = dff_long4.join(dff_long3, on=["batch","Gene"], how="outer")414 result = dff_5.select([415 pl.when((pl.col('len').is_not_null()) & (pl.col('total').is_not_null()))416 .then(pl.col('len') / pl.col('total')*100)417 .otherwise(None).alias("%"),418 ])419 result = result.with_columns(pl.col("%").fill_null(100))420 dff_5[["percentage"]] = result[["%"]]421 dff_5 = dff_5.select(pl.col("batch","Gene","percentage"))422 423 # Final part to join the percentage expressed and mean expression levels424 # TO DO425 expression_means = expression_means.join(dff_5, on=["batch","Gene"], how="inner")426 427 #expression_means = expression_means.select(["batch", "Gene", "Expression"] + condition3_chosen)428 429 fig_pie = px.pie(names=labels, values=values, title=pie_title,template="seaborn")430 431 # Create the scatter plots432 fig_scatter = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color='batch',433 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},434 hover_name='batch',template="seaborn")435 436 fig_scatter_2 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=col_mt,437 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},438 hover_name='batch',template="seaborn")439 440 fig_scatter_3 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=col_features,441 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},442 hover_name='batch',template="seaborn")443 444 445 fig_scatter_4 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=col_counts,446 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},447 hover_name='batch',template="seaborn")448 449 fig_scatter_5 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=s_chosen,450 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},451 hover_name='batch', title="S-cycle gene:",template="seaborn")452 453 fig_scatter_6 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=g2m_chosen,454 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},455 hover_name='batch', title="G2M-cycle gene:",template="seaborn")456 457 fig_scatter_7 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color="S_score",458 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},459 hover_name='batch', title="S score:",template="seaborn")460 461 fig_scatter_8 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color="G2M_score",462 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},463 hover_name='batch', title="G2M score:",template="seaborn")464 465 fig_scatter_9 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=condition1_chosen,466 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},467 hover_name='batch',template="seaborn")468 469 fig_scatter_10 = px.scatter(data_frame=dff, x='X_umap-0', y='X_umap-1', color=condition2_chosen,470 labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},471 hover_name='batch',template="seaborn")472 473 fig_scatter_11 = px.scatter(data_frame=dff, x=condition1_chosen, y=condition2_chosen, color='batch',474 #labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},475 hover_name='batch',template="seaborn")476 477 fig_scatter_12 = px.scatter(data_frame=expression_means, x="Gene", y="batch", color="Mean expression",478 size="percentage", size_max = 20,479 #labels={'X_umap-0': 'umap1' , 'X_umap-1': 'umap2'},480 hover_name='batch',template="seaborn")481 482 fig_violin2 = px.violin(data_frame=dff, x=condition1_chosen, y=condition2_chosen, box=True, points="all",483 color=condition1_chosen, hover_name=condition1_chosen,template="seaborn")484 485 486 return fig_violin, fig_pie, fig_scatter, fig_scatter_2, fig_scatter_3, fig_scatter_4, fig_scatter_5, fig_scatter_6, fig_scatter_7, fig_scatter_8, fig_scatter_9, fig_scatter_10, fig_scatter_11, fig_scatter_12, fig_violin2487 488# Set http://localhost:5000/ in web browser489# Now create your regular FASTAPI application490 491if __name__ == '__main__':492 app.run_server(debug=False, use_reloader=False, host='0.0.0.0', port=5000) #493 