From 7faac1ae303885882d6154ca6db7db3a79be0fdf Mon Sep 17 00:00:00 2001 From: matsvanes Date: Mon, 14 Mar 2022 10:40:50 +0000 Subject: [PATCH 01/50] first effort --- app.py | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 app.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..280cf1e --- /dev/null +++ b/app.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +@author: MWJ van Es, 2022 +""" + + +import numpy as np +from scipy.stats import norm +import matplotlib.pyplot as plt + +# define standard parameters +t=np.arange(0.001, 0.101, 0.001) + +#%% example 1 +f1 = 10 # frequency of signal 1 +a1 = 1.5 # magnitude of signal 1, channel 1 +a2 = a1*0.5 # magnitude of signal 1, channel 2 +a = [a1, a2] +s1 = 0.5 # noise magnitude channel 1 +s2 = 0.5 # noise magnitude channel 2 +s = [s1, s2] +theta_a1 = -np.pi/2 +theta_a2 = -np.pi/2 +theta = [theta_a1, theta_a2] + +# signals +xa=[] +x0=[] +for k in range(2): + xa.append(a[k]*np.cos(2*np.pi*f1*t + theta[k])) # signal of condition 1, channel k + x0.append(0*t) + + +# compute mutual information +A = np.diag(a) +Sigma = np.diag(s)**2 +c_om = 0.5 * np.trace(A * np.linalg.inv(Sigma) * A * np.cos(theta-np.transpose(theta))) +temp1 = 0.5 * np.trace(A * np.linalg.inv(Sigma) * A * np.sin(theta+np.transpose(theta))) +temp2 = 0.5 * np.trace(A * np.linalg.inv(Sigma) * A * np.cos(theta+np.transpose(theta))) +r_om = np.sqrt(temp1**2 + temp2**2) +psi_om = np.arctan2(temp1, temp2) +alphaterm = c_om + r_om * np.cos(2*np.pi*2*f1*t + psi_om) + +a.append(c_om) + +grid_time = np.linspace(0,0.1,9) +grid_amp = np.linspace(-2,2,21) +grid_pow = np.linspace(0,2,21) +grid_freq = np.linspace(0,40,11) +grid_mi = np.linspace(0, 12, 21) +grid_mipow = np.linspace(0, 7, 21) + +ticks_time = np.linspace(0,0.1,3) +ticks_amp = np.linspace(-2,2,5) +ticks_freq = np.linspace(0,40,3) +ticks_pow = np.linspace(0,2,5) +ticks_mi = np.linspace(0, 12, 5) +ticks_mipow = np.linspace(0,7,5) +fig = plt.figure() +for k in range(3): + ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) + if k<2: + f=f1 + linefmt = 'b' + markerfmt = 'bo' + plt.plot(t, x0[k], 'k', linewidth=2) + plt.fill_between(t, x0[k]-s[k], x0[k]+s[k], facecolor='gray', alpha=0.3) + plt.plot(t, xa[k], 'b', linewidth=2) + plt.fill_between(t, xa[k]-s[k], xa[k]+s[k], facecolor='b', alpha=0.3) + plt.ylim((-2, 2)) + ax1.set_aspect(1./ax1.get_data_ratio()) + ax1.set_xticks(grid_time, minor=True) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),21), minor=True) + ax1.set_xticks(ticks_time) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),5)) + ax1.grid(which='both', linestyle='--', linewidth=0.5) + ax1.tick_params(which='minor', bottom=False, left=False) + plt.ylabel('Magnitude') + else: + plt.plot(t, alphaterm, linewidth=2, color='k') + plt.xlim((0, np.max(t))) + ax1.set_aspect(1./ax1.get_data_ratio()) + ax1.set_xticks(grid_time, minor=True) + ax1.set_yticks(grid_mi, minor=True) + ax1.set_xticks(ticks_time) + ax1.set_yticks(ticks_mi) + ax1.grid(which='both', linestyle='--', linewidth=0.5) + ax1.tick_params(which='minor', bottom=False, left=False) + plt.ylabel('f^-1 (I(X,Y))') + + f=2*f1 + linefmt = 'k' + markerfmt = 'ko' + + plt.xlabel('Time') + ax2 = fig.add_subplot(3,2,2*(k+1)) + markerline, stemlines, baseline = plt.stem( + [f], [a[k]], linefmt=linefmt, markerfmt=markerfmt) + markerline.set_markerfacecolor('none') + if k<2: + plt.ylim((0, 2)) + ax2.set_aspect(10*ax2.get_data_ratio()) + ax2.set_yticks(grid_pow, minor=True) + ax2.set_yticks(ticks_pow) + else: + plt.ylim((0,7)) + ax2.set_aspect(1.65*ax2.get_data_ratio()) + ax2.set_yticks(grid_mipow, minor=True) + ax2.set_yticks(ticks_mipow) + ax2.set_xticks(grid_freq, minor=True) + ax2.set_xticks(ticks_freq) + ax2.grid(which='both', linestyle='--', linewidth=0.5) + ax2.tick_params(which='minor', bottom=False, left=False) + plt.xlabel('Frequency (Hz)') + plt.ylabel('PSD') + plt.show() + + +#%% Example 2 +# This example has multiple frequncy components: +f1 = 10.0 +f2 = 1.5*f1 +f = np.array([f1, f2]) +a1a = 0.7*0.7 # magnitude of signal 1, channel 1, frequency component 1 +a1b = 1.1*0.7 # magnitude of signal 1, channel 1, frequency component 2 +a2a = 0.5*a1a # magnitude of signal 1, channel 2, frequency component 1 +a2b = 1.5 # magnitude of signal 1, channel 2, frequency component 2 +a = np.array(((a1a, a1b), (a2a, a2b))) +a01a = 0.9*0.2*a1a # magnitude of signal 2, channel 1, frequency component 1 +a01b = 0.9*0.6 # magnitude of signal 2, channel 1, frequency component 1 +a02a = a01a # magnitude of signal 2, channel 1, frequency component 1 +a02b = a01b # magnitude of signal 2, channel 1, frequency component 1 +a0 = np.array(((a01a, a01b), (a02a, a02b))) +s1 = 0.5 # noise magnitude channel 1 +s2 = 0.5 # noise magnitude channel 2 +s = np.array([s1, s2]) +theta_a1a = -np.pi/2 +theta_a1b = -np.pi/2 +theta_a2a = -np.pi/2 +theta_a2b = -np.pi/2 +theta = np.array(((theta_a1a, theta_a1b), (theta_a2a, theta_a2b))) +theta_a01a = -np.pi/2 +theta_a01b = -np.pi/2 +theta_a02a = -np.pi/2 +theta_a02b = -np.pi/2 +theta0 = np.array(((theta_a01a, theta_a01b), (theta_a02a, theta_a02b))) + + +# signals +xa=[] +x0=[] +c_om=[] +for k in range(2): + xa.append(a[k,0]*np.cos(2*np.pi*f[0]*t + theta[k,0]) + a[k,1]*np.cos(2*np.pi*f[1]*t + theta[k,1])) # first channel, first condition + x0.append(a0[k,0]*np.cos(2*np.pi*f[0]*t + theta0[k,0]) + a0[k,1]*np.cos(2*np.pi*f[1]*t + theta0[k,1])) # first channel, second condition + tmp = 0.5*np.trace(np.diag(a[:,k]) * np.linalg.inv(Sigma) * np.diag(a[:,k]) * np.cos(theta[:,k].reshape((2,1)) - theta[:,k].reshape((1,2)))) + c_om.append() + +# compute MI +Sigma = np.diag(s)**2 +alphaterm=[] +for k in range(len(t)): + alphaterm.append(np.matmul(np.matmul(np.array(((xa[0][k]-x0[0][k]), (xa[1][k]-x0[1][k]))), np.linalg.inv(Sigma)), np.array(((xa[0][k]-x0[0][k]), (xa[1][k]-x0[1][k]))))) + +c_om = np.array([1.5,1,0.3,0.4]) + + +grid_time = np.linspace(0,0.1,9) +grid_amp = np.linspace(-2,2,21) +grid_pow = np.linspace(0,2,21) +grid_freq = np.linspace(0,40,11) +grid_mi = np.linspace(0, 12, 21) +grid_mipow = np.linspace(0, 7, 21) + +ticks_time = np.linspace(0,0.1,3) +ticks_amp = np.linspace(-2,2,5) +ticks_freq = np.linspace(0,40,3) +ticks_pow = np.linspace(0,2,5) +ticks_mi = np.linspace(0, 12, 5) +ticks_mipow = np.linspace(0,7,5) +fig = plt.figure() +for k in range(3): + ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) + if k<2: + linefmt = 'b' + markerfmt = 'bo' + plt.plot(t, x0[k], 'k', linewidth=2) + plt.fill_between(t, x0[k]-s[k], x0[k]+s[k], facecolor='gray', alpha=0.3) + plt.plot(t, xa[k], 'b', linewidth=2) + plt.fill_between(t, xa[k]-s[k], xa[k]+s[k], facecolor='b', alpha=0.3) + plt.ylim((-2, 2)) + ax1.set_box_aspect(1) + ax1.set_xticks(grid_time, minor=True) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),21), minor=True) + ax1.set_xticks(ticks_time) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),5)) + ax1.grid(which='both', linestyle='--', linewidth=0.5) + ax1.tick_params(which='minor', bottom=False, left=False) + plt.ylabel('Magnitude') + else: + plt.plot(t, alphaterm, linewidth=2, color='k') + plt.xlim((0, np.max(t))) + ax1.set_box_aspect(1) + ax1.set_xticks(grid_time, minor=True) + ax1.set_yticks(grid_mi, minor=True) + ax1.set_xticks(ticks_time) + ax1.set_yticks(ticks_mi) + ax1.grid(which='both', linestyle='--', linewidth=0.5) + ax1.tick_params(which='minor', bottom=False, left=False) + plt.ylabel('f^-1 (I(X,Y))') + + linefmt = 'k' + markerfmt = 'ko' + + plt.xlabel('Time') + ax2 = fig.add_subplot(3,2,2*(k+1)) + if k<2: + markerline, stemlines, baseline = plt.stem( + 2 * f, a[k, :], linefmt=linefmt, markerfmt=markerfmt, basefmt='w') + plt.ylim((0, 2)) + ax2.set_box_aspect(1) + ax2.set_yticks(grid_pow, minor=True) + ax2.set_yticks(ticks_pow) + else: + markerline, stemlines, baseline = plt.stem( + np.concatenate((2*f, np.array((np.abs(np.diff(f))[0], np.abs(np.sum(f)))))), c_om, linefmt=linefmt, markerfmt=markerfmt, basefmt='w') + plt.ylim((0,7)) + ax2.set_box_aspect(1) + ax2.set_yticks(grid_mipow, minor=True) + ax2.set_yticks(ticks_mipow) + markerline.set_markerfacecolor('none') + ax2.set_xticks(grid_freq, minor=True) + ax2.set_xticks(ticks_freq) + ax2.grid(which='both', linestyle='--', linewidth=0.5) + ax2.tick_params(which='minor', bottom=False, left=False) + plt.xlabel('Frequency (Hz)') + plt.ylabel('PSD') + plt.show() + + + + + From 7a4d08fc353a20228207d5b8214714c7b921b5d7 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 16 Mar 2022 16:40:03 +0000 Subject: [PATCH 02/50] implement working example 1 + 2 --- app.py | 372 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 183 insertions(+), 189 deletions(-) diff --git a/app.py b/app.py index 280cf1e..a892d30 100644 --- a/app.py +++ b/app.py @@ -4,241 +4,235 @@ @author: MWJ van Es, 2022 """ - import numpy as np from scipy.stats import norm import matplotlib.pyplot as plt # define standard parameters -t=np.arange(0.001, 0.101, 0.001) - -#%% example 1 -f1 = 10 # frequency of signal 1 -a1 = 1.5 # magnitude of signal 1, channel 1 -a2 = a1*0.5 # magnitude of signal 1, channel 2 -a = [a1, a2] -s1 = 0.5 # noise magnitude channel 1 -s2 = 0.5 # noise magnitude channel 2 -s = [s1, s2] -theta_a1 = -np.pi/2 -theta_a2 = -np.pi/2 -theta = [theta_a1, theta_a2] - -# signals -xa=[] -x0=[] -for k in range(2): - xa.append(a[k]*np.cos(2*np.pi*f1*t + theta[k])) # signal of condition 1, channel k - x0.append(0*t) - - -# compute mutual information -A = np.diag(a) -Sigma = np.diag(s)**2 -c_om = 0.5 * np.trace(A * np.linalg.inv(Sigma) * A * np.cos(theta-np.transpose(theta))) -temp1 = 0.5 * np.trace(A * np.linalg.inv(Sigma) * A * np.sin(theta+np.transpose(theta))) -temp2 = 0.5 * np.trace(A * np.linalg.inv(Sigma) * A * np.cos(theta+np.transpose(theta))) -r_om = np.sqrt(temp1**2 + temp2**2) -psi_om = np.arctan2(temp1, temp2) -alphaterm = c_om + r_om * np.cos(2*np.pi*2*f1*t + psi_om) - -a.append(c_om) - -grid_time = np.linspace(0,0.1,9) -grid_amp = np.linspace(-2,2,21) -grid_pow = np.linspace(0,2,21) -grid_freq = np.linspace(0,40,11) -grid_mi = np.linspace(0, 12, 21) -grid_mipow = np.linspace(0, 7, 21) - -ticks_time = np.linspace(0,0.1,3) -ticks_amp = np.linspace(-2,2,5) -ticks_freq = np.linspace(0,40,3) -ticks_pow = np.linspace(0,2,5) -ticks_mi = np.linspace(0, 12, 5) -ticks_mipow = np.linspace(0,7,5) -fig = plt.figure() -for k in range(3): - ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) - if k<2: - f=f1 - linefmt = 'b' - markerfmt = 'bo' - plt.plot(t, x0[k], 'k', linewidth=2) - plt.fill_between(t, x0[k]-s[k], x0[k]+s[k], facecolor='gray', alpha=0.3) - plt.plot(t, xa[k], 'b', linewidth=2) - plt.fill_between(t, xa[k]-s[k], xa[k]+s[k], facecolor='b', alpha=0.3) - plt.ylim((-2, 2)) - ax1.set_aspect(1./ax1.get_data_ratio()) - ax1.set_xticks(grid_time, minor=True) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),21), minor=True) - ax1.set_xticks(ticks_time) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),5)) - ax1.grid(which='both', linestyle='--', linewidth=0.5) - ax1.tick_params(which='minor', bottom=False, left=False) - plt.ylabel('Magnitude') - else: - plt.plot(t, alphaterm, linewidth=2, color='k') - plt.xlim((0, np.max(t))) - ax1.set_aspect(1./ax1.get_data_ratio()) - ax1.set_xticks(grid_time, minor=True) - ax1.set_yticks(grid_mi, minor=True) - ax1.set_xticks(ticks_time) - ax1.set_yticks(ticks_mi) - ax1.grid(which='both', linestyle='--', linewidth=0.5) - ax1.tick_params(which='minor', bottom=False, left=False) - plt.ylabel('f^-1 (I(X,Y))') - - f=2*f1 - linefmt = 'k' - markerfmt = 'ko' - - plt.xlabel('Time') - ax2 = fig.add_subplot(3,2,2*(k+1)) - markerline, stemlines, baseline = plt.stem( - [f], [a[k]], linefmt=linefmt, markerfmt=markerfmt) - markerline.set_markerfacecolor('none') - if k<2: - plt.ylim((0, 2)) - ax2.set_aspect(10*ax2.get_data_ratio()) - ax2.set_yticks(grid_pow, minor=True) - ax2.set_yticks(ticks_pow) - else: - plt.ylim((0,7)) - ax2.set_aspect(1.65*ax2.get_data_ratio()) - ax2.set_yticks(grid_mipow, minor=True) - ax2.set_yticks(ticks_mipow) - ax2.set_xticks(grid_freq, minor=True) - ax2.set_xticks(ticks_freq) - ax2.grid(which='both', linestyle='--', linewidth=0.5) - ax2.tick_params(which='minor', bottom=False, left=False) - plt.xlabel('Frequency (Hz)') - plt.ylabel('PSD') - plt.show() - - -#%% Example 2 -# This example has multiple frequncy components: -f1 = 10.0 -f2 = 1.5*f1 -f = np.array([f1, f2]) -a1a = 0.7*0.7 # magnitude of signal 1, channel 1, frequency component 1 -a1b = 1.1*0.7 # magnitude of signal 1, channel 1, frequency component 2 -a2a = 0.5*a1a # magnitude of signal 1, channel 2, frequency component 1 -a2b = 1.5 # magnitude of signal 1, channel 2, frequency component 2 +t = np.arange(0.001, 0.101, 0.001) +example = 2 +if example == 1: + f1 = 10.0 + f2 = 0 + a1a = 1.5 # magnitude of signal 1, channel 1, frequency component 1 + a1b = 0 # magnitude of signal 1, channel 1, frequency component 2 + a2a = 0.5 * a1a # magnitude of signal 1, channel 2, frequency component 1 + a2b = 0 # magnitude of signal 1, channel 2, frequency component 2 + a01a = 0 # magnitude of signal 2, channel 1, frequency component 1 + a01b = 0 # magnitude of signal 2, channel 1, frequency component 1 + a02a = 0 # magnitude of signal 2, channel 1, frequency component 1 + a02b = 0 # magnitude of signal 2, channel 1, frequency component 1 + s1 = 0.5 # noise magnitude channel 1 + s2 = 0.5 # noise magnitude channel 2 + theta_a1a = -np.pi / 2 + theta_a1b = -np.pi / 2 + theta_a2a = -np.pi / 2 + theta_a2b = -np.pi / 2 + theta_a01a = -np.pi / 2 + theta_a01b = -np.pi / 2 + theta_a02a = -np.pi / 2 + theta_a02b = -np.pi / 2 + + ticks_amp = np.linspace(-2, 2, 5) + ticks_pow = np.linspace(0, 2, 5) + ticks_mi = np.linspace(0, 6, 4) + ticks_mipow = np.linspace(0, 3, 4) + + grid_time = np.linspace(0, 0.1, 9) + grid_amp = np.linspace(-2, 2, 21) + grid_pow = np.linspace(0, 2, 21) + grid_freq = np.linspace(0, 40, 11) + grid_mi = np.linspace(0, 12, 21) + grid_mipow = np.linspace(0, 7, 21) + + ylim0 = (0, 2) + ylim1 = (0, 6) + ylim2 = (0, 3) +elif example == 2: + # This example has multiple frequncy components: + f1 = 10.0 + f2 = 1.5 * f1 + a1a = 0.5 # magnitude of signal 1, channel 1, frequency component 1 + a1b = 0.77 # magnitude of signal 1, channel 1, frequency component 2 + a2a = 0.5 * a1a # magnitude of signal 1, channel 2, frequency component 1 + a2b = 1.5 # magnitude of signal 1, channel 2, frequency component 2 + a01a = 0.09 # magnitude of signal 2, channel 1, frequency component 1 + a01b = 0.9 * 0.6 # magnitude of signal 2, channel 1, frequency component 1 + a02a = a01a # magnitude of signal 2, channel 1, frequency component 1 + a02b = a01b # magnitude of signal 2, channel 1, frequency component 1 + s1 = 0.5 # noise magnitude channel 1 + s2 = 0.5 # noise magnitude channel 2 + theta_a1a = -np.pi / 2 + theta_a1b = -np.pi / 2 + theta_a2a = -np.pi / 2 + theta_a2b = -np.pi / 2 + theta_a01a = -np.pi / 2 + theta_a01b = -np.pi / 2 + theta_a02a = -np.pi / 2 + theta_a02b = -np.pi / 2 + + ticks_amp = np.linspace(-2, 2, 5) + ticks_pow = np.linspace(0, 2, 5) + ticks_mi = np.linspace(0, 1.5, 4) + ticks_mipow = np.linspace(0, 0.5, 6) + + grid_time = np.linspace(0, 0.1, 9) + grid_amp = np.linspace(-2, 2, 21) + grid_pow = np.linspace(0, 2, 21) + grid_freq = np.linspace(0, 40, 11) + grid_mi = np.linspace(0, 12, 21) + grid_mipow = np.linspace(0, 7, 21) + + ylim0 = (0, 2) + ylim1 = (0, 1.5) + ylim2 = (0, 0.5) +else: + print('this is the interactive part') + +ticks_time = np.linspace(0, 0.1, 3) +ticks_freq = np.linspace(0, 40, 3) + +# combine parameters in arrays a = np.array(((a1a, a1b), (a2a, a2b))) -a01a = 0.9*0.2*a1a # magnitude of signal 2, channel 1, frequency component 1 -a01b = 0.9*0.6 # magnitude of signal 2, channel 1, frequency component 1 -a02a = a01a # magnitude of signal 2, channel 1, frequency component 1 -a02b = a01b # magnitude of signal 2, channel 1, frequency component 1 a0 = np.array(((a01a, a01b), (a02a, a02b))) -s1 = 0.5 # noise magnitude channel 1 -s2 = 0.5 # noise magnitude channel 2 +f = np.array([f1, f2]) s = np.array([s1, s2]) -theta_a1a = -np.pi/2 -theta_a1b = -np.pi/2 -theta_a2a = -np.pi/2 -theta_a2b = -np.pi/2 +Sig = np.diag(s) ** 2 theta = np.array(((theta_a1a, theta_a1b), (theta_a2a, theta_a2b))) -theta_a01a = -np.pi/2 -theta_a01b = -np.pi/2 -theta_a02a = -np.pi/2 -theta_a02b = -np.pi/2 theta0 = np.array(((theta_a01a, theta_a01b), (theta_a02a, theta_a02b))) - # signals -xa=[] -x0=[] -c_om=[] +xa = [] +x0 = [] for k in range(2): - xa.append(a[k,0]*np.cos(2*np.pi*f[0]*t + theta[k,0]) + a[k,1]*np.cos(2*np.pi*f[1]*t + theta[k,1])) # first channel, first condition - x0.append(a0[k,0]*np.cos(2*np.pi*f[0]*t + theta0[k,0]) + a0[k,1]*np.cos(2*np.pi*f[1]*t + theta0[k,1])) # first channel, second condition - tmp = 0.5*np.trace(np.diag(a[:,k]) * np.linalg.inv(Sigma) * np.diag(a[:,k]) * np.cos(theta[:,k].reshape((2,1)) - theta[:,k].reshape((1,2)))) - c_om.append() - -# compute MI -Sigma = np.diag(s)**2 -alphaterm=[] -for k in range(len(t)): - alphaterm.append(np.matmul(np.matmul(np.array(((xa[0][k]-x0[0][k]), (xa[1][k]-x0[1][k]))), np.linalg.inv(Sigma)), np.array(((xa[0][k]-x0[0][k]), (xa[1][k]-x0[1][k]))))) - -c_om = np.array([1.5,1,0.3,0.4]) - - -grid_time = np.linspace(0,0.1,9) -grid_amp = np.linspace(-2,2,21) -grid_pow = np.linspace(0,2,21) -grid_freq = np.linspace(0,40,11) -grid_mi = np.linspace(0, 12, 21) -grid_mipow = np.linspace(0, 7, 21) - -ticks_time = np.linspace(0,0.1,3) -ticks_amp = np.linspace(-2,2,5) -ticks_freq = np.linspace(0,40,3) -ticks_pow = np.linspace(0,2,5) -ticks_mi = np.linspace(0, 12, 5) -ticks_mipow = np.linspace(0,7,5) + xa.append(np.matmul(np.expand_dims(a[k, :], 0), np.cos( + 2 * np.pi * np.expand_dims(f, 1) * np.transpose(np.expand_dims(t, 1)) + np.expand_dims(theta[k, :], 1)))[ + 0]) # first channel, first condition + x0.append(np.matmul(np.expand_dims(a0[k, :], 0), np.cos( + 2 * np.pi * np.expand_dims(f, 1) * np.transpose(np.expand_dims(t, 1)) + np.expand_dims(theta0[k, :], 1)))[0]) + +# now we need to map these two channel signals back to the format given in the paper: +A_omega = [] +mu_omega = [] +phi_omega = [] +phi_mean = [] +for ifreq in np.arange(2): + A_omega.append(0.5 * np.diag(np.sqrt(a[:, ifreq] ** 2 + a0[:, ifreq] ** 2 + + 2 * a[:, ifreq] * a0[:, ifreq] * np.cos( + theta[:, ifreq] - theta0[:, ifreq] + np.array((-np.pi, -np.pi)))))) + mu_omega.append(0.5 * np.diag(np.sqrt(a[:, ifreq] ** 2 + a0[:, ifreq] ** 2 + + 2 * a[:, ifreq] * a0[:, ifreq] * np.cos(theta[:, ifreq] - theta0[:, ifreq])))) + phi_omega.append(np.arctan2(a[:, ifreq] * np.sin(theta[:, ifreq]) + a0[:, ifreq] * np.sin(theta0[:, ifreq] + np.pi), + a[:, ifreq] * np.cos(theta[:, ifreq]) + a0[:, ifreq] * np.cos( + theta0[:, ifreq] + np.pi))) + phi_mean.append(-np.pi / 2 + np.arctan2( + a[:, ifreq] * np.sin(theta[:, ifreq] + np.pi / 2) + a0[:, ifreq] * np.sin(theta0[:, ifreq] + np.pi / 2), + a[:, ifreq] * np.cos(theta[:, ifreq] + np.pi / 2) + a0[:, ifreq] * np.cos(theta0[:, ifreq] + np.pi / 2))) + +# find the information content terms: +c_b = 0 +if len(f[f > 0]) == 1: + Sigma = Sig +else: + Sigma = Sig + Sig # broadband noise: sum over both frequency bands +r_b = [] +psi = [] +for ifreq in np.arange(2): + c_b = c_b + np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), A_omega[ifreq]) * np.cos( + np.expand_dims(phi_omega[ifreq], 1) - phi_omega[ifreq])) # equal to above expression + temp1 = np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), + A_omega[ifreq] * np.cos(np.expand_dims(phi_omega[ifreq], 1) + phi_omega[ifreq]))) + temp2 = np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), + A_omega[ifreq] * np.sin(np.expand_dims(phi_omega[ifreq], 1) + phi_omega[ifreq]))) + r_b.append(np.sqrt(temp1 ** 2 + temp2 ** 2)) + psi.append(np.arctan2(temp2, temp1)) + +# and cross-frequency components: +posmin = [1, -1] +for i in np.arange(2): + temp1 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), + A_omega[1] * np.cos(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) + temp2 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), + A_omega[1] * np.sin(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) + r_b.append(2 * np.sqrt(temp1 ** 2 + temp2 ** 2)) + psi.append(np.arctan2(temp2, temp1)) +r_b = np.array(r_b) + +infotermest = c_b +freqs_all = np.concatenate((2 * f, np.array([np.sum(f)]), np.diff(f))) +for ifreq in np.arange(4): + infotermest = infotermest + r_b[ifreq] * np.cos(2 * np.pi * freqs_all[ifreq] * t + psi[ifreq]) + +# we do not need the alternative computation alphaterm = np.zeros((1,len(t))) for i in np.arange(len(t)): alphaterm[ +# 0][i] = 2*np.matmul(np.matmul(np.expand_dims(np.array(((xa[0][i]-x0[0][i])/2, (xa[1][i]-x0[1][i])/2)),0), +# np.linalg.inv(Sigma)), np.expand_dims(np.array(((xa[0][i] - x0[0][i]) / 2, (xa[1][i] - x0[1][i]) / 2)), 1))[0][0] + +if not np.logical_or(example == 1, example == 2): + ticks_amp = np.linspace(np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), + np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1]))), + np.diff((np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), + np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))))[0] + 1) + ticks_pow = np.linspace(0, np.max((np.max(a), np.max(a0))), 2 * np.max((np.max(a), np.max(a0))) + 1) + ticks_mi = np.linspace(0, np.ceil(np.max(infotermest)), 2 * np.ceil(np.max(infotermest)) + 1) + ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), 2 * np.ceil(np.max(r_b)) + 1) +#%% Plot everything fig = plt.figure() for k in range(3): ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) - if k<2: - linefmt = 'b' - markerfmt = 'bo' + if k < 2: plt.plot(t, x0[k], 'k', linewidth=2) - plt.fill_between(t, x0[k]-s[k], x0[k]+s[k], facecolor='gray', alpha=0.3) + plt.fill_between(t, x0[k] - s[k], x0[k] + s[k], facecolor='gray', alpha=0.3) plt.plot(t, xa[k], 'b', linewidth=2) - plt.fill_between(t, xa[k]-s[k], xa[k]+s[k], facecolor='b', alpha=0.3) - plt.ylim((-2, 2)) + plt.fill_between(t, xa[k] - s[k], xa[k] + s[k], facecolor='b', alpha=0.3) ax1.set_box_aspect(1) ax1.set_xticks(grid_time, minor=True) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),21), minor=True) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 21), minor=True) ax1.set_xticks(ticks_time) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k]+s[k])),np.ceil(np.max(xa[k]+s[k])),5)) - ax1.grid(which='both', linestyle='--', linewidth=0.5) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 5)) + # ax1.grid(which='both', linestyle='--', linewidth=0.5) ax1.tick_params(which='minor', bottom=False, left=False) plt.ylabel('Magnitude') + plt.ylim((-2, 2)) else: - plt.plot(t, alphaterm, linewidth=2, color='k') + plt.plot(t, np.squeeze(infotermest), linewidth=2, color='k') plt.xlim((0, np.max(t))) ax1.set_box_aspect(1) ax1.set_xticks(grid_time, minor=True) ax1.set_yticks(grid_mi, minor=True) ax1.set_xticks(ticks_time) ax1.set_yticks(ticks_mi) - ax1.grid(which='both', linestyle='--', linewidth=0.5) + # ax1.grid(which='both', linestyle='--', linewidth=0.5) ax1.tick_params(which='minor', bottom=False, left=False) plt.ylabel('f^-1 (I(X,Y))') - - linefmt = 'k' - markerfmt = 'ko' + plt.ylim(ylim1) plt.xlabel('Time') - ax2 = fig.add_subplot(3,2,2*(k+1)) - if k<2: - markerline, stemlines, baseline = plt.stem( - 2 * f, a[k, :], linefmt=linefmt, markerfmt=markerfmt, basefmt='w') + ax2 = fig.add_subplot(3, 2, 2 * (k + 1)) + if k < 2: + if np.any(a0[k, :] > 0): + markerline, stemlines, baseline = plt.stem( + f[a0[k, :] > 0], a0[k, a0[k, :] > 0], linefmt='k', markerfmt='ko', basefmt='w') + markerline.set_markerfacecolor('none') + if np.any(a[k, :] > 0): + markerline, stemlines, baseline = plt.stem( + f[a[k, :] > 0], a[k, a[k, :] > 0], linefmt='b', markerfmt='bo', basefmt='w') + markerline.set_markerfacecolor('none') plt.ylim((0, 2)) ax2.set_box_aspect(1) ax2.set_yticks(grid_pow, minor=True) ax2.set_yticks(ticks_pow) else: markerline, stemlines, baseline = plt.stem( - np.concatenate((2*f, np.array((np.abs(np.diff(f))[0], np.abs(np.sum(f)))))), c_om, linefmt=linefmt, markerfmt=markerfmt, basefmt='w') - plt.ylim((0,7)) + freqs_all[r_b > 0], r_b[r_b > 0], linefmt='k', markerfmt='ko', basefmt='w') ax2.set_box_aspect(1) ax2.set_yticks(grid_mipow, minor=True) ax2.set_yticks(ticks_mipow) - markerline.set_markerfacecolor('none') + plt.ylim(ylim2) + markerline.set_markerfacecolor('none') ax2.set_xticks(grid_freq, minor=True) ax2.set_xticks(ticks_freq) - ax2.grid(which='both', linestyle='--', linewidth=0.5) + # ax2.grid(which='both', linestyle='--', linewidth=0.5) ax2.tick_params(which='minor', bottom=False, left=False) plt.xlabel('Frequency (Hz)') plt.ylabel('PSD') plt.show() - - - - - From 6e257ab168c6f853ff10357bb4727e33db671196 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 16 Mar 2022 18:11:50 +0000 Subject: [PATCH 03/50] add app html components --- app.py | 632 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 408 insertions(+), 224 deletions(-) diff --git a/app.py b/app.py index a892d30..52d4e0a 100644 --- a/app.py +++ b/app.py @@ -5,234 +5,418 @@ """ import numpy as np -from scipy.stats import norm import matplotlib.pyplot as plt +import dash +from dash.dependencies import Input, Output, State +import plotly.express as px +import pandas as pd +import emd +import numpy as np +from scipy.fft import fft, fftfreq +from scipy import stats +from dash import html, dcc +import dash_bootstrap_components as dbc -# define standard parameters -t = np.arange(0.001, 0.101, 0.001) -example = 2 -if example == 1: - f1 = 10.0 - f2 = 0 - a1a = 1.5 # magnitude of signal 1, channel 1, frequency component 1 - a1b = 0 # magnitude of signal 1, channel 1, frequency component 2 - a2a = 0.5 * a1a # magnitude of signal 1, channel 2, frequency component 1 - a2b = 0 # magnitude of signal 1, channel 2, frequency component 2 - a01a = 0 # magnitude of signal 2, channel 1, frequency component 1 - a01b = 0 # magnitude of signal 2, channel 1, frequency component 1 - a02a = 0 # magnitude of signal 2, channel 1, frequency component 1 - a02b = 0 # magnitude of signal 2, channel 1, frequency component 1 - s1 = 0.5 # noise magnitude channel 1 - s2 = 0.5 # noise magnitude channel 2 - theta_a1a = -np.pi / 2 - theta_a1b = -np.pi / 2 - theta_a2a = -np.pi / 2 - theta_a2b = -np.pi / 2 - theta_a01a = -np.pi / 2 - theta_a01b = -np.pi / 2 - theta_a02a = -np.pi / 2 - theta_a02b = -np.pi / 2 - - ticks_amp = np.linspace(-2, 2, 5) - ticks_pow = np.linspace(0, 2, 5) - ticks_mi = np.linspace(0, 6, 4) - ticks_mipow = np.linspace(0, 3, 4) - - grid_time = np.linspace(0, 0.1, 9) - grid_amp = np.linspace(-2, 2, 21) - grid_pow = np.linspace(0, 2, 21) - grid_freq = np.linspace(0, 40, 11) - grid_mi = np.linspace(0, 12, 21) - grid_mipow = np.linspace(0, 7, 21) - ylim0 = (0, 2) - ylim1 = (0, 6) - ylim2 = (0, 3) -elif example == 2: - # This example has multiple frequncy components: - f1 = 10.0 - f2 = 1.5 * f1 - a1a = 0.5 # magnitude of signal 1, channel 1, frequency component 1 - a1b = 0.77 # magnitude of signal 1, channel 1, frequency component 2 - a2a = 0.5 * a1a # magnitude of signal 1, channel 2, frequency component 1 - a2b = 1.5 # magnitude of signal 1, channel 2, frequency component 2 - a01a = 0.09 # magnitude of signal 2, channel 1, frequency component 1 - a01b = 0.9 * 0.6 # magnitude of signal 2, channel 1, frequency component 1 - a02a = a01a # magnitude of signal 2, channel 1, frequency component 1 - a02b = a01b # magnitude of signal 2, channel 1, frequency component 1 - s1 = 0.5 # noise magnitude channel 1 - s2 = 0.5 # noise magnitude channel 2 - theta_a1a = -np.pi / 2 - theta_a1b = -np.pi / 2 - theta_a2a = -np.pi / 2 - theta_a2b = -np.pi / 2 - theta_a01a = -np.pi / 2 - theta_a01b = -np.pi / 2 - theta_a02a = -np.pi / 2 - theta_a02b = -np.pi / 2 - - ticks_amp = np.linspace(-2, 2, 5) - ticks_pow = np.linspace(0, 2, 5) - ticks_mi = np.linspace(0, 1.5, 4) - ticks_mipow = np.linspace(0, 0.5, 6) - - grid_time = np.linspace(0, 0.1, 9) - grid_amp = np.linspace(-2, 2, 21) - grid_pow = np.linspace(0, 2, 21) - grid_freq = np.linspace(0, 40, 11) - grid_mi = np.linspace(0, 12, 21) - grid_mipow = np.linspace(0, 7, 21) +def card(header, name, props, traffic_light=''): + card_content = [ + dbc.CardBody( + [ + html.H4(header, className="card-title", id=header), + html.Div( + style={'width': '90%'}, + children=[ - ylim0 = (0, 2) - ylim1 = (0, 1.5) - ylim2 = (0, 0.5) -else: - print('this is the interactive part') - -ticks_time = np.linspace(0, 0.1, 3) -ticks_freq = np.linspace(0, 40, 3) - -# combine parameters in arrays -a = np.array(((a1a, a1b), (a2a, a2b))) -a0 = np.array(((a01a, a01b), (a02a, a02b))) -f = np.array([f1, f2]) -s = np.array([s1, s2]) -Sig = np.diag(s) ** 2 -theta = np.array(((theta_a1a, theta_a1b), (theta_a2a, theta_a2b))) -theta0 = np.array(((theta_a01a, theta_a01b), (theta_a02a, theta_a02b))) - -# signals -xa = [] -x0 = [] -for k in range(2): - xa.append(np.matmul(np.expand_dims(a[k, :], 0), np.cos( - 2 * np.pi * np.expand_dims(f, 1) * np.transpose(np.expand_dims(t, 1)) + np.expand_dims(theta[k, :], 1)))[ - 0]) # first channel, first condition - x0.append(np.matmul(np.expand_dims(a0[k, :], 0), np.cos( - 2 * np.pi * np.expand_dims(f, 1) * np.transpose(np.expand_dims(t, 1)) + np.expand_dims(theta0[k, :], 1)))[0]) - -# now we need to map these two channel signals back to the format given in the paper: -A_omega = [] -mu_omega = [] -phi_omega = [] -phi_mean = [] -for ifreq in np.arange(2): - A_omega.append(0.5 * np.diag(np.sqrt(a[:, ifreq] ** 2 + a0[:, ifreq] ** 2 + - 2 * a[:, ifreq] * a0[:, ifreq] * np.cos( - theta[:, ifreq] - theta0[:, ifreq] + np.array((-np.pi, -np.pi)))))) - mu_omega.append(0.5 * np.diag(np.sqrt(a[:, ifreq] ** 2 + a0[:, ifreq] ** 2 + - 2 * a[:, ifreq] * a0[:, ifreq] * np.cos(theta[:, ifreq] - theta0[:, ifreq])))) - phi_omega.append(np.arctan2(a[:, ifreq] * np.sin(theta[:, ifreq]) + a0[:, ifreq] * np.sin(theta0[:, ifreq] + np.pi), - a[:, ifreq] * np.cos(theta[:, ifreq]) + a0[:, ifreq] * np.cos( - theta0[:, ifreq] + np.pi))) - phi_mean.append(-np.pi / 2 + np.arctan2( - a[:, ifreq] * np.sin(theta[:, ifreq] + np.pi / 2) + a0[:, ifreq] * np.sin(theta0[:, ifreq] + np.pi / 2), - a[:, ifreq] * np.cos(theta[:, ifreq] + np.pi / 2) + a0[:, ifreq] * np.cos(theta0[:, ifreq] + np.pi / 2))) - -# find the information content terms: -c_b = 0 -if len(f[f > 0]) == 1: - Sigma = Sig -else: - Sigma = Sig + Sig # broadband noise: sum over both frequency bands -r_b = [] -psi = [] -for ifreq in np.arange(2): - c_b = c_b + np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), A_omega[ifreq]) * np.cos( - np.expand_dims(phi_omega[ifreq], 1) - phi_omega[ifreq])) # equal to above expression - temp1 = np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), - A_omega[ifreq] * np.cos(np.expand_dims(phi_omega[ifreq], 1) + phi_omega[ifreq]))) - temp2 = np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), - A_omega[ifreq] * np.sin(np.expand_dims(phi_omega[ifreq], 1) + phi_omega[ifreq]))) - r_b.append(np.sqrt(temp1 ** 2 + temp2 ** 2)) - psi.append(np.arctan2(temp2, temp1)) - -# and cross-frequency components: -posmin = [1, -1] -for i in np.arange(2): - temp1 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), - A_omega[1] * np.cos(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) - temp2 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), - A_omega[1] * np.sin(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) - r_b.append(2 * np.sqrt(temp1 ** 2 + temp2 ** 2)) - psi.append(np.arctan2(temp2, temp1)) -r_b = np.array(r_b) - -infotermest = c_b -freqs_all = np.concatenate((2 * f, np.array([np.sum(f)]), np.diff(f))) -for ifreq in np.arange(4): - infotermest = infotermest + r_b[ifreq] * np.cos(2 * np.pi * freqs_all[ifreq] * t + psi[ifreq]) - -# we do not need the alternative computation alphaterm = np.zeros((1,len(t))) for i in np.arange(len(t)): alphaterm[ -# 0][i] = 2*np.matmul(np.matmul(np.expand_dims(np.array(((xa[0][i]-x0[0][i])/2, (xa[1][i]-x0[1][i])/2)),0), -# np.linalg.inv(Sigma)), np.expand_dims(np.array(((xa[0][i] - x0[0][i]) / 2, (xa[1][i] - x0[1][i]) / 2)), 1))[0][0] - -if not np.logical_or(example == 1, example == 2): - ticks_amp = np.linspace(np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), - np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1]))), - np.diff((np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), - np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))))[0] + 1) - ticks_pow = np.linspace(0, np.max((np.max(a), np.max(a0))), 2 * np.max((np.max(a), np.max(a0))) + 1) - ticks_mi = np.linspace(0, np.ceil(np.max(infotermest)), 2 * np.ceil(np.max(infotermest)) + 1) - ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), 2 * np.ceil(np.max(r_b)) + 1) -#%% Plot everything -fig = plt.figure() -for k in range(3): - ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) - if k < 2: - plt.plot(t, x0[k], 'k', linewidth=2) - plt.fill_between(t, x0[k] - s[k], x0[k] + s[k], facecolor='gray', alpha=0.3) - plt.plot(t, xa[k], 'b', linewidth=2) - plt.fill_between(t, xa[k] - s[k], xa[k] + s[k], facecolor='b', alpha=0.3) - ax1.set_box_aspect(1) - ax1.set_xticks(grid_time, minor=True) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 21), minor=True) - ax1.set_xticks(ticks_time) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 5)) - # ax1.grid(which='both', linestyle='--', linewidth=0.5) - ax1.tick_params(which='minor', bottom=False, left=False) - plt.ylabel('Magnitude') - plt.ylim((-2, 2)) + dbc.Row( + [ + + dbc.Col( + html.Div( + children=[html.P('Channel 1, freq 1'), + html.P('Channel 1, freq 2'), + html.P('Channel 2, freq 1'), + html.P('Channel 2, freq 2')] + ), + width=4 + ), + + dbc.Col( + [ + html.Div( + style={'padding-right': '0%', 'width': '120%'}, + children=[dcc.Slider( + id=name[0], + min=props[0], + max=props[1], + value=props[2][0], + step=props[3], + marks=props[4], + ), + dcc.Slider( + id=name[1], + min=props[0], + max=props[1], + value=props[2][1], + step=props[3], + marks=props[4] + ), + dcc.Slider( + id=name[2], + min=props[0], + max=props[1], + value=props[2][2], + step=props[3], + marks=props[4], + ), + dcc.Slider( + id=name[2], + min=props[0], + max=props[1], + value=props[2][2], + step=props[3], + marks=props[4], + ), ]) + + ], + width=8) + ]) + ] + ) + ] + ) + ] + + return card_content + + +switches = dbc.Row([ + html.Div( + [ + dbc.Label(""), + dbc.Switch( + id="example1", + label="Example 1", + value=False, + ), + ]), + html.Div( + [ + dbc.Label(""), + dbc.Switch( + id="example2", + label="Example 2", + value=False, + ), + ]) + ]) + + +# %% Example plotly figure +headers = ['Frequency (Hz)', 'Condition 1', 'Condition 2'] +names = [str(i) for i in range(10)] + +marks_amp = {str(x): {'label': str(round(x, 2)), 'style': {'color': 'black'}} for x in np.linspace(0, 2, 9)} +marks_f = {str(int(x)): {'label': str(round(x)), 'style': {'color': 'black'}} for x in np.linspace(0, 20, 5)} + +props_amp = [0, 1, [1, 0.2, 0], 0.05, marks_amp] +props_f = [0, 5, [1, 2, 3], 0.5, marks_f] + +instructions = html.Div( + [ + dbc.Button("Instructions", id="open-fs", size="lg"), + dbc.Modal( + [ + dbc.ModalHeader(dbc.ModalTitle("Instructions")), + dbc.ModalBody( + html.Div([ + html.A("Accompanying publication", + href='https://www.biorxiv.org/content/10.1101/2021.12.21.473676v1', target="_blank"), + html.H5(""), + html.P( + "This simulator generates non-sinusoidal waveforms from\ + three user-defined sinusoids. The user can control the \ + amplitude, frequency, and phase of each sinusoid \ + by the corresponsing slider. The output graphs show \ + the waveform, its instantaneous frequency, and its spectrum."), + html.H5(" "), + html.P("\ + The 'Harmonic conditions met' traffic light shows whether\ + the resulting waveform should (green) or should not (red) be considered a harmonic\ + structure according to 2 conditions from Fabus et al. (2022).\ + These conditions are (1) integer frequency ratios and constant phase\ + and (2) non-negative instantaneous frequency. The user is\ + encouraged to explore how clear secondary oscillatory\ + dynamics is present when these are not met."), + html.H5(" "), + html.P("\ + Additionally, in 'Advanced mode', each condition gets\ + its own traffic light and the user can see which of \ + amplitude, frequency, and phase is driving the results.\ + A 1/f^gamma fit is also added to the spectrum, so\ + the user can explore differences between strong (gamma>2)\ + and weak (gamma<=2) harmonics."), + ])), + ], + id="modal-fs", + fullscreen=False, + scrollable=True, + size="lg", + ), + ] +) + +app = dash.Dash(__name__, title='RepresentationalDynamics', external_stylesheets=[dbc.themes.BOOTSTRAP]) +app.layout = html.Div( + [ + dcc.Location(id='url', refresh=False), + html.H1('Representational Dynamics Simulator'), + + dbc.Row( + [ + dbc.Col(dbc.Card(card(headers[0], names[:2], props_f), style={"width": "20vw"}, + color="light", inverse=False), + width=3, id='amp-col'), + dbc.Col(dbc.Card(card(headers[1], names[2:5], props_amp), style={"width": "20vw"}, color="light", + inverse=False), + width=3), + dbc.Col(dbc.Card(card(headers[2], names[5:8], props_amp), style={"width": "20vw"}, color="light", + inverse=False), + width=3), + dbc.Col(children=[instructions, switches]) + ], + ), + + dbc.Row( + [ + dbc.Col( + [ + dbc.Row(dcc.Graph(id='graph-sig', style={'width': '60vw', 'height': '35vh'})), + dbc.Row(dcc.Graph(id='graph-if', style={'width': '60vw', 'height': '35vh'})), + ] + ), + dbc.Col( + [ + dbc.Row([dcc.Graph(id='graph-fft', style={'width': '35vw', 'height': '70vh'})]) + ] + ) + ] + ) + ] +) + + +@app.callback( + Output("modal-fs", "is_open"), + Input("open-fs", "n_clicks"), + State("modal-fs", "is_open"), +) +def toggle_modal(n, is_open): + if n: + return not is_open + return is_open + + +@app.callback( + [Output('graph-sig', 'figure'), + Output('graph-if', 'figure'), + Output('graph-fft', 'figure'), + Output(headers[0], 'children'), + Output(headers[1], 'children'), + Output(headers[2], 'children')], + [Input(component_id='advanced-switch', component_property='value')] + + [Input(x, 'value') for x in names], ) +def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, + s2ch2f2=0, example=1): + # define standard parameters + t = np.arange(0.001, 0.101, 0.001) + if example == 1: + # frequency + f1 = 10.0 + f2 = 0 + + # amplitude + s1ch1f1 = 1.5 # magnitude of signal 1, channel 1, frequency component 1 + s1ch1f2 = 0 # magnitude of signal 1, channel 1, frequency component 2 + s1ch2f1 = 0.5 * s1ch1f1 # magnitude of signal 1, channel 2, frequency component 1 + s1ch2f2 = 0 # magnitude of signal 1, channel 2, frequency component 2 + s2ch1f1 = 0 # magnitude of signal 2, channel 1, frequency component 1 + s2ch1f2 = 0 # magnitude of signal 2, channel 1, frequency component 2 + s2ch2f1 = 0 # magnitude of signal 2, channel 2, frequency component 1 + s2ch2f2 = 0 # magnitude of signal 2, channel 2, frequency component 2 + + ticks_amp = np.linspace(-2, 2, 5) + ticks_pow = np.linspace(0, 2, 5) + ticks_mi = np.linspace(0, 6, 4) + ticks_mipow = np.linspace(0, 3, 4) + + ylim1 = (0, 6) + ylim2 = (0, 3) + elif example == 2: + # This example has multiple frequncy components: + f1 = 10.0 + f2 = 1.5 * f1 + + s1ch1f1 = 0.5 # magnitude of signal 1, channel 1, frequency component 1 + s1ch1f2 = 0.77 # magnitude of signal 1, channel 1, frequency component 2 + s1ch2f1 = 0.5 * s1ch1f1 # magnitude of signal 1, channel 2, frequency component 1 + s1ch2f2 = 1.5 # magnitude of signal 1, channel 2, frequency component 2 + s2ch1f1 = 0.09 # magnitude of signal 2, channel 1, frequency component 1 + s2ch1f2 = 0.9 * 0.6 # magnitude of signal 2, channel 1, frequency component 1 + s2ch2f1 = s2ch1f1 # magnitude of signal 2, channel 1, frequency component 1 + s2ch2f2 = s2ch1f2 # magnitude of signal 2, channel 1, frequency component 1 + + ticks_amp = np.linspace(-2, 2, 5) + ticks_pow = np.linspace(0, 2, 5) + ticks_mi = np.linspace(0, 1.5, 4) + ticks_mipow = np.linspace(0, 0.5, 6) + + ylim1 = (0, 1.5) + ylim2 = (0, 0.5) + + theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) + theta0 = theta + s = np.array([0.5, 0.5]) # noise for channel 1 and channel 2 + Sig = np.diag(s) ** 2 + a = np.array(((s1ch1f1, s1ch1f2), (s1ch2f1, s1ch2f2))) + a0 = np.array(((s2ch1f1, s2ch1f2), (s2ch2f1, s2ch2f2))) + f = np.array([f1, f2]) + + # signals + xa = [] + x0 = [] + for k in range(2): + xa.append(np.matmul(np.expand_dims(a[k, :], 0), np.cos( + 2 * np.pi * np.expand_dims(f, 1) * np.transpose(np.expand_dims(t, 1)) + np.expand_dims(theta[k, :], 1)))[ + 0]) # first channel, first condition + x0.append(np.matmul(np.expand_dims(a0[k, :], 0), np.cos( + 2 * np.pi * np.expand_dims(f, 1) * np.transpose(np.expand_dims(t, 1)) + np.expand_dims(theta0[k, :], 1)))[ + 0]) + + # now we need to map these two channel signals back to the format given in the paper: + A_omega = [] + mu_omega = [] + phi_omega = [] + phi_mean = [] + for ifreq in np.arange(2): + A_omega.append(0.5 * np.diag(np.sqrt(a[:, ifreq] ** 2 + a0[:, ifreq] ** 2 + + 2 * a[:, ifreq] * a0[:, ifreq] * np.cos( + theta[:, ifreq] - theta0[:, ifreq] + np.array((-np.pi, -np.pi)))))) + mu_omega.append(0.5 * np.diag(np.sqrt(a[:, ifreq] ** 2 + a0[:, ifreq] ** 2 + + 2 * a[:, ifreq] * a0[:, ifreq] * np.cos( + theta[:, ifreq] - theta0[:, ifreq])))) + phi_omega.append( + np.arctan2(a[:, ifreq] * np.sin(theta[:, ifreq]) + a0[:, ifreq] * np.sin(theta0[:, ifreq] + np.pi), + a[:, ifreq] * np.cos(theta[:, ifreq]) + a0[:, ifreq] * np.cos( + theta0[:, ifreq] + np.pi))) + phi_mean.append(-np.pi / 2 + np.arctan2( + a[:, ifreq] * np.sin(theta[:, ifreq] + np.pi / 2) + a0[:, ifreq] * np.sin(theta0[:, ifreq] + np.pi / 2), + a[:, ifreq] * np.cos(theta[:, ifreq] + np.pi / 2) + a0[:, ifreq] * np.cos(theta0[:, ifreq] + np.pi / 2))) + + # find the information content terms: + c_b = 0 + if len(f[f > 0]) == 1: + Sigma = Sig else: - plt.plot(t, np.squeeze(infotermest), linewidth=2, color='k') - plt.xlim((0, np.max(t))) - ax1.set_box_aspect(1) - ax1.set_xticks(grid_time, minor=True) - ax1.set_yticks(grid_mi, minor=True) - ax1.set_xticks(ticks_time) - ax1.set_yticks(ticks_mi) - # ax1.grid(which='both', linestyle='--', linewidth=0.5) - ax1.tick_params(which='minor', bottom=False, left=False) - plt.ylabel('f^-1 (I(X,Y))') - plt.ylim(ylim1) - - plt.xlabel('Time') - ax2 = fig.add_subplot(3, 2, 2 * (k + 1)) - if k < 2: - if np.any(a0[k, :] > 0): - markerline, stemlines, baseline = plt.stem( - f[a0[k, :] > 0], a0[k, a0[k, :] > 0], linefmt='k', markerfmt='ko', basefmt='w') - markerline.set_markerfacecolor('none') - if np.any(a[k, :] > 0): + Sigma = Sig + Sig # broadband noise: sum over both frequency bands + r_b = [] + psi = [] + for ifreq in np.arange(2): + c_b = c_b + np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), A_omega[ifreq]) * np.cos( + np.expand_dims(phi_omega[ifreq], 1) - phi_omega[ifreq])) # equal to above expression + temp1 = np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), + A_omega[ifreq] * np.cos(np.expand_dims(phi_omega[ifreq], 1) + phi_omega[ifreq]))) + temp2 = np.trace(np.matmul(np.matmul(A_omega[ifreq], np.linalg.inv(Sigma)), + A_omega[ifreq] * np.sin(np.expand_dims(phi_omega[ifreq], 1) + phi_omega[ifreq]))) + r_b.append(np.sqrt(temp1 ** 2 + temp2 ** 2)) + psi.append(np.arctan2(temp2, temp1)) + + # and cross-frequency components: + posmin = [1, -1] + for i in np.arange(2): + temp1 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), + A_omega[1] * np.cos(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) + temp2 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), + A_omega[1] * np.sin(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) + r_b.append(2 * np.sqrt(temp1 ** 2 + temp2 ** 2)) + psi.append(np.arctan2(temp2, temp1)) + r_b = np.array(r_b) + + infotermest = c_b + freqs_all = np.concatenate((2 * f, np.array([np.sum(f)]), np.diff(f))) + for ifreq in np.arange(4): + infotermest = infotermest + r_b[ifreq] * np.cos(2 * np.pi * freqs_all[ifreq] * t + psi[ifreq]) + + # Figure parameters + ylim0 = (0, 2) + ticks_time = np.linspace(0, 0.1, 3) + ticks_freq = np.linspace(0, 40, 3) + + if not np.logical_or(example == 1, example == 2): + ticks_amp = np.linspace(np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), + np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1]))), + np.diff((np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), + np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))))[0] + 1) + ticks_pow = np.linspace(0, np.max((np.max(a), np.max(a0))), 2 * np.max((np.max(a), np.max(a0))) + 1) + ticks_mi = np.linspace(0, np.ceil(np.max(infotermest)), 2 * np.ceil(np.max(infotermest)) + 1) + ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), 2 * np.ceil(np.max(r_b)) + 1) + ylim1 = (0, np.ceil(np.max(infotermest))) + ylim2 = (0, np.ceil(np.max(r_b))) + + # %% Plot everything + fig = plt.figure() + for k in range(3): + ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) + if k < 2: + plt.plot(t, x0[k], 'k', linewidth=2) + plt.fill_between(t, x0[k] - s[k], x0[k] + s[k], facecolor='gray', alpha=0.3) + plt.plot(t, xa[k], 'b', linewidth=2) + plt.fill_between(t, xa[k] - s[k], xa[k] + s[k], facecolor='b', alpha=0.3) + ax1.set_box_aspect(1) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 21), minor=True) + ax1.set_xticks(ticks_time) + ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 5)) + ax1.tick_params(which='minor', bottom=False, left=False) + plt.ylabel('Magnitude') + plt.ylim((-2, 2)) + else: + plt.plot(t, np.squeeze(infotermest), linewidth=2, color='k') + plt.xlim((0, np.max(t))) + ax1.set_box_aspect(1) + ax1.set_xticks(ticks_time) + ax1.set_yticks(ticks_mi) + ax1.tick_params(which='minor', bottom=False, left=False) + plt.ylabel('f^-1 (I(X,Y))') + plt.ylim(ylim1) + + plt.xlabel('Time') + ax2 = fig.add_subplot(3, 2, 2 * (k + 1)) + if k < 2: + if np.any(a0[k, :] > 0): + markerline, stemlines, baseline = plt.stem( + f[a0[k, :] > 0], a0[k, a0[k, :] > 0], linefmt='k', markerfmt='ko', basefmt='w') + markerline.set_markerfacecolor('none') + if np.any(a[k, :] > 0): + markerline, stemlines, baseline = plt.stem( + f[a[k, :] > 0], a[k, a[k, :] > 0], linefmt='b', markerfmt='bo', basefmt='w') + markerline.set_markerfacecolor('none') + plt.ylim(ylim0) + ax2.set_box_aspect(1) + ax2.set_yticks(ticks_pow) + else: markerline, stemlines, baseline = plt.stem( - f[a[k, :] > 0], a[k, a[k, :] > 0], linefmt='b', markerfmt='bo', basefmt='w') + freqs_all[r_b > 0], r_b[r_b > 0], linefmt='k', markerfmt='ko', basefmt='w') + ax2.set_box_aspect(1) + ax2.set_yticks(ticks_mipow) + plt.ylim(ylim2) markerline.set_markerfacecolor('none') - plt.ylim((0, 2)) - ax2.set_box_aspect(1) - ax2.set_yticks(grid_pow, minor=True) - ax2.set_yticks(ticks_pow) - else: - markerline, stemlines, baseline = plt.stem( - freqs_all[r_b > 0], r_b[r_b > 0], linefmt='k', markerfmt='ko', basefmt='w') - ax2.set_box_aspect(1) - ax2.set_yticks(grid_mipow, minor=True) - ax2.set_yticks(ticks_mipow) - plt.ylim(ylim2) - markerline.set_markerfacecolor('none') - ax2.set_xticks(grid_freq, minor=True) - ax2.set_xticks(ticks_freq) - # ax2.grid(which='both', linestyle='--', linewidth=0.5) - ax2.tick_params(which='minor', bottom=False, left=False) - plt.xlabel('Frequency (Hz)') - plt.ylabel('PSD') - plt.show() + ax2.set_xticks(ticks_freq) + ax2.tick_params(which='minor', bottom=False, left=False) + plt.xlabel('Frequency (Hz)') + plt.ylabel('PSD') + plt.show() + + return fig + + +if __name__ == '__main__': + app.run_server(host='127.0.0.1', debug=True) From 994c77f0ce6cbc8a0de579a6e160fe9af29cb541 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Thu, 17 Mar 2022 15:02:22 +0000 Subject: [PATCH 04/50] adapt app to figure --- app.py | 148 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/app.py b/app.py index 52d4e0a..f6bf386 100644 --- a/app.py +++ b/app.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- """ @author: MWJ van Es, 2022 +inspired by M. Fabus (https://gitlab.com/marcoFabus/fabus2022_harmonics/-/blob/main/app.py) """ import numpy as np @@ -18,7 +19,7 @@ import dash_bootstrap_components as dbc -def card(header, name, props, traffic_light=''): +def card_amp(header, name, props): card_content = [ dbc.CardBody( [ @@ -72,7 +73,7 @@ def card(header, name, props, traffic_light=''): id=name[2], min=props[0], max=props[1], - value=props[2][2], + value=props[2][3], step=props[3], marks=props[4], ), ]) @@ -88,28 +89,72 @@ def card(header, name, props, traffic_light=''): return card_content +def card_freq(header, name, props): + card_content = [ + dbc.CardBody( + [ + html.H4(header, className="card-title", id=header), + html.Div( + style={'width': '90%'}, + children=[ -switches = dbc.Row([ - html.Div( - [ - dbc.Label(""), - dbc.Switch( - id="example1", - label="Example 1", - value=False, - ), - ]), - html.Div( - [ - dbc.Label(""), - dbc.Switch( - id="example2", - label="Example 2", - value=False, - ), - ]) - ]) + dbc.Row( + [ + dbc.Col( + html.Div( + children=[html.P('Frequency component 1'), + html.P('Frequency component 2')] + ), + width=4 + ), + + dbc.Col( + [ + html.Div( + style={'padding-right': '0%', 'width': '120%'}, + children=[dcc.Slider( + id=name[0], + min=props[0], + max=props[1], + value=props[2][0], + step=props[3], + marks=props[4], + ), + dcc.Slider( + id=name[2], + min=props[0], + max=props[1], + value=props[2][1], + step=props[3], + marks=props[4], + ), ]) + + ], + width=8) + ]) + ] + ) + ] + ) + ] + + return card_content + + +radioitems = html.Div( + [ + dbc.Label("Examples"), + dbc.RadioItems( + options=[ + {"label": "Example 1", "value":1}, + {"label": "Example 2", "value":2}, + ], + value=1, + id="example_button", + ), + ] +) # %% Example plotly figure headers = ['Frequency (Hz)', 'Condition 1', 'Condition 2'] @@ -118,8 +163,8 @@ def card(header, name, props, traffic_light=''): marks_amp = {str(x): {'label': str(round(x, 2)), 'style': {'color': 'black'}} for x in np.linspace(0, 2, 9)} marks_f = {str(int(x)): {'label': str(round(x)), 'style': {'color': 'black'}} for x in np.linspace(0, 20, 5)} -props_amp = [0, 1, [1, 0.2, 0], 0.05, marks_amp] -props_f = [0, 5, [1, 2, 3], 0.5, marks_f] +props_amp = [0, 2, [1.5, 0, 0.75, 0], 0.25, marks_amp] +props_f = [0, 5, [10, 0], 0.5, marks_f] instructions = html.Div( [ @@ -130,31 +175,15 @@ def card(header, name, props, traffic_light=''): dbc.ModalBody( html.Div([ html.A("Accompanying publication", - href='https://www.biorxiv.org/content/10.1101/2021.12.21.473676v1', target="_blank"), + href='https://doi.org/10.1101/2022.02.07.479399', target="_blank"), html.H5(""), html.P( - "This simulator generates non-sinusoidal waveforms from\ - three user-defined sinusoids. The user can control the \ - amplitude, frequency, and phase of each sinusoid \ - by the corresponsing slider. The output graphs show \ - the waveform, its instantaneous frequency, and its spectrum."), - html.H5(" "), - html.P("\ - The 'Harmonic conditions met' traffic light shows whether\ - the resulting waveform should (green) or should not (red) be considered a harmonic\ - structure according to 2 conditions from Fabus et al. (2022).\ - These conditions are (1) integer frequency ratios and constant phase\ - and (2) non-negative instantaneous frequency. The user is\ - encouraged to explore how clear secondary oscillatory\ - dynamics is present when these are not met."), + "Paragraph\ + One "), html.H5(" "), html.P("\ - Additionally, in 'Advanced mode', each condition gets\ - its own traffic light and the user can see which of \ - amplitude, frequency, and phase is driving the results.\ - A 1/f^gamma fit is also added to the spectrum, so\ - the user can explore differences between strong (gamma>2)\ - and weak (gamma<=2) harmonics."), + Paragraph \ + Two"), ])), ], id="modal-fs", @@ -173,34 +202,20 @@ def card(header, name, props, traffic_light=''): dbc.Row( [ - dbc.Col(dbc.Card(card(headers[0], names[:2], props_f), style={"width": "20vw"}, + dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), style={"width": "20vw"}, color="light", inverse=False), width=3, id='amp-col'), - dbc.Col(dbc.Card(card(headers[1], names[2:5], props_amp), style={"width": "20vw"}, color="light", + dbc.Col(dbc.Card(card_amp(headers[1], names[2:5], props_amp), style={"width": "20vw"}, color="light", inverse=False), width=3), - dbc.Col(dbc.Card(card(headers[2], names[5:8], props_amp), style={"width": "20vw"}, color="light", + dbc.Col(dbc.Card(card_amp(headers[2], names[5:8], props_amp), style={"width": "20vw"}, color="light", inverse=False), width=3), - dbc.Col(children=[instructions, switches]) + dbc.Col(children=[instructions, radioitems]) ], ), - dbc.Row( - [ - dbc.Col( - [ - dbc.Row(dcc.Graph(id='graph-sig', style={'width': '60vw', 'height': '35vh'})), - dbc.Row(dcc.Graph(id='graph-if', style={'width': '60vw', 'height': '35vh'})), - ] - ), - dbc.Col( - [ - dbc.Row([dcc.Graph(id='graph-fft', style={'width': '35vw', 'height': '70vh'})]) - ] - ) - ] - ) + dbc.Row([dcc.Graph(id='graph', style={'width': '60vw', 'height': '70vh'})]) ] ) @@ -217,13 +232,10 @@ def toggle_modal(n, is_open): @app.callback( - [Output('graph-sig', 'figure'), - Output('graph-if', 'figure'), - Output('graph-fft', 'figure'), + [Output('graph', 'figure'), Output(headers[0], 'children'), Output(headers[1], 'children'), Output(headers[2], 'children')], - [Input(component_id='advanced-switch', component_property='value')] + [Input(x, 'value') for x in names], ) def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, s2ch2f2=0, example=1): From 5c0feb4eebb9024677e48daedbe5d6977f680f40 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 10:52:46 +0000 Subject: [PATCH 05/50] cleanup --- app.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/app.py b/app.py index f6bf386..7a24492 100644 --- a/app.py +++ b/app.py @@ -5,16 +5,10 @@ inspired by M. Fabus (https://gitlab.com/marcoFabus/fabus2022_harmonics/-/blob/main/app.py) """ -import numpy as np import matplotlib.pyplot as plt import dash from dash.dependencies import Input, Output, State -import plotly.express as px -import pandas as pd -import emd import numpy as np -from scipy.fft import fft, fftfreq -from scipy import stats from dash import html, dcc import dash_bootstrap_components as dbc @@ -202,19 +196,15 @@ def card_freq(header, name, props): dbc.Row( [ - dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), style={"width": "20vw"}, - color="light", inverse=False), - width=3, id='amp-col'), + dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), style={"width": "20vw"}, color="light", + inverse=False), width=3), dbc.Col(dbc.Card(card_amp(headers[1], names[2:5], props_amp), style={"width": "20vw"}, color="light", - inverse=False), - width=3), + inverse=False), width=3), dbc.Col(dbc.Card(card_amp(headers[2], names[5:8], props_amp), style={"width": "20vw"}, color="light", - inverse=False), - width=3), + inverse=False), width=3), dbc.Col(children=[instructions, radioitems]) ], ), - dbc.Row([dcc.Graph(id='graph', style={'width': '60vw', 'height': '70vh'})]) ] ) @@ -431,4 +421,4 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, if __name__ == '__main__': - app.run_server(host='127.0.0.1', debug=True) + app.run_server(host= '127.0.0.1', debug=True) From f0cbc742e407f4dd6173b53dc6a92b9bb389ce82 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 16:54:51 +0000 Subject: [PATCH 06/50] create requirements file --- requirements.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9937775 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +dash==2.3.0 +dash_bootstrap_components==1.0.3 +matplotlib==3.3.4 +numpy==1.21.5 From 7df675b7cec51420347ce8df92b1d7161c0e2754 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 17:26:05 +0000 Subject: [PATCH 07/50] Procfile --- Procfile | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Procfile diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..e69de29 From 1dd86e01770fa2212310d0941f8f7c207438d7f7 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 17:38:51 +0000 Subject: [PATCH 08/50] write Procfile --- Procfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Procfile b/Procfile index e69de29..b956a01 100644 --- a/Procfile +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn app.py \ No newline at end of file From bd95131f1c0c5dceb618947e679d143ab0163442 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 17:43:31 +0000 Subject: [PATCH 09/50] change Procfile --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index b956a01..e6cb5ad 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app.py \ No newline at end of file +web: python app.py \ No newline at end of file From 6953fd061513a957e1cd871b68ee2c175a881ba6 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 17:47:48 +0000 Subject: [PATCH 10/50] bugfig --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 7a24492..4eae425 100644 --- a/app.py +++ b/app.py @@ -116,7 +116,7 @@ def card_freq(header, name, props): marks=props[4], ), dcc.Slider( - id=name[2], + id=name[1], min=props[0], max=props[1], value=props[2][1], From 7e81b9d8d4bb8439ae4dcb6cb3d34492c24f2f5f Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 17:58:32 +0000 Subject: [PATCH 11/50] change web to worker --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index e6cb5ad..f674dd0 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: python app.py \ No newline at end of file +worker: python app.py \ No newline at end of file From 33ccd7c1ffbfbc04073aec51e49bf97732ddb7bc Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 18:01:10 +0000 Subject: [PATCH 12/50] revert worker back to web --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index f674dd0..e6cb5ad 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -worker: python app.py \ No newline at end of file +web: python app.py \ No newline at end of file From a2c60c0901098c025494251a910bf26f810c70a8 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 18 Mar 2022 18:03:46 +0000 Subject: [PATCH 13/50] change host --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 4eae425..3085967 100644 --- a/app.py +++ b/app.py @@ -421,4 +421,4 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, if __name__ == '__main__': - app.run_server(host= '127.0.0.1', debug=True) + app.run_server(host= '0.0.0.0', debug=True) From b4eadaf4d64fb8e6f64be0f618adfd049676afe5 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 8 Apr 2022 16:00:15 +0100 Subject: [PATCH 14/50] fix component id in layout --- app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 3085967..fce4562 100644 --- a/app.py +++ b/app.py @@ -64,7 +64,7 @@ def card_amp(header, name, props): marks=props[4], ), dcc.Slider( - id=name[2], + id=name[3], min=props[0], max=props[1], value=props[2][3], @@ -198,9 +198,9 @@ def card_freq(header, name, props): [ dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), style={"width": "20vw"}, color="light", inverse=False), width=3), - dbc.Col(dbc.Card(card_amp(headers[1], names[2:5], props_amp), style={"width": "20vw"}, color="light", + dbc.Col(dbc.Card(card_amp(headers[1], names[2:6], props_amp), style={"width": "20vw"}, color="light", inverse=False), width=3), - dbc.Col(dbc.Card(card_amp(headers[2], names[5:8], props_amp), style={"width": "20vw"}, color="light", + dbc.Col(dbc.Card(card_amp(headers[2], names[6:10], props_amp), style={"width": "20vw"}, color="light", inverse=False), width=3), dbc.Col(children=[instructions, radioitems]) ], From 1f527a15a5cef5d5d9f089dcecaf7828c66bb3c1 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 8 Apr 2022 16:06:13 +0100 Subject: [PATCH 15/50] put output of plotting in a list --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index fce4562..e2c3b90 100644 --- a/app.py +++ b/app.py @@ -417,7 +417,7 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, plt.ylabel('PSD') plt.show() - return fig + return [fig] if __name__ == '__main__': From 7d9e28fe94069f2fe00cbc461909520fee0d13b4 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 8 Apr 2022 16:13:28 +0100 Subject: [PATCH 16/50] clean up app callback --- app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/app.py b/app.py index e2c3b90..79ab05e 100644 --- a/app.py +++ b/app.py @@ -223,9 +223,6 @@ def toggle_modal(n, is_open): @app.callback( [Output('graph', 'figure'), - Output(headers[0], 'children'), - Output(headers[1], 'children'), - Output(headers[2], 'children')], [Input(x, 'value') for x in names], ) def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, s2ch2f2=0, example=1): From 39a4058cf253f29d7b259dc3ede6f9c1db6085aa Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 8 Apr 2022 16:37:46 +0100 Subject: [PATCH 17/50] try to fix H10 error due to Procfile? --- Procfile | 2 +- requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Procfile b/Procfile index e6cb5ad..b956a01 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: python app.py \ No newline at end of file +web: gunicorn app.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9937775..1d6334b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ dash==2.3.0 dash_bootstrap_components==1.0.3 matplotlib==3.3.4 numpy==1.21.5 +gunicorn==20.1.0 From 97557e3f3aaeb5ac9bf35dc3ffb543074db05cbc Mon Sep 17 00:00:00 2001 From: matsvanes Date: Fri, 8 Apr 2022 16:43:24 +0100 Subject: [PATCH 18/50] fix app callback --- app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 79ab05e..91fcbb1 100644 --- a/app.py +++ b/app.py @@ -222,8 +222,8 @@ def toggle_modal(n, is_open): @app.callback( - [Output('graph', 'figure'), - [Input(x, 'value') for x in names], ) + [Output('graph', 'figure')], + [Input(x, 'value') for x in names],) def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, s2ch2f2=0, example=1): # define standard parameters From c7b87d7cf3ee005e31eeb239dc246bacef94daa5 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Tue, 10 May 2022 15:39:11 +0100 Subject: [PATCH 19/50] change subplot definition --- app.py | 67 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/app.py b/app.py index 91fcbb1..071b68f 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,10 @@ inspired by M. Fabus (https://gitlab.com/marcoFabus/fabus2022_harmonics/-/blob/main/app.py) """ +import plotly import matplotlib.pyplot as plt +import matplotlib +matplotlib.use('Agg') import dash from dash.dependencies import Input, Output, State import numpy as np @@ -83,6 +86,7 @@ def card_amp(header, name, props): return card_content + def card_freq(header, name, props): card_content = [ dbc.CardBody( @@ -141,8 +145,8 @@ def card_freq(header, name, props): dbc.Label("Examples"), dbc.RadioItems( options=[ - {"label": "Example 1", "value":1}, - {"label": "Example 2", "value":2}, + {"label": "Example 1", "value": 1}, + {"label": "Example 2", "value": 2}, ], value=1, id="example_button", @@ -223,7 +227,9 @@ def toggle_modal(n, is_open): @app.callback( [Output('graph', 'figure')], - [Input(x, 'value') for x in names],) + # Output('example_button', component_property='value')], + [Input(x, 'value') for x in names] + + [Input(component_id='example_button', component_property='value')], ) def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, s2ch2f2=0, example=1): # define standard parameters @@ -272,6 +278,7 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ylim1 = (0, 1.5) ylim2 = (0, 0.5) + example = int(0) theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) theta0 = theta s = np.array([0.5, 0.5]) # noise for channel 1 and channel 2 @@ -351,44 +358,45 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ticks_freq = np.linspace(0, 40, 3) if not np.logical_or(example == 1, example == 2): - ticks_amp = np.linspace(np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), - np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1]))), - np.diff((np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), - np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))))[0] + 1) - ticks_pow = np.linspace(0, np.max((np.max(a), np.max(a0))), 2 * np.max((np.max(a), np.max(a0))) + 1) - ticks_mi = np.linspace(0, np.ceil(np.max(infotermest)), 2 * np.ceil(np.max(infotermest)) + 1) - ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), 2 * np.ceil(np.max(r_b)) + 1) + ticks_amp = np.linspace(int(np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1])))), + int(np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))), + int(np.diff((np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), + np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))))[0] + 1)) + ticks_pow = np.linspace(0, np.max((np.max(a), np.max(a0))), int(2 * np.max((np.max(a), np.max(a0))) + 1)) + ticks_mi = np.linspace(0, np.ceil(np.max(infotermest)), int(2 * np.ceil(np.max(infotermest)) + 1)) + ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), int(2 * np.ceil(np.max(r_b)) + 1)) ylim1 = (0, np.ceil(np.max(infotermest))) ylim2 = (0, np.ceil(np.max(r_b))) # %% Plot everything - fig = plt.figure() + fig, ax = plt.subplots(3, 2) for k in range(3): - ax1 = fig.add_subplot(3, 2, 2 * (k + 1) - 1) + plt.sca(ax[k, 0]) if k < 2: plt.plot(t, x0[k], 'k', linewidth=2) plt.fill_between(t, x0[k] - s[k], x0[k] + s[k], facecolor='gray', alpha=0.3) plt.plot(t, xa[k], 'b', linewidth=2) plt.fill_between(t, xa[k] - s[k], xa[k] + s[k], facecolor='b', alpha=0.3) - ax1.set_box_aspect(1) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 21), minor=True) - ax1.set_xticks(ticks_time) - ax1.set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 5)) - ax1.tick_params(which='minor', bottom=False, left=False) + ax[k, 0].set_box_aspect(1) + ax[k, 0].set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 21), + minor=True) + ax[k, 0].set_xticks(ticks_time) + ax[k, 0].set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 5)) + ax[k, 0].tick_params(which='minor', bottom=False, left=False) plt.ylabel('Magnitude') plt.ylim((-2, 2)) else: plt.plot(t, np.squeeze(infotermest), linewidth=2, color='k') plt.xlim((0, np.max(t))) - ax1.set_box_aspect(1) - ax1.set_xticks(ticks_time) - ax1.set_yticks(ticks_mi) - ax1.tick_params(which='minor', bottom=False, left=False) + ax[k, 0].set_box_aspect(1) + ax[k, 0].set_xticks(ticks_time) + ax[k, 0].set_yticks(ticks_mi) + ax[k, 0].tick_params(which='minor', bottom=False, left=False) plt.ylabel('f^-1 (I(X,Y))') plt.ylim(ylim1) plt.xlabel('Time') - ax2 = fig.add_subplot(3, 2, 2 * (k + 1)) + plt.sca(ax[k, 1]) if k < 2: if np.any(a0[k, :] > 0): markerline, stemlines, baseline = plt.stem( @@ -399,23 +407,22 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, f[a[k, :] > 0], a[k, a[k, :] > 0], linefmt='b', markerfmt='bo', basefmt='w') markerline.set_markerfacecolor('none') plt.ylim(ylim0) - ax2.set_box_aspect(1) - ax2.set_yticks(ticks_pow) + ax[k, 1].set_box_aspect(1) + ax[k, 1].set_yticks(ticks_pow) else: markerline, stemlines, baseline = plt.stem( freqs_all[r_b > 0], r_b[r_b > 0], linefmt='k', markerfmt='ko', basefmt='w') - ax2.set_box_aspect(1) - ax2.set_yticks(ticks_mipow) + ax[k, 1].set_box_aspect(1) + ax[k, 1].set_yticks(ticks_mipow) plt.ylim(ylim2) markerline.set_markerfacecolor('none') - ax2.set_xticks(ticks_freq) - ax2.tick_params(which='minor', bottom=False, left=False) + ax[k, 1].set_xticks(ticks_freq) + ax[k, 1].tick_params(which='minor', bottom=False, left=False) plt.xlabel('Frequency (Hz)') plt.ylabel('PSD') - plt.show() return [fig] if __name__ == '__main__': - app.run_server(host= '0.0.0.0', debug=True) + app.run_server(host='0.0.0.0', debug=True) From 73fd529f408ab99ebd614074441a25bff10f2e09 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 12:43:50 +0100 Subject: [PATCH 20/50] replace matplotlib by pyplot --- app.py | 187 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 127 insertions(+), 60 deletions(-) diff --git a/app.py b/app.py index 071b68f..87ede6f 100644 --- a/app.py +++ b/app.py @@ -6,9 +6,11 @@ """ import plotly +from plotly.subplots import make_subplots +import plotly.graph_objs as go + +import plotly.express as px import matplotlib.pyplot as plt -import matplotlib -matplotlib.use('Agg') import dash from dash.dependencies import Input, Output, State import numpy as np @@ -22,7 +24,7 @@ def card_amp(header, name, props): [ html.H4(header, className="card-title", id=header), html.Div( - style={'width': '90%'}, + style={'width': '100%'}, children=[ dbc.Row( @@ -41,7 +43,7 @@ def card_amp(header, name, props): dbc.Col( [ html.Div( - style={'padding-right': '0%', 'width': '120%'}, + style={'padding-right': '0%', 'width': '100%'}, children=[dcc.Slider( id=name[0], min=props[0], @@ -74,7 +76,6 @@ def card_amp(header, name, props): step=props[3], marks=props[4], ), ]) - ], width=8) ]) @@ -93,7 +94,7 @@ def card_freq(header, name, props): [ html.H4(header, className="card-title", id=header), html.Div( - style={'width': '90%'}, + style={'width': '100%'}, children=[ dbc.Row( @@ -110,7 +111,7 @@ def card_freq(header, name, props): dbc.Col( [ html.Div( - style={'padding-right': '0%', 'width': '120%'}, + style={'padding-right': '0%', 'width': '100%'}, children=[dcc.Slider( id=name[0], min=props[0], @@ -226,8 +227,8 @@ def toggle_modal(n, is_open): @app.callback( - [Output('graph', 'figure')], - # Output('example_button', component_property='value')], + [Output('graph', 'figure'), + Output('example_button', component_property='value')], [Input(x, 'value') for x in names] + [Input(component_id='example_button', component_property='value')], ) def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, @@ -275,10 +276,9 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ticks_mi = np.linspace(0, 1.5, 4) ticks_mipow = np.linspace(0, 0.5, 6) - ylim1 = (0, 1.5) + ylim1 = (0, 1.6) ylim2 = (0, 0.5) - example = int(0) theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) theta0 = theta s = np.array([0.5, 0.5]) # noise for channel 1 and channel 2 @@ -367,62 +367,129 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), int(2 * np.ceil(np.max(r_b)) + 1)) ylim1 = (0, np.ceil(np.max(infotermest))) ylim2 = (0, np.ceil(np.max(r_b))) + example = int(0) # %% Plot everything - fig, ax = plt.subplots(3, 2) + import plotly.graph_objs as go + from plotly.subplots import make_subplots + fig = make_subplots(rows=3, cols=2, + subplot_titles=("Stimulus Evoked Signals", "Power", "", "", "Information Content", "")) for k in range(3): - plt.sca(ax[k, 0]) if k < 2: - plt.plot(t, x0[k], 'k', linewidth=2) - plt.fill_between(t, x0[k] - s[k], x0[k] + s[k], facecolor='gray', alpha=0.3) - plt.plot(t, xa[k], 'b', linewidth=2) - plt.fill_between(t, xa[k] - s[k], xa[k] + s[k], facecolor='b', alpha=0.3) - ax[k, 0].set_box_aspect(1) - ax[k, 0].set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 21), - minor=True) - ax[k, 0].set_xticks(ticks_time) - ax[k, 0].set_yticks(np.linspace(-np.ceil(np.max(xa[k] + s[k])), np.ceil(np.max(xa[k] + s[k])), 5)) - ax[k, 0].tick_params(which='minor', bottom=False, left=False) - plt.ylabel('Magnitude') - plt.ylim((-2, 2)) + fig.add_trace( + go.Scatter( + x=t, + y=x0[k], + line=dict(color='black'), + mode='lines', + showlegend=False + ), + row=k + 1, + col=1 + ) + fig.add_trace( + go.Scatter( + x=t.tolist() + t.tolist()[::-1], # x, then x reversed + y=(x0[k] + s[k]).tolist() + (x0[k] - s[k])[::-1].tolist(), # upper, then lower reversed + fill='toself', + fillcolor='gray', + opacity=0.3, + line=dict(color='rgba(255,255,255,0)'), + hoverinfo="skip", + showlegend=False + ), + row=k + 1, + col=1 + ) + fig.add_trace( + go.Scatter( + x=t, + y=xa[k], + line=dict(color='blue'), + mode='lines', + showlegend=False + ), + row=k + 1, + col=1 + ) + fig.add_trace( + go.Scatter( + x=t.tolist() + t.tolist()[::-1], # x, then x reversed + y=(xa[k] + s[k]).tolist() + (xa[k] - s[k])[::-1].tolist(), # upper, then lower reversed + fill='toself', + fillcolor='blue', + opacity=0.3, + line=dict(color='rgba(255,255,255,0)'), + hoverinfo="skip", + showlegend=False + ), + row=k + 1, + col=1 + ) + # fig.update_layout(xaxis=dict(title_text="Time (s)"), yaxis=dict(title_text="Magnitude")) + fig.update_yaxes(dict(title_text=f"CHANNEL {k + 1}
Magnitude"), range=[-2, 2], row=k + 1, col=1) else: - plt.plot(t, np.squeeze(infotermest), linewidth=2, color='k') - plt.xlim((0, np.max(t))) - ax[k, 0].set_box_aspect(1) - ax[k, 0].set_xticks(ticks_time) - ax[k, 0].set_yticks(ticks_mi) - ax[k, 0].tick_params(which='minor', bottom=False, left=False) - plt.ylabel('f^-1 (I(X,Y))') - plt.ylim(ylim1) - - plt.xlabel('Time') - plt.sca(ax[k, 1]) + fig.add_trace( + go.Scatter( + x=t, + y=infotermest, + line=dict(color='black'), + mode='lines', + showlegend=False + ), + row=k + 1, + col=1 + ) + fig.update_xaxes(dict(title_text="Time (s)"), row=k + 1, col=1) + fig.update_yaxes(dict(title_text="f-1 (I(X,Y))"), range=ylim1, row=k + 1, col=1) + + # right side of the figure if k < 2: - if np.any(a0[k, :] > 0): - markerline, stemlines, baseline = plt.stem( - f[a0[k, :] > 0], a0[k, a0[k, :] > 0], linefmt='k', markerfmt='ko', basefmt='w') - markerline.set_markerfacecolor('none') - if np.any(a[k, :] > 0): - markerline, stemlines, baseline = plt.stem( - f[a[k, :] > 0], a[k, a[k, :] > 0], linefmt='b', markerfmt='bo', basefmt='w') - markerline.set_markerfacecolor('none') - plt.ylim(ylim0) - ax[k, 1].set_box_aspect(1) - ax[k, 1].set_yticks(ticks_pow) + for i in np.where(a0[k, :] > 0)[0]: + fig.add_trace( + go.Scatter( + x=[f[i], f[i]], + y=[-1, a0[k, i]], + mode='lines+markers', + line=dict(color='black', width=3), + marker=dict(size=10, color='black', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) + for i in np.where(a[k, :] > 0)[0]: + fig.add_trace( + go.Scatter( + x=[f[i], f[i]], + y=[-1, a[k, i]], + mode='lines+markers', + line=dict(color='blue', width=3), + marker=dict(size=10, color='blue', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) + fig.update_yaxes(dict(title_text=f"PSD"), range=ylim0, row=k + 1, col=2) + fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: - markerline, stemlines, baseline = plt.stem( - freqs_all[r_b > 0], r_b[r_b > 0], linefmt='k', markerfmt='ko', basefmt='w') - ax[k, 1].set_box_aspect(1) - ax[k, 1].set_yticks(ticks_mipow) - plt.ylim(ylim2) - markerline.set_markerfacecolor('none') - ax[k, 1].set_xticks(ticks_freq) - ax[k, 1].tick_params(which='minor', bottom=False, left=False) - plt.xlabel('Frequency (Hz)') - plt.ylabel('PSD') - - return [fig] - + for i in np.where(r_b > 0)[0]: + fig.add_trace( + go.Scatter( + x=[freqs_all[i], freqs_all[i]], + y=[-1, r_b[i]], + mode='lines+markers', + line=dict(color='black', width=3), + marker=dict(size=10, color='black', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) + fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 40], row=k + 1, col=2) + fig.update_yaxes(dict(title_text="PSD"), range=ylim2, row=k + 1, col=2) + return [fig, example] if __name__ == '__main__': app.run_server(host='0.0.0.0', debug=True) From 53e42925c7164230e3ca16b64d9d2c9a9dcc07c4 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 13:34:50 +0100 Subject: [PATCH 21/50] improve layout --- app.py | 68 +++++++++++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/app.py b/app.py index 87ede6f..2ecf2c0 100644 --- a/app.py +++ b/app.py @@ -26,58 +26,47 @@ def card_amp(header, name, props): html.Div( style={'width': '100%'}, children=[ - dbc.Row( [ - - dbc.Col( - html.Div( - children=[html.P('Channel 1, freq 1'), - html.P('Channel 1, freq 2'), - html.P('Channel 2, freq 1'), - html.P('Channel 2, freq 2')] - ), - width=4 - ), - dbc.Col( [ html.Div( style={'padding-right': '0%', 'width': '100%'}, - children=[dcc.Slider( + children=[ + html.P('Channel 1, Amplitude component 1'), + dcc.Slider( id=name[0], min=props[0], max=props[1], value=props[2][0], step=props[3], - marks=props[4], ), + html.P('Channel 1, Amplitude component 2'), dcc.Slider( id=name[1], min=props[0], max=props[1], value=props[2][1], step=props[3], - marks=props[4] ), + html.P('Channel 2, Amplitude component 1'), dcc.Slider( id=name[2], min=props[0], max=props[1], value=props[2][2], step=props[3], - marks=props[4], ), + html.P('Channel 2, Amplitude component 2'), dcc.Slider( id=name[3], min=props[0], max=props[1], value=props[2][3], step=props[3], - marks=props[4], ), ]) ], - width=8) + ) ]) ] ) @@ -99,38 +88,30 @@ def card_freq(header, name, props): dbc.Row( [ - - dbc.Col( - html.Div( - children=[html.P('Frequency component 1'), - html.P('Frequency component 2')] - ), - width=4 - ), - dbc.Col( [ html.Div( style={'padding-right': '0%', 'width': '100%'}, - children=[dcc.Slider( + children=[ + html.P('Frequency component 1'), + dcc.Slider( id=name[0], min=props[0], max=props[1], value=props[2][0], step=props[3], - marks=props[4], ), + html.P('Frequency component 2'), dcc.Slider( id=name[1], min=props[0], max=props[1], value=props[2][1], step=props[3], - marks=props[4], ), ]) ], - width=8) + ) ]) ] ) @@ -151,6 +132,7 @@ def card_freq(header, name, props): ], value=1, id="example_button", + inline=True, ), ] ) @@ -201,16 +183,21 @@ def card_freq(header, name, props): dbc.Row( [ - dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), style={"width": "20vw"}, color="light", - inverse=False), width=3), - dbc.Col(dbc.Card(card_amp(headers[1], names[2:6], props_amp), style={"width": "20vw"}, color="light", - inverse=False), width=3), - dbc.Col(dbc.Card(card_amp(headers[2], names[6:10], props_amp), style={"width": "20vw"}, color="light", - inverse=False), width=3), - dbc.Col(children=[instructions, radioitems]) - ], + dbc.Col([ + dbc.Col(children=[instructions, radioitems]), + + dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), color="light", + inverse=False)), + ]), + dbc.Col(dbc.Card(card_amp(headers[1], names[2:6], props_amp), color="light", + inverse=False)), + dbc.Col(dbc.Card(card_amp(headers[2], names[6:10], props_amp), color="light", + inverse=False)) + ], style={"width": "100vw"}), + dbc.Row( + [dcc.Graph(id='graph'), + ], ), - dbc.Row([dcc.Graph(id='graph', style={'width': '60vw', 'height': '70vh'})]) ] ) @@ -367,7 +354,6 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), int(2 * np.ceil(np.max(r_b)) + 1)) ylim1 = (0, np.ceil(np.max(infotermest))) ylim2 = (0, np.ceil(np.max(r_b))) - example = int(0) # %% Plot everything import plotly.graph_objs as go From 0249444324f4efbb0b01d5868ae31ab7fc1266fb Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 14:52:37 +0100 Subject: [PATCH 22/50] fix figure interaction --- app.py | 73 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/app.py b/app.py index 2ecf2c0..2de9425 100644 --- a/app.py +++ b/app.py @@ -35,12 +35,12 @@ def card_amp(header, name, props): children=[ html.P('Channel 1, Amplitude component 1'), dcc.Slider( - id=name[0], - min=props[0], - max=props[1], - value=props[2][0], - step=props[3], - ), + id=name[0], + min=props[0], + max=props[1], + value=props[2][0], + step=props[3], + ), html.P('Channel 1, Amplitude component 2'), dcc.Slider( id=name[1], @@ -66,7 +66,7 @@ def card_amp(header, name, props): step=props[3], ), ]) ], - ) + ) ]) ] ) @@ -95,12 +95,12 @@ def card_freq(header, name, props): children=[ html.P('Frequency component 1'), dcc.Slider( - id=name[0], - min=props[0], - max=props[1], - value=props[2][0], - step=props[3], - ), + id=name[0], + min=props[0], + max=props[1], + value=props[2][0], + step=props[3], + ), html.P('Frequency component 2'), dcc.Slider( id=name[1], @@ -111,7 +111,7 @@ def card_freq(header, name, props): ), ]) ], - ) + ) ]) ] ) @@ -145,7 +145,7 @@ def card_freq(header, name, props): marks_f = {str(int(x)): {'label': str(round(x)), 'style': {'color': 'black'}} for x in np.linspace(0, 20, 5)} props_amp = [0, 2, [1.5, 0, 0.75, 0], 0.25, marks_amp] -props_f = [0, 5, [10, 0], 0.5, marks_f] +props_f = [0, 20, [10, 0], 1, marks_f] instructions = html.Div( [ @@ -187,16 +187,16 @@ def card_freq(header, name, props): dbc.Col(children=[instructions, radioitems]), dbc.Col(dbc.Card(card_freq(headers[0], names[:2], props_f), color="light", - inverse=False)), - ]), + inverse=False)), + ]), dbc.Col(dbc.Card(card_amp(headers[1], names[2:6], props_amp), color="light", inverse=False)), dbc.Col(dbc.Card(card_amp(headers[2], names[6:10], props_amp), color="light", inverse=False)) - ], style={"width": "100vw"}), + ], style={"width": "100vw"}), dbc.Row( [dcc.Graph(id='graph'), - ], + ], ), ] ) @@ -215,11 +215,27 @@ def toggle_modal(n, is_open): @app.callback( [Output('graph', 'figure'), - Output('example_button', component_property='value')], - [Input(x, 'value') for x in names] + - [Input(component_id='example_button', component_property='value')], ) -def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, - s2ch2f2=0, example=1): + Output('example_button', component_property='value'), + Output('0', component_property='value'), + Output('1', component_property='value'), + Output('2', component_property='value'), + Output('3', component_property='value'), + Output('4', component_property='value'), + Output('5', component_property='value'), + Output('6', component_property='value'), + Output('7', component_property='value'), + Output('8', component_property='value'), + Output('9', component_property='value')], + [Input(component_id='example_button', component_property='value')] + + [Input(x, 'value') for x in names], ) +#def update_figure(example, f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, +# s2ch2f2=0): +def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, + s2ch2f2): + params_example1 = np.array((10, 0, 1.5, 0, 0.75, 0, 0, 0, 0, 0)) + params_example2 = np.array((10, 15, 0.5, 0.77, 0.25, 1.5, 0.09, 0.54, 0.09, 0.54)) + params = np.array((f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2)) + # define standard parameters t = np.arange(0.001, 0.101, 0.001) if example == 1: @@ -266,6 +282,10 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ylim1 = (0, 1.6) ylim2 = (0, 0.5) + + + + theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) theta0 = theta s = np.array([0.5, 0.5]) # noise for channel 1 and channel 2 @@ -355,6 +375,7 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ylim1 = (0, np.ceil(np.max(infotermest))) ylim2 = (0, np.ceil(np.max(r_b))) + example = 0 # %% Plot everything import plotly.graph_objs as go from plotly.subplots import make_subplots @@ -475,7 +496,9 @@ def update_figure(f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, ) fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 40], row=k + 1, col=2) fig.update_yaxes(dict(title_text="PSD"), range=ylim2, row=k + 1, col=2) - return [fig, example] + + return [fig, example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2] + if __name__ == '__main__': app.run_server(host='0.0.0.0', debug=True) From 45da9b36210894b2c40a5f720f2436ae8fac26b0 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:04:43 +0100 Subject: [PATCH 23/50] don't plot 0 Hz stems --- app.py | 83 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/app.py b/app.py index 2de9425..b8962fe 100644 --- a/app.py +++ b/app.py @@ -228,7 +228,7 @@ def toggle_modal(n, is_open): Output('9', component_property='value')], [Input(component_id='example_button', component_property='value')] + [Input(x, 'value') for x in names], ) -#def update_figure(example, f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, +# def update_figure(example, f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, # s2ch2f2=0): def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2): @@ -282,10 +282,6 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ylim1 = (0, 1.6) ylim2 = (0, 0.5) - - - - theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) theta0 = theta s = np.array([0.5, 0.5]) # noise for channel 1 and channel 2 @@ -376,11 +372,13 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ylim2 = (0, np.ceil(np.max(r_b))) example = 0 + # %% Plot everything import plotly.graph_objs as go from plotly.subplots import make_subplots fig = make_subplots(rows=3, cols=2, subplot_titles=("Stimulus Evoked Signals", "Power", "", "", "Information Content", "")) + # left side of the figure for k in range(3): if k < 2: fig.add_trace( @@ -453,47 +451,50 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, # right side of the figure if k < 2: for i in np.where(a0[k, :] > 0)[0]: - fig.add_trace( - go.Scatter( - x=[f[i], f[i]], - y=[-1, a0[k, i]], - mode='lines+markers', - line=dict(color='black', width=3), - marker=dict(size=10, color='black', symbol='circle'), - showlegend=False - ), - row=k + 1, - col=2 - ) + if f[i] > 0: + fig.add_trace( + go.Scatter( + x=[f[i], f[i]], + y=[-1, a0[k, i]], + mode='lines+markers', + line=dict(color='black', width=3), + marker=dict(size=10, color='black', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) for i in np.where(a[k, :] > 0)[0]: - fig.add_trace( - go.Scatter( - x=[f[i], f[i]], - y=[-1, a[k, i]], - mode='lines+markers', - line=dict(color='blue', width=3), - marker=dict(size=10, color='blue', symbol='circle'), - showlegend=False - ), - row=k + 1, - col=2 - ) + if f[i] > 0: + fig.add_trace( + go.Scatter( + x=[f[i], f[i]], + y=[-1, a[k, i]], + mode='lines+markers', + line=dict(color='blue', width=3), + marker=dict(size=10, color='blue', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) fig.update_yaxes(dict(title_text=f"PSD"), range=ylim0, row=k + 1, col=2) fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: - fig.add_trace( - go.Scatter( - x=[freqs_all[i], freqs_all[i]], - y=[-1, r_b[i]], - mode='lines+markers', - line=dict(color='black', width=3), - marker=dict(size=10, color='black', symbol='circle'), - showlegend=False - ), - row=k + 1, - col=2 - ) + if freqs_all[i] > 0: + fig.add_trace( + go.Scatter( + x=[freqs_all[i], freqs_all[i]], + y=[-1, r_b[i]], + mode='lines+markers', + line=dict(color='black', width=3), + marker=dict(size=10, color='black', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 40], row=k + 1, col=2) fig.update_yaxes(dict(title_text="PSD"), range=ylim2, row=k + 1, col=2) From 0466f1b47f4053c9cf2a116fbca98a16cadeef20 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:17:32 +0100 Subject: [PATCH 24/50] replace matplotlib with plotly --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1d6334b..e6d90a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ dash==2.3.0 dash_bootstrap_components==1.0.3 -matplotlib==3.3.4 numpy==1.21.5 gunicorn==20.1.0 +plotly==5.6.0 From 2d0642bb2c07aad2f968d0303fcd3ca361e9c877 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:18:40 +0100 Subject: [PATCH 25/50] cleanup --- app.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/app.py b/app.py index b8962fe..479709e 100644 --- a/app.py +++ b/app.py @@ -5,12 +5,8 @@ inspired by M. Fabus (https://gitlab.com/marcoFabus/fabus2022_harmonics/-/blob/main/app.py) """ -import plotly -from plotly.subplots import make_subplots import plotly.graph_objs as go - -import plotly.express as px -import matplotlib.pyplot as plt +from plotly.subplots import make_subplots import dash from dash.dependencies import Input, Output, State import numpy as np @@ -228,14 +224,9 @@ def toggle_modal(n, is_open): Output('9', component_property='value')], [Input(component_id='example_button', component_property='value')] + [Input(x, 'value') for x in names], ) -# def update_figure(example, f1=10, f2=0, s1ch1f1=1.5, s1ch1f2=0, s1ch2f1=0.75, s1ch2f2=0, s2ch1f1=0, s2ch1f2=0, s2ch2f1=0, -# s2ch2f2=0): + def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2): - params_example1 = np.array((10, 0, 1.5, 0, 0.75, 0, 0, 0, 0, 0)) - params_example2 = np.array((10, 15, 0.5, 0.77, 0.25, 1.5, 0.09, 0.54, 0.09, 0.54)) - params = np.array((f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2)) - # define standard parameters t = np.arange(0.001, 0.101, 0.001) if example == 1: @@ -374,8 +365,6 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, example = 0 # %% Plot everything - import plotly.graph_objs as go - from plotly.subplots import make_subplots fig = make_subplots(rows=3, cols=2, subplot_titles=("Stimulus Evoked Signals", "Power", "", "", "Information Content", "")) # left side of the figure From bc296f462b869a5b1e15263ba4e80fb24c2293e1 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:24:24 +0100 Subject: [PATCH 26/50] try debug=False --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 479709e..f811fc4 100644 --- a/app.py +++ b/app.py @@ -491,4 +491,4 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, if __name__ == '__main__': - app.run_server(host='0.0.0.0', debug=True) + app.run_server(host='0.0.0.0', debug=False) From 3d1cafc4af9fd2995cae4ab1c5f39b4ce248f806 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:29:26 +0100 Subject: [PATCH 27/50] revert previous --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index f811fc4..479709e 100644 --- a/app.py +++ b/app.py @@ -491,4 +491,4 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, if __name__ == '__main__': - app.run_server(host='0.0.0.0', debug=False) + app.run_server(host='0.0.0.0', debug=True) From 296b8c5107f5d8f8d0f37c0dcf701d05e9b1fbbd Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:32:45 +0100 Subject: [PATCH 28/50] downgrade workzeug --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index e6d90a5..75dcc48 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ dash_bootstrap_components==1.0.3 numpy==1.21.5 gunicorn==20.1.0 plotly==5.6.0 +Werkzeug==2.0.0 \ No newline at end of file From f89646476b6ed82cae2d60b51a584acea9c6391d Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:39:44 +0100 Subject: [PATCH 29/50] try different procfile --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index b956a01..e1d0544 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app.py \ No newline at end of file +web: gunicorn app:app.py \ No newline at end of file From 751a9c98e0655e5b0cc1c7aece36d796b90a7bea Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:42:53 +0100 Subject: [PATCH 30/50] remove Procfile --- Procfile | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Procfile diff --git a/Procfile b/Procfile deleted file mode 100644 index e1d0544..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: gunicorn app:app.py \ No newline at end of file From ad607df2d5dba37bd97f2558a99809bd66b3abfd Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:50:18 +0100 Subject: [PATCH 31/50] add procfile --- Procfile | 1 + 1 file changed, 1 insertion(+) create mode 100644 Procfile diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..b956a01 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn app.py \ No newline at end of file From 098ee65954411de53080b6800324c181c5e5cf11 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:53:32 +0100 Subject: [PATCH 32/50] add preload to procfile --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index b956a01..11a1a21 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app.py \ No newline at end of file +web: gunicorn app.py --preload \ No newline at end of file From 623dc963620c10bdda8deaec327636e7dea070bd Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 15:59:04 +0100 Subject: [PATCH 33/50] try another procfile --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index 11a1a21..8001d1a 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app.py --preload \ No newline at end of file +web: gunicorn app:app \ No newline at end of file From 40ff0bc1e599bd8f5d7151eeb405878751b8f8a2 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 16:09:38 +0100 Subject: [PATCH 34/50] try different init --- app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 479709e..3df6da8 100644 --- a/app.py +++ b/app.py @@ -491,4 +491,6 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, if __name__ == '__main__': - app.run_server(host='0.0.0.0', debug=True) + #app.run_server(host='0.0.0.0', debug=True) + app.debug=True + app.run() From 7d220c4040a31b080943fc59c445dbcfc159e374 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 16:17:54 +0100 Subject: [PATCH 35/50] try adding server --- Procfile | 2 +- app.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Procfile b/Procfile index 8001d1a..b308fd8 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app:app \ No newline at end of file +web: gunicorn app:server \ No newline at end of file diff --git a/app.py b/app.py index 3df6da8..7f45e19 100644 --- a/app.py +++ b/app.py @@ -172,6 +172,7 @@ def card_freq(header, name, props): ) app = dash.Dash(__name__, title='RepresentationalDynamics', external_stylesheets=[dbc.themes.BOOTSTRAP]) +server=app.server app.layout = html.Div( [ dcc.Location(id='url', refresh=False), From 139a9f1ef4d01fe345cce74d1545a2709175814b Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 16:36:08 +0100 Subject: [PATCH 36/50] add instructions --- app.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 7f45e19..f3e27fd 100644 --- a/app.py +++ b/app.py @@ -155,12 +155,23 @@ def card_freq(header, name, props): href='https://doi.org/10.1101/2022.02.07.479399', target="_blank"), html.H5(""), html.P( - "Paragraph\ - One "), + "In this simulator we illustrate the relationship between\ + the frequency content of a (neural) signal and the subsequent\ + decoding accuracy metrics when we use instantaneous signal\ + decoding."), html.H5(" "), html.P("\ - Paragraph \ - Two"), + We simulate two conditions across two channels, each of which \ + are made up out of a maximum two frequency components. The \ + frequencies and the amplitudes in each condition and channel \ + can be changed using the sliders. Example 1/2 can be toggled to\ + see the examples corresponding to figure 2 in Higgins et al (2022).\ + \ + We can see that when we use instantaneous signal decoding, the\ + information content oscillates with twice the original frequency. \ + \ + \ + App created by Mats W.J. van Es, 2022"), ])), ], id="modal-fs", From a7abc6aa23acf420e77b58c2b965362573a061bb Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 16:39:49 +0100 Subject: [PATCH 37/50] edit authorship --- app.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index f3e27fd..181b153 100644 --- a/app.py +++ b/app.py @@ -166,11 +166,10 @@ def card_freq(header, name, props): frequencies and the amplitudes in each condition and channel \ can be changed using the sliders. Example 1/2 can be toggled to\ see the examples corresponding to figure 2 in Higgins et al (2022).\ - \ We can see that when we use instantaneous signal decoding, the\ - information content oscillates with twice the original frequency. \ - \ - \ + information content oscillates with twice the original frequency."), + html.H5(" "), + html.P("\ App created by Mats W.J. van Es, 2022"), ])), ], From 69d82ff535e35e8a316170e4323cca930f1b5af0 Mon Sep 17 00:00:00 2001 From: matsvanes Date: Wed, 11 May 2022 16:53:14 +0100 Subject: [PATCH 38/50] nullify amplitude when frequency is zero --- app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app.py b/app.py index 181b153..20aa1c5 100644 --- a/app.py +++ b/app.py @@ -343,6 +343,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, # and cross-frequency components: posmin = [1, -1] + A_omega = np.matmul(np.diag(f>0), A_omega) for i in np.arange(2): temp1 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), A_omega[1] * np.cos(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) From 2a564cb3c0a51eb37834146ef5cd294975ba465e Mon Sep 17 00:00:00 2001 From: matsvanes Date: Thu, 12 May 2022 11:39:13 +0100 Subject: [PATCH 39/50] start frequency slider at 1 instead of 0 Hz --- app.py | 92 +++++++++++++++++++++++++++------------------------------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/app.py b/app.py index 20aa1c5..02e46c0 100644 --- a/app.py +++ b/app.py @@ -141,7 +141,7 @@ def card_freq(header, name, props): marks_f = {str(int(x)): {'label': str(round(x)), 'style': {'color': 'black'}} for x in np.linspace(0, 20, 5)} props_amp = [0, 2, [1.5, 0, 0.75, 0], 0.25, marks_amp] -props_f = [0, 20, [10, 0], 1, marks_f] +props_f = [1, 20, [10, 0], 1, marks_f] instructions = html.Div( [ @@ -182,7 +182,7 @@ def card_freq(header, name, props): ) app = dash.Dash(__name__, title='RepresentationalDynamics', external_stylesheets=[dbc.themes.BOOTSTRAP]) -server=app.server +server = app.server app.layout = html.Div( [ dcc.Location(id='url', refresh=False), @@ -235,7 +235,6 @@ def toggle_modal(n, is_open): Output('9', component_property='value')], [Input(component_id='example_button', component_property='value')] + [Input(x, 'value') for x in names], ) - def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2): # define standard parameters @@ -243,7 +242,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, if example == 1: # frequency f1 = 10.0 - f2 = 0 + f2 = 1.5 * f1 # amplitude s1ch1f1 = 1.5 # magnitude of signal 1, channel 1, frequency component 1 @@ -343,7 +342,6 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, # and cross-frequency components: posmin = [1, -1] - A_omega = np.matmul(np.diag(f>0), A_omega) for i in np.arange(2): temp1 = np.trace(np.matmul(np.matmul(A_omega[0], np.linalg.inv(Sigma)), A_omega[1] * np.cos(np.expand_dims(phi_omega[0], 1) + posmin[i] * phi_omega[1]))) @@ -452,57 +450,53 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, # right side of the figure if k < 2: for i in np.where(a0[k, :] > 0)[0]: - if f[i] > 0: - fig.add_trace( - go.Scatter( - x=[f[i], f[i]], - y=[-1, a0[k, i]], - mode='lines+markers', - line=dict(color='black', width=3), - marker=dict(size=10, color='black', symbol='circle'), - showlegend=False - ), - row=k + 1, - col=2 - ) + fig.add_trace( + go.Scatter( + x=[f[i], f[i]], + y=[-1, a0[k, i]], + mode='lines+markers', + line=dict(color='black', width=3), + marker=dict(size=10, color='black', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) for i in np.where(a[k, :] > 0)[0]: - if f[i] > 0: - fig.add_trace( - go.Scatter( - x=[f[i], f[i]], - y=[-1, a[k, i]], - mode='lines+markers', - line=dict(color='blue', width=3), - marker=dict(size=10, color='blue', symbol='circle'), - showlegend=False - ), - row=k + 1, - col=2 - ) + fig.add_trace( + go.Scatter( + x=[f[i], f[i]], + y=[-1, a[k, i]], + mode='lines+markers', + line=dict(color='blue', width=3), + marker=dict(size=10, color='blue', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) fig.update_yaxes(dict(title_text=f"PSD"), range=ylim0, row=k + 1, col=2) fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: - if freqs_all[i] > 0: - fig.add_trace( - go.Scatter( - x=[freqs_all[i], freqs_all[i]], - y=[-1, r_b[i]], - mode='lines+markers', - line=dict(color='black', width=3), - marker=dict(size=10, color='black', symbol='circle'), - showlegend=False - ), - row=k + 1, - col=2 - ) - fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 40], row=k + 1, col=2) - fig.update_yaxes(dict(title_text="PSD"), range=ylim2, row=k + 1, col=2) + fig.add_trace( + go.Scatter( + x=[freqs_all[i], freqs_all[i]], + y=[-1, r_b[i]], + mode='lines+markers', + line=dict(color='black', width=3), + marker=dict(size=10, color='black', symbol='circle'), + showlegend=False + ), + row=k + 1, + col=2 + ) + fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 40], row=k + 1, col=2) + fig.update_yaxes(dict(title_text="PSD"), range=ylim2, row=k + 1, col=2) return [fig, example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2] - if __name__ == '__main__': - #app.run_server(host='0.0.0.0', debug=True) - app.debug=True + # app.run_server(host='0.0.0.0', debug=True) + app.debug = True app.run() From a68e890850012dee6cbf20870e870bd9f4222589 Mon Sep 17 00:00:00 2001 From: Mats Date: Tue, 24 May 2022 10:51:29 +0100 Subject: [PATCH 40/50] Create LICENSE.md --- LICENSE.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..5eec1c1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1 @@ +Distributed under a CC-BY-4.0 license. From 1a413818ad0356753dfb369bf129c78a326ff4fc Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Tue, 24 May 2022 15:42:48 +0100 Subject: [PATCH 41/50] if freq comps are the same, only plot sum info term --- app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.py b/app.py index 02e46c0..1f31f53 100644 --- a/app.py +++ b/app.py @@ -479,6 +479,8 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: + if np.logical_and(f1 == f2, i != 2): # frequency components are the same. Only plot the sum of information terms + continue fig.add_trace( go.Scatter( x=[freqs_all[i], freqs_all[i]], From 100e8c8c3df9a49bc2a31f082304fdaf06bf9c8d Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Tue, 24 May 2022 15:59:20 +0100 Subject: [PATCH 42/50] fix y-axes --- app.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 1f31f53..87f1a5c 100644 --- a/app.py +++ b/app.py @@ -259,10 +259,12 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ticks_mi = np.linspace(0, 6, 4) ticks_mipow = np.linspace(0, 3, 4) + ylim0 = (-2, 2) ylim1 = (0, 6) ylim2 = (0, 3) + ylim3 = (0, 2) elif example == 2: - # This example has multiple frequncy components: + # This example has multiple frequency components: f1 = 10.0 f2 = 1.5 * f1 @@ -280,8 +282,10 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ticks_mi = np.linspace(0, 1.5, 4) ticks_mipow = np.linspace(0, 0.5, 6) + ylim0 = (-2, 2) ylim1 = (0, 1.6) ylim2 = (0, 0.5) + ylim3 = (0, 2) theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) theta0 = theta @@ -371,6 +375,8 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), int(2 * np.ceil(np.max(r_b)) + 1)) ylim1 = (0, np.ceil(np.max(infotermest))) ylim2 = (0, np.ceil(np.max(r_b))) + ylim0 = np.max((np.max(x0[0] + s[0]), np.max(x0[1] + s[1]), np.max(xa[0] + s[0]), np.max(xa[1] + s[1]))) + ylim3 = (0, 1.2*np.max((np.max(a0), np.max(a)))) example = 0 @@ -431,7 +437,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, col=1 ) # fig.update_layout(xaxis=dict(title_text="Time (s)"), yaxis=dict(title_text="Magnitude")) - fig.update_yaxes(dict(title_text=f"CHANNEL {k + 1}
Magnitude"), range=[-2, 2], row=k + 1, col=1) + fig.update_yaxes(dict(title_text=f"CHANNEL {k + 1}
Magnitude"), range=[-ylim0, ylim0], row=k + 1, col=1) else: fig.add_trace( go.Scatter( @@ -475,7 +481,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, row=k + 1, col=2 ) - fig.update_yaxes(dict(title_text=f"PSD"), range=ylim0, row=k + 1, col=2) + fig.update_yaxes(dict(title_text=f"PSD"), range=ylim3, row=k + 1, col=2) fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: From 8a61ca0caef06e10507e59e5c4ff08265de3e8ba Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 10:26:34 +0100 Subject: [PATCH 43/50] fix ylim bug --- app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 87f1a5c..042878f 100644 --- a/app.py +++ b/app.py @@ -259,7 +259,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ticks_mi = np.linspace(0, 6, 4) ticks_mipow = np.linspace(0, 3, 4) - ylim0 = (-2, 2) + ylim0 = 2 ylim1 = (0, 6) ylim2 = (0, 3) ylim3 = (0, 2) @@ -282,7 +282,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ticks_mi = np.linspace(0, 1.5, 4) ticks_mipow = np.linspace(0, 0.5, 6) - ylim0 = (-2, 2) + ylim0 = 2 ylim1 = (0, 1.6) ylim2 = (0, 0.5) ylim3 = (0, 2) @@ -361,7 +361,6 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, infotermest = infotermest + r_b[ifreq] * np.cos(2 * np.pi * freqs_all[ifreq] * t + psi[ifreq]) # Figure parameters - ylim0 = (0, 2) ticks_time = np.linspace(0, 0.1, 3) ticks_freq = np.linspace(0, 40, 3) From a95f1ff172935f40e2c5c20648b47c743f3ec2db Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 10:42:48 +0100 Subject: [PATCH 44/50] fix infoterm ambiguity with same freq --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 042878f..4d17974 100644 --- a/app.py +++ b/app.py @@ -484,7 +484,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: - if np.logical_and(f1 == f2, i != 2): # frequency components are the same. Only plot the sum of information terms + if not np.logical_and(f1 == f2, r_b[i] == np.max(r_b[:-1])): # frequency components are the same. Only plot the sum of information terms continue fig.add_trace( go.Scatter( From b64e98c150591459bb89f70248fe6878b20c02b3 Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 10:46:06 +0100 Subject: [PATCH 45/50] fix faulty if statement --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 4d17974..1b2e294 100644 --- a/app.py +++ b/app.py @@ -484,7 +484,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, fig.update_xaxes(range=[0, 40], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: - if not np.logical_and(f1 == f2, r_b[i] == np.max(r_b[:-1])): # frequency components are the same. Only plot the sum of information terms + if np.logical_and(f1 == f2, r_b[i] != np.max(r_b[:-1])): # frequency components are the same. Only plot the sum of information terms continue fig.add_trace( go.Scatter( From 90db4dfca4b92b2b1b1fa8ee872d887e0fca9734 Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 11:00:14 +0100 Subject: [PATCH 46/50] optimise layout and clean up --- app.py | 76 ++++++++++++++++------------------------------------------ 1 file changed, 21 insertions(+), 55 deletions(-) diff --git a/app.py b/app.py index 1b2e294..0df885c 100644 --- a/app.py +++ b/app.py @@ -2,9 +2,8 @@ # -*- coding: utf-8 -*- """ @author: MWJ van Es, 2022 -inspired by M. Fabus (https://gitlab.com/marcoFabus/fabus2022_harmonics/-/blob/main/app.py) +Copyright University of Oxford """ - import plotly.graph_objs as go from plotly.subplots import make_subplots import dash @@ -69,7 +68,6 @@ def card_amp(header, name, props): ] ) ] - return card_content @@ -114,7 +112,6 @@ def card_freq(header, name, props): ] ) ] - return card_content @@ -133,15 +130,12 @@ def card_freq(header, name, props): ] ) -# %% Example plotly figure +# %% App settings headers = ['Frequency (Hz)', 'Condition 1', 'Condition 2'] names = [str(i) for i in range(10)] -marks_amp = {str(x): {'label': str(round(x, 2)), 'style': {'color': 'black'}} for x in np.linspace(0, 2, 9)} -marks_f = {str(int(x)): {'label': str(round(x)), 'style': {'color': 'black'}} for x in np.linspace(0, 20, 5)} - -props_amp = [0, 2, [1.5, 0, 0.75, 0], 0.25, marks_amp] -props_f = [1, 20, [10, 0], 1, marks_f] +props_amp = [0, 2, [1.5, 0, 0.75, 0], 0.25] +props_f = [1, 20, [10, 0], 1] instructions = html.Div( [ @@ -170,7 +164,7 @@ def card_freq(header, name, props): information content oscillates with twice the original frequency."), html.H5(" "), html.P("\ - App created by Mats W.J. van Es, 2022"), + App created by Mats W.J. van Es, 2022, Copyright University of Oxford"), ])), ], id="modal-fs", @@ -254,17 +248,8 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch2f1 = 0 # magnitude of signal 2, channel 2, frequency component 1 s2ch2f2 = 0 # magnitude of signal 2, channel 2, frequency component 2 - ticks_amp = np.linspace(-2, 2, 5) - ticks_pow = np.linspace(0, 2, 5) - ticks_mi = np.linspace(0, 6, 4) - ticks_mipow = np.linspace(0, 3, 4) - - ylim0 = 2 - ylim1 = (0, 6) - ylim2 = (0, 3) - ylim3 = (0, 2) elif example == 2: - # This example has multiple frequency components: + # frequency f1 = 10.0 f2 = 1.5 * f1 @@ -277,16 +262,6 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch2f1 = s2ch1f1 # magnitude of signal 2, channel 1, frequency component 1 s2ch2f2 = s2ch1f2 # magnitude of signal 2, channel 1, frequency component 1 - ticks_amp = np.linspace(-2, 2, 5) - ticks_pow = np.linspace(0, 2, 5) - ticks_mi = np.linspace(0, 1.5, 4) - ticks_mipow = np.linspace(0, 0.5, 6) - - ylim0 = 2 - ylim1 = (0, 1.6) - ylim2 = (0, 0.5) - ylim3 = (0, 2) - theta = np.array([[-np.pi / 2, -np.pi / 2], [-np.pi / 2, -np.pi / 2]]) theta0 = theta s = np.array([0.5, 0.5]) # noise for channel 1 and channel 2 @@ -361,23 +336,12 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, infotermest = infotermest + r_b[ifreq] * np.cos(2 * np.pi * freqs_all[ifreq] * t + psi[ifreq]) # Figure parameters - ticks_time = np.linspace(0, 0.1, 3) - ticks_freq = np.linspace(0, 40, 3) - - if not np.logical_or(example == 1, example == 2): - ticks_amp = np.linspace(int(np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1])))), - int(np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))), - int(np.diff((np.floor(np.min((np.min(xa[0]) - s[0], np.min(xa[1]) - s[1]))), - np.ceil(np.max((np.max(xa[0]) + s[0], np.max(xa[1]) + s[1])))))[0] + 1)) - ticks_pow = np.linspace(0, np.max((np.max(a), np.max(a0))), int(2 * np.max((np.max(a), np.max(a0))) + 1)) - ticks_mi = np.linspace(0, np.ceil(np.max(infotermest)), int(2 * np.ceil(np.max(infotermest)) + 1)) - ticks_mipow = np.linspace(0, np.ceil(np.max(r_b)), int(2 * np.ceil(np.max(r_b)) + 1)) - ylim1 = (0, np.ceil(np.max(infotermest))) - ylim2 = (0, np.ceil(np.max(r_b))) - ylim0 = np.max((np.max(x0[0] + s[0]), np.max(x0[1] + s[1]), np.max(xa[0] + s[0]), np.max(xa[1] + s[1]))) - ylim3 = (0, 1.2*np.max((np.max(a0), np.max(a)))) - - example = 0 + ylim0 = np.max((np.max(x0[0] + s[0]), np.max(x0[1] + s[1]), np.max(xa[0] + s[0]), np.max(xa[1] + s[1]))) + ylim1 = (0, np.ceil(np.max(infotermest))) + ylim2 = (0, 1.2 * np.max((np.max(a0), np.max(a)))) + ylim3 = (0, np.ceil(np.max(r_b))) + + example = 0 # reset example # %% Plot everything fig = make_subplots(rows=3, cols=2, @@ -436,7 +400,8 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, col=1 ) # fig.update_layout(xaxis=dict(title_text="Time (s)"), yaxis=dict(title_text="Magnitude")) - fig.update_yaxes(dict(title_text=f"CHANNEL {k + 1}
Magnitude"), range=[-ylim0, ylim0], row=k + 1, col=1) + fig.update_yaxes(dict(title_text=f"CHANNEL {k + 1}
Magnitude"), range=[-ylim0, ylim0], row=k + 1, + col=1) else: fig.add_trace( go.Scatter( @@ -480,11 +445,12 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, row=k + 1, col=2 ) - fig.update_yaxes(dict(title_text=f"PSD"), range=ylim3, row=k + 1, col=2) - fig.update_xaxes(range=[0, 40], row=k + 1, col=2) + fig.update_yaxes(dict(title_text=f"PSD"), range=ylim2, row=k + 1, col=2) + fig.update_xaxes(range=[0, 45], row=k + 1, col=2) else: for i in np.where(r_b > 0)[0]: - if np.logical_and(f1 == f2, r_b[i] != np.max(r_b[:-1])): # frequency components are the same. Only plot the sum of information terms + if np.logical_and(f1 == f2, r_b[i] != np.max( + r_b[:-1])): # frequency components are the same. Only plot the nonzero information term continue fig.add_trace( go.Scatter( @@ -498,12 +464,12 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, row=k + 1, col=2 ) - fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 40], row=k + 1, col=2) - fig.update_yaxes(dict(title_text="PSD"), range=ylim2, row=k + 1, col=2) + fig.update_xaxes(dict(title_text="Frequency (Hz)"), range=[0, 45], row=k + 1, col=2) + fig.update_yaxes(dict(title_text="PSD"), range=ylim3, row=k + 1, col=2) return [fig, example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, s2ch1f2, s2ch2f1, s2ch2f2] + if __name__ == '__main__': - # app.run_server(host='0.0.0.0', debug=True) app.debug = True app.run() From a0bbf6466375aa5f68c5f4f6fc0d49cac2e3ad64 Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 11:08:08 +0100 Subject: [PATCH 47/50] update ylim infoterm --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index 0df885c..4071dcd 100644 --- a/app.py +++ b/app.py @@ -339,7 +339,7 @@ def update_figure(example, f1, f2, s1ch1f1, s1ch1f2, s1ch2f1, s1ch2f2, s2ch1f1, ylim0 = np.max((np.max(x0[0] + s[0]), np.max(x0[1] + s[1]), np.max(xa[0] + s[0]), np.max(xa[1] + s[1]))) ylim1 = (0, np.ceil(np.max(infotermest))) ylim2 = (0, 1.2 * np.max((np.max(a0), np.max(a)))) - ylim3 = (0, np.ceil(np.max(r_b))) + ylim3 = (0, 1.2*np.max(r_b)) example = 0 # reset example From 3594998b0e6e9cd946cf12726266b19e5de5305d Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 12:34:34 +0100 Subject: [PATCH 48/50] add DOIs and authorship to Instructions --- app.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 4071dcd..8326a85 100644 --- a/app.py +++ b/app.py @@ -145,7 +145,9 @@ def card_freq(header, name, props): dbc.ModalHeader(dbc.ModalTitle("Instructions")), dbc.ModalBody( html.Div([ - html.A("Accompanying publication", + html.A("This app is hosting Van Es (2022)", + href='https://doi.org/10.5281/zenodo.6579997', target="_blank"), + html.A("The simulator is based on the work in Higgins et al (2022)", href='https://doi.org/10.1101/2022.02.07.479399', target="_blank"), html.H5(""), html.P( @@ -163,8 +165,9 @@ def card_freq(header, name, props): We can see that when we use instantaneous signal decoding, the\ information content oscillates with twice the original frequency."), html.H5(" "), - html.P("\ - App created by Mats W.J. van Es, 2022, Copyright University of Oxford"), + html.P("\ App created by Mats W.J. van Es, 2022, Copyright University of Oxford \ + Please cite as: \ + Van Es, M.W.J. (2022). Representational Dynamics Simulator. Zenodo. https://doi.org/10.5281/zenodo.6579997"), ])), ], id="modal-fs", From 0fe47b12bf509b19b39b9a4c8e747ad1e0a886cb Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Wed, 25 May 2022 13:20:57 +0100 Subject: [PATCH 49/50] improve instructions --- app.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/app.py b/app.py index 8326a85..c0f526c 100644 --- a/app.py +++ b/app.py @@ -145,29 +145,22 @@ def card_freq(header, name, props): dbc.ModalHeader(dbc.ModalTitle("Instructions")), dbc.ModalBody( html.Div([ - html.A("This app is hosting Van Es (2022)", - href='https://doi.org/10.5281/zenodo.6579997', target="_blank"), - html.A("The simulator is based on the work in Higgins et al (2022)", - href='https://doi.org/10.1101/2022.02.07.479399', target="_blank"), - html.H5(""), - html.P( - "In this simulator we illustrate the relationship between\ + dcc.Markdown("This app is hosting [Van Es (2022)](https://doi.org/10.5281/zenodo.6579997), \ + and is based on the work in [Higgins et al. (2022)](https://doi.org/10.1101/2022.02.07.479399).\ + \n Please cite as: *Van Es, M.W.J. (2022). Representational Dynamics Simulator.\ + Zenodo. doi: 10.5281/zenodo.6579997.* \ + \n\nIn this simulator we illustrate the relationship between\ the frequency content of a (neural) signal and the subsequent\ decoding accuracy metrics when we use instantaneous signal\ - decoding."), - html.H5(" "), - html.P("\ - We simulate two conditions across two channels, each of which \ + decoding.\ + \n We simulate two conditions across two channels, each of which \ are made up out of a maximum two frequency components. The \ frequencies and the amplitudes in each condition and channel \ can be changed using the sliders. Example 1/2 can be toggled to\ see the examples corresponding to figure 2 in Higgins et al (2022).\ We can see that when we use instantaneous signal decoding, the\ - information content oscillates with twice the original frequency."), - html.H5(" "), - html.P("\ App created by Mats W.J. van Es, 2022, Copyright University of Oxford \ - Please cite as: \ - Van Es, M.W.J. (2022). Representational Dynamics Simulator. Zenodo. https://doi.org/10.5281/zenodo.6579997"), + information content oscillates with twice the original frequency. \ + \n\nApp created by Mats W.J. van Es, 2022, Copyright University of Oxford."), ])), ], id="modal-fs", From f305cd5c6e07203861d77bfd964397f217181ed5 Mon Sep 17 00:00:00 2001 From: "Mats Van Es (transfer)" Date: Thu, 9 Jun 2022 16:32:45 +0100 Subject: [PATCH 50/50] change citation --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index c0f526c..fcdd2da 100644 --- a/app.py +++ b/app.py @@ -147,7 +147,7 @@ def card_freq(header, name, props): html.Div([ dcc.Markdown("This app is hosting [Van Es (2022)](https://doi.org/10.5281/zenodo.6579997), \ and is based on the work in [Higgins et al. (2022)](https://doi.org/10.1101/2022.02.07.479399).\ - \n Please cite as: *Van Es, M.W.J. (2022). Representational Dynamics Simulator.\ + \n Please cite as: *Van Es, M.W.J., Higgins, C., Quinn, A.J., Vidaurre, D., Gould Van Praag, C.D., Fabus, M.S., Woolrich, M.W. (2022). Representational Dynamics Simulator.\ Zenodo. doi: 10.5281/zenodo.6579997.* \ \n\nIn this simulator we illustrate the relationship between\ the frequency content of a (neural) signal and the subsequent\