-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functions for correlation matrix, copula indicator and plotting #103
Changes from 14 commits
f8fd628
f3a6148
a35a65c
6b01cf5
537ebad
d8dcf86
5fa6138
b363f75
33d3094
98ffa6b
e9a23a2
7087b4b
ad2ae99
bc77cb4
e61ef3b
3fd6967
c5cb6f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -201,3 +201,127 @@ def get_matrices_of_full_dim_polytope(A, b, Aeq, beq): | |
print("gmscale failed to compute a good scaling.") | ||
|
||
return A, b, N, N_shift | ||
|
||
|
||
|
||
def correlated_reactions(steady_states, pearson_cutoff = 0.90, indicator_cutoff = 10, | ||
cells = 10, cop_coeff = 0.3, lower_triangle = True): | ||
"""A Python function to calculate the pearson correlation matrix of a model | ||
and filter values based on the copula's indicator | ||
|
||
Keyword arguments: | ||
steady_states -- A numpy array of the generated steady states fluxes | ||
pearson_cutoff -- A cutoff to filter reactions based on pearson coefficient | ||
indicator_cutoff -- A cutoff to filter reactions based on indicator value | ||
cells -- Number of cells to compute the copula | ||
cop_coeff -- A value that narrows or widens the width of the copula's diagonal | ||
lower_triangle -- A boolean variable that if True plots only the lower triangular matrix | ||
""" | ||
|
||
if cop_coeff > 0.4 or cop_coeff < 0.2: | ||
raise Exception("Input value to cop_coeff parameter must be between 0.2 and 0.4") | ||
|
||
# calculate coefficients to access red and blue copula mass | ||
cop_coeff_1 = cop_coeff | ||
cop_coeff_2 = 1 - cop_coeff | ||
cop_coeff_3 = 1 + cop_coeff | ||
|
||
# compute correlation matrix | ||
corr_matrix = np.corrcoef(steady_states, rowvar=True) | ||
|
||
# replace not assigned values with 0 | ||
corr_matrix[np.isnan(corr_matrix)] = 0 | ||
|
||
# create a copy of correlation matrix to replace/filter values | ||
filtered_corr_matrix = corr_matrix.copy() | ||
|
||
# find indices of correlation matrix where correlation does not occur | ||
no_corr_indices = np.argwhere((filtered_corr_matrix < pearson_cutoff) & (filtered_corr_matrix > -pearson_cutoff)) | ||
|
||
# replace values from the correlation matrix that do not overcome | ||
# the pearson cutoff with 0 | ||
for i in range(0, no_corr_indices.shape[0]): | ||
index1 = no_corr_indices[i][0] | ||
index2 = no_corr_indices[i][1] | ||
filtered_corr_matrix[index1, index2] = 0 | ||
|
||
# if user does not provide an indicator cutoff then do not proceed | ||
# with the filtering of the correlation matrix | ||
if indicator_cutoff == 0: | ||
if lower_triangle == True: | ||
filtered_corr_matrix[np.triu_indices(filtered_corr_matrix.shape[0], 1)] = np.nan | ||
np.fill_diagonal(filtered_corr_matrix, 1) | ||
return filtered_corr_matrix | ||
else: | ||
np.fill_diagonal(filtered_corr_matrix, 1) | ||
return filtered_corr_matrix | ||
else: | ||
|
||
# keep only the lower triangle | ||
corr_matrix = np.tril(corr_matrix) | ||
# replace diagonal values with 0 | ||
np.fill_diagonal(corr_matrix, 0) | ||
|
||
# find indices of correlation matrix where correlation occurs | ||
corr_indices = np.argwhere((corr_matrix > pearson_cutoff) | (corr_matrix < -pearson_cutoff)) | ||
|
||
# count reactions with positive or negative correlation based on indicator | ||
positive = 0 | ||
negative = 0 | ||
|
||
# compute copula for each set of correlated reactions | ||
for i in range(0, corr_indices.shape[0]): | ||
|
||
index1 = corr_indices[i][0] | ||
index2 = corr_indices[i][1] | ||
|
||
flux1 = steady_states[index1] | ||
flux2 = steady_states[index2] | ||
|
||
copula = compute_copula(flux1, flux2, cells) | ||
rows, cols = copula.shape | ||
|
||
red_mass = 0 | ||
blue_mass = 0 | ||
indicator = 0 | ||
|
||
for row in range(rows): | ||
for col in range(cols): | ||
# values in the diagonal | ||
if ((row-col >= -cop_coeff_1*rows) & (row-col <= cop_coeff_1*rows)): | ||
# values near the top left and bottom right corner | ||
if ((row+col < cop_coeff_2*rows) | (row+col > cop_coeff_3*rows)): | ||
red_mass = red_mass + copula[row][col] | ||
else: | ||
# values near the top right and bottom left corner | ||
if ((row+col >= cop_coeff_2*rows-1) & (row+col <= cop_coeff_3*rows-1)): | ||
blue_mass = blue_mass + copula[row][col] | ||
|
||
indicator = (red_mass+1e-9) / (blue_mass+1e-9) | ||
|
||
# classify specific pair of reactions as positive or negative | ||
# correlated based on indicator cutoff | ||
if indicator > indicator_cutoff: | ||
positive += 1 | ||
elif indicator < 1/indicator_cutoff: | ||
negative += 1 | ||
# if they do not overcome the cutoff replace their corresponding | ||
# value in the correlation matrix with 0 | ||
else: | ||
filtered_corr_matrix[index1, index2] = 0 | ||
filtered_corr_matrix[index2, index1] = 0 | ||
|
||
print("Completed process of",i+1,"from",corr_indices.shape[0],"copulas") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All the prints should ideally requested by the user and be off by default. |
||
|
||
|
||
print(positive, "out of", i+1, "copulas were positive correlated based on copula indicator") | ||
print(negative, "out of", i+1, "copulas were negative correlated based on copula indicator") | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The prints should be requested by the user I think. These numbers could be part of the output as with a label for each copula: "positive" or "negative" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I created a dictionary of this format that is returned
|
||
if lower_triangle == True: | ||
filtered_corr_matrix[np.triu_indices(filtered_corr_matrix.shape[0], 1)] = np.nan | ||
np.fill_diagonal(filtered_corr_matrix, 1) | ||
return filtered_corr_matrix | ||
|
||
else: | ||
np.fill_diagonal(filtered_corr_matrix, 1) | ||
return filtered_corr_matrix |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
|
||
from dingo.utils import correlated_reactions | ||
from dingo import MetabolicNetwork, PolytopeSampler | ||
import numpy as np | ||
import unittest | ||
|
||
class TestCorrelation(unittest.TestCase): | ||
|
||
def test_correlation(self): | ||
|
||
dingo_model = MetabolicNetwork.from_json('ext_data/e_coli_core.json') | ||
reactions = dingo_model.reactions | ||
|
||
sampler = PolytopeSampler(dingo_model) | ||
steady_states = sampler.generate_steady_states() | ||
|
||
# calculate correlation matrix without filtering from copula indicator | ||
corr_matrix = correlated_reactions(steady_states, | ||
indicator_cutoff=2, | ||
pearson_cutoff = 0.99999, | ||
lower_triangle = False) | ||
|
||
# sum values in the diagonal of the correlation matrix ==> 95*pearson ==> 95*1 | ||
self.assertTrue(np.trace(corr_matrix) == len(reactions)) | ||
# rows and columns must be equal to model reactions | ||
self.assertTrue(corr_matrix.shape[0] == len(reactions)) | ||
self.assertTrue(corr_matrix.shape[1] == len(reactions)) | ||
|
||
|
||
dingo_model = MetabolicNetwork.from_json('ext_data/e_coli_core.json') | ||
reactions = dingo_model.reactions | ||
|
||
sampler = PolytopeSampler(dingo_model) | ||
steady_states = sampler.generate_steady_states() | ||
|
||
# calculate correlation matrix without filtering from copula indicator | ||
corr_matrix = correlated_reactions(steady_states, | ||
indicator_cutoff=0, | ||
pearson_cutoff = 0, | ||
lower_triangle = True) | ||
|
||
# sum values in the diagonal of the correlation matrix ==> 95*pearson ==> 95*1 | ||
self.assertTrue(np.trace(corr_matrix) == len(reactions)) | ||
# rows and columns must be equal to model reactions | ||
self.assertTrue(corr_matrix.shape[0] == len(reactions)) | ||
self.assertTrue(corr_matrix.shape[1] == len(reactions)) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plz, see my comment below for the printed messages.