forked from fmilisav/milisav_hierarchical_modularity
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdynamics_plotting_utils.py
More file actions
203 lines (169 loc) · 7.25 KB
/
Copy pathdynamics_plotting_utils.py
File metadata and controls
203 lines (169 loc) · 7.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import os
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from score_plotting_utils import mannwhitneyu_print, create_fig_path
import pickle
from joblib import Parallel, delayed
from statsmodels.tsa.stattools import acf
import json
sns.set_style("ticks")
sns.set(context=None, style=None, palette=None, font_scale=5, color_codes=None)
plt.rcParams['svg.fonttype'] = 'none'
plt.rcParams.update({'font.size': 15})
plt.rcParams['legend.fontsize'] = 15
from matplotlib import cm
PuBu = cm.get_cmap('PuBu')
GnBu = cm.get_cmap('GnBu')
PuRd = cm.get_cmap('PuRd')
palette = [PuBu(0.6), GnBu(0.6), PuRd(0.6)]
#function to linearly fit the exponential decay of the ACF
#adapted from https://stackoverflow.com/a/3938548
def fit_exp_linear(t, y, C=0):
#cut off starting from the negative values
negatives = np.where(y < 0)[0]
if len(negatives) > 0:
first_negative = negatives[0]
t = t[:first_negative]
y = y[:first_negative]
y = y - C
y = np.log(y)
K, A_log = np.polyfit(t, y, 1)
A = np.exp(A_log)
return A, K
#function to calculate a timeseries' timescale
def timescale_analysis(timeseries):
acf_arr = acf(timeseries)
t = np.arange(len(acf_arr))
A, K = fit_exp_linear(t, acf_arr)
timescale = -1/K
return timescale
def plot_timescale(experiment_path, aggregate='std', input=False, nmodules=8,
save_timescales=True):
"""
Plots the timescale distributions as KDE plots across seeds
for different network levels at criticality
Generates statistics files for pairwise comparisons
Parameters
----------
experiment_path : str
Path to the experiment folder
aggregate : str
Aggregation method ('average', 'std', or 'cumulative')
input : bool
Whether to include input nodes in the analysis
nmodules : int
Number of modules in the network
save_timescales : bool
Whether to save the timescales DataFrame as a pickle file
"""
rs_path = os.path.join(experiment_path, 'reservoir_states')
fig_path = create_fig_path(experiment_path)
#check if the dataframe already exists
df_path = os.path.join(experiment_path,
f'timescales_df_input{input}.pickle')
if os.path.exists(df_path):
df = pd.read_pickle(df_path)
else:
data = []
seeds = []
labels = []
for rs_file in os.listdir(rs_path):
if 'critical' in rs_file:
if not input:
filename = rs_file.split('/')[-1]
#Get the input module
module = filename.split('_')[-2]
module = int(module[-1])
#Get the number of nodes in the module
#from the filename
nnodes = int(experiment_path.split('_')[3][6:])
nodes = (list(range(module*nnodes)) +
list(range((module+1)*nnodes, nmodules*nnodes)))
task = experiment_path.split('_')[1]
rs = np.load(os.path.join(rs_path, rs_file), allow_pickle=True)
if task == 'NG':
rs = np.concatenate(rs)
rs = rs.T
if not input:
rs = rs[nodes]
timescales = Parallel(n_jobs=25, verbose=0)(delayed(timescale_analysis)(timeseries) for timeseries in rs)
data.extend(timescales)
seeds.extend([rs_file.split('_')[-1].replace('.npy', '')] * len(timescales))
labels.extend([rs_file.split('_')[3][5:]] * len(timescales))
df = pd.DataFrame({'timescale': data, 'seed': seeds, 'level': labels})
df = df[df['level'] != 'MS']
if save_timescales:
df.to_pickle(os.path.join(experiment_path,
f'timescales_df_input{input}.pickle'))
if aggregate == 'average':
df = df.groupby(['seed', 'level']).mean().reset_index()
elif aggregate == 'std':
df = df.groupby(['seed', 'level']).std().reset_index()
with open(os.path.join(fig_path, f'timescale_criticality_{aggregate}_input{input}_stats.txt'), 'w') as f:
timescales_1 = df[df['level'] == '1']['timescale']
timescales_2 = df[df['level'] == '2']['timescale']
timescales_3 = df[df['level'] == '3']['timescale']
x_stats, y_stats, MWU_stats, cles_stats = mannwhitneyu_print(timescales_1, timescales_2, '1', '2')
f.write(f'{x_stats}\n{y_stats}\n{MWU_stats}\n{cles_stats}\n\n')
x_stats, y_stats, MWU_stats, cles_stats = mannwhitneyu_print(timescales_1, timescales_3, '1', '3')
f.write(f'{x_stats}\n{y_stats}\n{MWU_stats}\n{cles_stats}\n\n')
x_stats, y_stats, MWU_stats, cles_stats = mannwhitneyu_print(timescales_2, timescales_3, '2', '3')
f.write(f'{x_stats}\n{y_stats}\n{MWU_stats}\n{cles_stats}\n\n')
ax = sns.kdeplot(data=df, x='timescale',
hue='level', palette=palette,
fill=True, cut=0, alpha=0.8)
ax.set_xlabel('timescale')
ax.set_box_aspect(1)
fig = ax.get_figure()
fig.savefig(os.path.join(fig_path,
f'criticality_{aggregate}_input{input}.png'), dpi=300)
fig.savefig(os.path.join(fig_path,
f'criticality_{aggregate}_input{input}.svg'), dpi=300)
plt.close(fig)
def plot_LE_curve(experiment_path):
"""
Plots the maximum Lyapunov exponent distributions as boxplots across seeds
for different network levels and alpha values
Generates statistics files for pairwise comparisons
Parameters
----------
experiment_path : str
Path to the experiment folder
"""
LE_path = os.path.join(experiment_path, 'LE')
data = []
labels = []
alphas = []
#Get nnetworks in config.txt
with open(os.path.join(experiment_path, 'config.txt'), 'r') as f:
config = json.load(f)
nnetworks = config['nnetworks']
levels = ['1', '2', '3']
for seed in range(nnetworks):
for level in levels:
for filename in os.listdir(LE_path):
if ('LEs_alpha' in filename and f'level{level}' in filename and
f'{seed}.npy' in filename):
LEs = np.load(os.path.join(LE_path, filename),
allow_pickle=True)
data.append(LEs[0])
labels.append(level)
#Get the alpha value from the filename
alpha = filename.split('alpha')[1].split('_')[0]
alphas.append(float(alpha))
fig_path = create_fig_path(experiment_path)
df = pd.DataFrame({'maximum Lyapunov exponent': data, 'level': labels, 'alpha': alphas})
ax = sns.boxplot(x='alpha', y='maximum Lyapunov exponent', hue='level',
data=df, palette=palette,
linewidth=0.75, showfliers=False)
for _, s in ax.spines.items():
s.set_linewidth(0.5)
ax.set_box_aspect(1)
ax.set_xlabel(r'$\alpha$')
ax.set_ylabel('maximum Lyapunov exponent')
fig = ax.get_figure()
fig.savefig(os.path.join(fig_path, f'LE_curve.png'), dpi=300)
fig.savefig(os.path.join(fig_path, f'LE_curve.svg'), dpi=300)
plt.close(fig)