1- from flask import jsonify
1+ from flask import Response , jsonify , stream_with_context
2+ from scipy .cluster .hierarchy import linkage , dendrogram
3+ import pandas as pd
24import app .models as models
35from app .config import LATEST , db
46
5- def get_gene_expr (dataset_ID : int = None , disease_name = None , ensg_number = None , gene_symbol = None , sponge_db_version : int = LATEST ):
7+ def get_gene_expr (dataset_ID : int = None , disease_name = None , ensg_number = None , gene_symbol = None , cluster : bool = False , limit : int = None , offset : int = None , sponge_db_version : int = LATEST ):
68 """
79 Handles API call /exprValue/getceRNA to get gene expression values
810 :param dataset_ID: dataset_ID of interest
911 :param disease_name: disease_name of interest
1012 :param ensg_number: esng number of the gene of interest
1113 :param gene_symbol: gene symbol of the gene of interest
1214 :param sponge_db_version: version of the database
15+ :param cluster: whether to cluster the gene expression (rows and columns)
16+ :param limit: limit the number of results
1317 :return: all expression values for the genes of interest
1418 """
15-
1619 # test if any of the two identification possibilities is given
1720 if ensg_number is None and gene_symbol is None :
1821 return jsonify ({
@@ -82,9 +85,70 @@ def get_gene_expr(dataset_ID: int = None, disease_name=None, ensg_number=None, g
8285 result = models .GeneExpressionValues .query \
8386 .filter (* queries ) \
8487 .all ()
85-
88+
8689 if len (result ) > 0 :
87- return models .geneExpressionSchema (many = True ).dump (result )
90+ # perform hierarchical clustering on rows and columns
91+ if cluster :
92+ # Convert result to a DataFrame for clustering
93+ data = pd .DataFrame ([{
94+ "gene_ID" : r .gene .gene_symbol if r .gene .gene_symbol else r .gene .ensg_number ,
95+ "sample_ID" : r .sample_ID + "___" + (
96+ str (r .dataset .disease_name ) if disease_name == "pancancer" else
97+ str (r .dataset .disease_subtype )
98+ ),
99+ "expression_value" : r .expr_value ,
100+ } for r in result ])
101+
102+ # Pivot the data to create a matrix for clustering
103+ expression_matrix = data .pivot (index = "gene_ID" , columns = "sample_ID" , values = "expression_value" ).fillna (0 )
104+
105+ # Perform hierarchical clustering on rows (genes) and columns (datasets)
106+ try :
107+ row_linkage = linkage (expression_matrix , method = 'ward' , optimal_ordering = False )
108+ col_linkage = linkage (expression_matrix .T , method = 'ward' , optimal_ordering = False )
109+ except ValueError as e :
110+ # Handle the case where the data is not suitable for clustering
111+ return jsonify ({
112+ "detail" : str (e ),
113+ "status" : 400 ,
114+ "title" : "Bad Request" ,
115+ "type" : "about:blank"
116+ }), 400
117+
118+ # Add clustering results to the response
119+ row_order = dendrogram (row_linkage , labels = expression_matrix .index , no_plot = True ).get ('leaves' )
120+ col_order = dendrogram (col_linkage , labels = expression_matrix .columns , no_plot = True ).get ('leaves' )
121+ expression_matrix = expression_matrix .iloc [row_order , col_order ]
122+
123+ result = expression_matrix .reset_index ().melt (id_vars = 'gene_ID' , var_name = 'sample_ID' , value_name = 'expression_value' )
124+ result = [models .GeneExpressionValues (gene = {"gene_symbol" : row ['gene_ID' ], "ensg_number" : None },
125+ expr_value = row ['expression_value' ],
126+ sample_ID = row ['sample_ID' ], #.split('___')[0],
127+ # note that this is 'pancancer' if the disease is 'pancancer'
128+ dataset = {"disease_subtype" : row ['sample_ID' ].split ('___' )[1 ]},
129+ )
130+ for _ , row in result .iterrows ()]
131+
132+
133+ # Limit the number of results if specified
134+ if offset is not None :
135+ result = result [offset :]
136+ if limit is not None :
137+ result = result [:limit ]
138+
139+ def _generate ():
140+ yield "["
141+ first = True
142+ for r in result :
143+ if not first :
144+ yield ","
145+ yield models .geneExpressionSchema ().dumps (r )
146+ first = False
147+ yield "]"
148+
149+ return Response (stream_with_context (_generate ()), content_type = 'application/json' )
150+
151+ # return models.geneExpressionSchema(many=True).dump(result)
88152 else :
89153 return jsonify ({
90154 "detail" : "No results." ,
@@ -93,16 +157,21 @@ def get_gene_expr(dataset_ID: int = None, disease_name=None, ensg_number=None, g
93157 "type" : "about:blank" ,
94158 "data" : []
95159 }), 200
160+
161+
162+
96163
97164
98- def get_transcript_expression (dataset_ID : int = None , disease_name : str = None , enst_number : str = None , ensg_number : str = None , gene_symbol : str = None , sponge_db_version : int = LATEST ):
165+ def get_transcript_expression (dataset_ID : int = None , disease_name : str = None , enst_number : str = None , ensg_number : str = None , gene_symbol : str = None , cluster : bool = False , limit : int = None , offset : int = None , sponge_db_version : int = LATEST ):
99166 """
100167 Handles API call /exprValue/getTranscriptExpr to return transcript expressions
101168 :param dataset_ID: dataset_ID of interest
102169 :param disease_name: Name of the disease
103170 :param enst_number: Ensembl transcript ID
104171 :param ensg_number: Ensembl gene ID
105172 :param gene_symbol: gene symbol
173+ :param limit: limit the number of results
174+ :param cluster: whether to cluster the gene expression (rows and columns)
106175 :param sponge_db_version: version of the database
107176 :return: expression values for given search parameters
108177 """
@@ -187,7 +256,59 @@ def get_transcript_expression(dataset_ID: int = None, disease_name: str = None,
187256 result = db .session .execute (query ).scalars ().all ()
188257
189258 if len (result ) > 0 :
190- return models .ExpressionDataTranscriptSchema (many = True ).dump (result )
259+ # perform hierarchical clustering on rows and columns
260+ if cluster :
261+ # Convert result to a DataFrame for clustering
262+ data = pd .DataFrame ([{
263+ "ensembl_ID" : r .transcript .enst_number + "___" + (r .transcript .gene .gene_symbol if r .transcript .gene .gene_symbol else r .transcript .gene .ensg_number ),
264+ "sample_ID" : r .sample_ID + "___" + (
265+ str (r .dataset .disease_name ) if disease_name == "pancancer" else
266+ str (r .dataset .disease_subtype )
267+ ),
268+ "expression_value" : r .expr_value ,
269+ "expression_value" : r .expr_value ,
270+ } for r in result ])
271+
272+ # Pivot the data to create a matrix for clustering
273+ expression_matrix = data .pivot (index = "ensembl_ID" , columns = "sample_ID" , values = "expression_value" ).fillna (0 )
274+
275+ # Perform hierarchical clustering on rows (genes) and columns (datasets)
276+ row_linkage = linkage (expression_matrix , method = 'ward' )
277+ col_linkage = linkage (expression_matrix .T , method = 'ward' )
278+
279+ # Add clustering results to the response
280+ row_order = dendrogram (row_linkage , labels = expression_matrix .index , no_plot = True ).get ('leaves' )
281+ col_order = dendrogram (col_linkage , labels = expression_matrix .columns , no_plot = True ).get ('leaves' )
282+ expression_matrix = expression_matrix .iloc [row_order , col_order ]
283+
284+ result = expression_matrix .reset_index ().melt (id_vars = 'ensembl_ID' , var_name = 'sample_ID' , value_name = 'expression_value' )
285+ result = [models .ExpressionDataTranscript (
286+ transcript = {"enst_number" : row ['ensembl_ID' ].split ("___" )[0 ], "gene" : {"gene_symbol" : row ['ensembl_ID' ].split ('___' )[1 ]}},
287+ expr_value = row ['expression_value' ],
288+ sample_ID = row ['sample_ID' ], #.split('___')[0],
289+ # note that this is 'pancancer' if the disease is 'pancancer'
290+ dataset = {"disease_subtype" : row ['sample_ID' ].split ('___' )[1 ]},
291+ )for _ , row in result .iterrows ()]
292+
293+ # Limit the number of results if specified
294+ if offset is not None :
295+ result = result [offset :]
296+ if limit is not None :
297+ result = result [:limit ]
298+
299+ def _generate ():
300+ yield "["
301+ first = True
302+ for r in result :
303+ if not first :
304+ yield ","
305+ yield models .ExpressionDataTranscriptSchema ().dumps (r )
306+ first = False
307+ yield "]"
308+
309+ return Response (stream_with_context (_generate ()), content_type = 'application/json' )
310+
311+ # return models.ExpressionDataTranscriptSchema(many=True).dump(result)
191312 else :
192313 return jsonify ({
193314 "detail" : "No transcript expression data found for the given filters." ,
0 commit comments