bmtool 0.6.8.7__py3-none-any.whl → 0.6.8.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bmtool/SLURM.py +4 -4
- bmtool/analysis/lfp.py +193 -2
- {bmtool-0.6.8.7.dist-info → bmtool-0.6.8.9.dist-info}/METADATA +1 -1
- {bmtool-0.6.8.7.dist-info → bmtool-0.6.8.9.dist-info}/RECORD +8 -8
- {bmtool-0.6.8.7.dist-info → bmtool-0.6.8.9.dist-info}/WHEEL +0 -0
- {bmtool-0.6.8.7.dist-info → bmtool-0.6.8.9.dist-info}/entry_points.txt +0 -0
- {bmtool-0.6.8.7.dist-info → bmtool-0.6.8.9.dist-info}/licenses/LICENSE +0 -0
- {bmtool-0.6.8.7.dist-info → bmtool-0.6.8.9.dist-info}/top_level.txt +0 -0
bmtool/SLURM.py
CHANGED
@@ -379,7 +379,7 @@ class BlockRunner:
|
|
379
379
|
source_dir = block.component_path
|
380
380
|
destination_dir = f"{source_dir}{i+1}"
|
381
381
|
block.component_path = destination_dir
|
382
|
-
shutil.copytree(source_dir, destination_dir) # create new components folder
|
382
|
+
shutil.copytree(source_dir, destination_dir,dirs_exist_ok = True) # create new components folder
|
383
383
|
print(f"skipping json editing for block {block.block_name}",flush=True)
|
384
384
|
else:
|
385
385
|
if len(self.blocks) != len(self.param_values):
|
@@ -390,7 +390,7 @@ class BlockRunner:
|
|
390
390
|
destination_dir = f"{source_dir}{i+1}"
|
391
391
|
block.component_path = destination_dir
|
392
392
|
|
393
|
-
shutil.copytree(source_dir, destination_dir) # create new components folder
|
393
|
+
shutil.copytree(source_dir, destination_dir,dirs_exist_ok = True) # create new components folder
|
394
394
|
json_file_path = os.path.join(destination_dir,self.json_file_path)
|
395
395
|
|
396
396
|
if self.syn_dict == None:
|
@@ -439,7 +439,7 @@ class BlockRunner:
|
|
439
439
|
source_dir = block.component_path
|
440
440
|
destination_dir = f"{source_dir}{i+1}"
|
441
441
|
block.component_path = destination_dir
|
442
|
-
shutil.copytree(source_dir, destination_dir) # create new components folder
|
442
|
+
shutil.copytree(source_dir, destination_dir,dirs_exist_ok = True) # create new components folder
|
443
443
|
print(f"skipping json editing for block {block.block_name}",flush=True)
|
444
444
|
else:
|
445
445
|
if block.component_path == None:
|
@@ -450,7 +450,7 @@ class BlockRunner:
|
|
450
450
|
destination_dir = f"{source_dir}{i+1}"
|
451
451
|
block.component_path = destination_dir
|
452
452
|
|
453
|
-
shutil.copytree(source_dir, destination_dir) # create new components folder
|
453
|
+
shutil.copytree(source_dir, destination_dir,dirs_exist_ok = True) # create new components folder
|
454
454
|
json_file_path = os.path.join(destination_dir,self.json_file_path)
|
455
455
|
|
456
456
|
if self.syn_dict == None:
|
bmtool/analysis/lfp.py
CHANGED
@@ -6,13 +6,14 @@ import h5py
|
|
6
6
|
import numpy as np
|
7
7
|
import xarray as xr
|
8
8
|
from fooof import FOOOF
|
9
|
-
from fooof.sim.gen import gen_model
|
9
|
+
from fooof.sim.gen import gen_model, gen_aperiodic
|
10
10
|
import matplotlib.pyplot as plt
|
11
11
|
from scipy import signal
|
12
12
|
import pywt
|
13
13
|
from bmtool.bmplot import is_notebook
|
14
14
|
import numba
|
15
15
|
from numba import cuda
|
16
|
+
import pandas as pd
|
16
17
|
|
17
18
|
|
18
19
|
def load_ecp_to_xarray(ecp_file: str, demean: bool = False) -> xr.DataArray:
|
@@ -647,6 +648,196 @@ def calculate_ppc2(spike_times: np.ndarray = None, lfp_signal: np.ndarray = None
|
|
647
648
|
|
648
649
|
return ppc2
|
649
650
|
|
650
|
-
|
651
651
|
|
652
|
+
# windowing functions
|
653
|
+
def windowed_xarray(da, windows, dim='time',
|
654
|
+
new_coord_name='cycle', new_coord=None):
|
655
|
+
"""Divide xarray into windows of equal size along an axis
|
656
|
+
da: input DataArray
|
657
|
+
windows: 2d-array of windows
|
658
|
+
dim: dimension along which to divide
|
659
|
+
new_coord_name: name of new dimemsion along which to concatenate windows
|
660
|
+
new_coord: pandas Index object of new coordinates. Defaults to integer index
|
661
|
+
"""
|
662
|
+
win_da = [da.sel({dim: slice(*w)}) for w in windows]
|
663
|
+
n_win = min(x.coords[dim].size for x in win_da)
|
664
|
+
idx = {dim: slice(n_win)}
|
665
|
+
coords = da.coords[dim].isel(idx).coords
|
666
|
+
win_da = [x.isel(idx).assign_coords(coords) for x in win_da]
|
667
|
+
if new_coord is None:
|
668
|
+
new_coord = pd.Index(range(len(win_da)), name=new_coord_name)
|
669
|
+
win_da = xr.concat(win_da, dim=new_coord)
|
670
|
+
return win_da
|
671
|
+
|
672
|
+
|
673
|
+
def group_windows(win_da, win_grp_idx={}, win_dim='cycle'):
|
674
|
+
"""Group windows into a dictionary of DataArrays
|
675
|
+
win_da: input windowed DataArrays
|
676
|
+
win_grp_idx: dictionary of {window group id: window indices}
|
677
|
+
win_dim: dimension for different windows
|
678
|
+
Return: dictionaries of {window group id: DataArray of grouped windows}
|
679
|
+
win_on / win_off for windows selected / not selected by `win_grp_idx`
|
680
|
+
"""
|
681
|
+
win_on, win_off = {}, {}
|
682
|
+
for g, w in win_grp_idx.items():
|
683
|
+
win_on[g] = win_da.sel({win_dim: w})
|
684
|
+
win_off[g] = win_da.drop_sel({win_dim: w})
|
685
|
+
return win_on, win_off
|
686
|
+
|
687
|
+
|
688
|
+
def average_group_windows(win_da, win_dim='cycle', grp_dim='unique_cycle'):
|
689
|
+
"""Average over windows in each group and stack groups in a DataArray
|
690
|
+
win_da: input dictionary of {window group id: DataArray of grouped windows}
|
691
|
+
win_dim: dimension for different windows
|
692
|
+
grp_dim: dimension along which to stack average of window groups
|
693
|
+
"""
|
694
|
+
win_avg = {g: xr.concat([x.mean(dim=win_dim), x.std(dim=win_dim)],
|
695
|
+
pd.Index(('mean_', 'std_'), name='stats'))
|
696
|
+
for g, x in win_da.items()}
|
697
|
+
win_avg = xr.concat(win_avg.values(), dim=pd.Index(win_avg.keys(), name=grp_dim))
|
698
|
+
win_avg = win_avg.to_dataset(dim='stats')
|
699
|
+
return win_avg
|
700
|
+
|
701
|
+
# used for avg spectrogram across different trials
|
702
|
+
def get_windowed_data(x, windows, win_grp_idx, dim='time',
|
703
|
+
win_dim='cycle', win_coord=None, grp_dim='unique_cycle'):
|
704
|
+
"""Apply functions of windowing to data
|
705
|
+
x: DataArray
|
706
|
+
windows: `windows` for `windowed_xarray`
|
707
|
+
win_grp_idx: `win_grp_idx` for `group_windows`
|
708
|
+
dim: dimension along which to divide
|
709
|
+
win_dim: dimension for different windows
|
710
|
+
win_coord: pandas Index object of `win_dim` coordinates
|
711
|
+
grp_dim: dimension along which to stack average of window groups.
|
712
|
+
If None or empty or False, do not calculate average.
|
713
|
+
Return: data returned by three functions,
|
714
|
+
`windowed_xarray`, `group_windows`, `average_group_windows`
|
715
|
+
"""
|
716
|
+
x_win = windowed_xarray(x, windows, dim=dim,
|
717
|
+
new_coord_name=win_dim, new_coord=win_coord)
|
718
|
+
x_win_onff = group_windows(x_win, win_grp_idx, win_dim=win_dim)
|
719
|
+
if grp_dim:
|
720
|
+
x_win_avg = [average_group_windows(x, win_dim=win_dim, grp_dim=grp_dim)
|
721
|
+
for x in x_win_onff]
|
722
|
+
else:
|
723
|
+
x_win_avg = None
|
724
|
+
return x_win, x_win_onff, x_win_avg
|
725
|
+
|
726
|
+
# cone of influence in frequency for cmorxx-1.0 wavelet. need to add logic to calculate in function
|
727
|
+
f0 = 2 * np.pi
|
728
|
+
CMOR_COI = 2 ** -0.5
|
729
|
+
CMOR_FLAMBDA = 4 * np.pi / (f0 + (2 + f0 ** 2) ** 0.5)
|
730
|
+
COI_FREQ = 1 / (CMOR_COI * CMOR_FLAMBDA)
|
731
|
+
|
732
|
+
def cwt_spectrogram(x, fs, nNotes=6, nOctaves=np.inf, freq_range=(0, np.inf),
|
733
|
+
bandwidth=1.0, axis=-1, detrend=False, normalize=False):
|
734
|
+
"""Calculate spectrogram using continuous wavelet transform"""
|
735
|
+
x = np.asarray(x)
|
736
|
+
N = x.shape[axis]
|
737
|
+
times = np.arange(N) / fs
|
738
|
+
# detrend and normalize
|
739
|
+
if detrend:
|
740
|
+
x = signal.detrend(x, axis=axis, type='linear')
|
741
|
+
if normalize:
|
742
|
+
x = x / x.std()
|
743
|
+
# Define some parameters of our wavelet analysis.
|
744
|
+
# range of scales (in time) that makes sense
|
745
|
+
# min = 2 (Nyquist frequency)
|
746
|
+
# max = np.floor(N/2)
|
747
|
+
nOctaves = min(nOctaves, np.log2(2 * np.floor(N / 2)))
|
748
|
+
scales = 2 ** np.arange(1, nOctaves, 1 / nNotes)
|
749
|
+
# cwt and the frequencies used.
|
750
|
+
# Use the complex morelet with bw=2*bandwidth^2 and center frequency of 1.0
|
751
|
+
# bandwidth is sigma of the gaussian envelope
|
752
|
+
wavelet = 'cmor' + str(2 * bandwidth ** 2) + '-1.0'
|
753
|
+
frequencies = pywt.scale2frequency(wavelet, scales) * fs
|
754
|
+
scales = scales[(frequencies >= freq_range[0]) & (frequencies <= freq_range[1])]
|
755
|
+
coef, frequencies = pywt.cwt(x, scales[::-1], wavelet=wavelet, sampling_period=1 / fs, axis=axis)
|
756
|
+
power = np.real(coef * np.conj(coef)) # equivalent to power = np.abs(coef)**2
|
757
|
+
# cone of influence in terms of wavelength
|
758
|
+
coi = N / 2 - np.abs(np.arange(N) - (N - 1) / 2)
|
759
|
+
# cone of influence in terms of frequency
|
760
|
+
coif = COI_FREQ * fs / coi
|
761
|
+
return power, times, frequencies, coif
|
762
|
+
|
763
|
+
|
764
|
+
def cwt_spectrogram_xarray(x, fs, time=None, axis=-1, downsample_fs=None,
|
765
|
+
channel_coords=None, **cwt_kwargs):
|
766
|
+
"""Calculate spectrogram using continuous wavelet transform and return an xarray.Dataset
|
767
|
+
x: input array
|
768
|
+
fs: sampling frequency (Hz)
|
769
|
+
axis: dimension index of time axis in x
|
770
|
+
downsample_fs: downsample to the frequency if specified
|
771
|
+
time_unit: unit of time in seconds
|
772
|
+
channel_coords: dictionary of {coordinate name: index} for channels
|
773
|
+
cwt_kwargs: keyword arguments for cwt_spectrogram()
|
774
|
+
"""
|
775
|
+
x = np.asarray(x)
|
776
|
+
T = x.shape[axis] # number of time points
|
777
|
+
t = np.arange(T) / fs if time is None else np.asarray(time)
|
778
|
+
if downsample_fs is None or downsample_fs >= fs:
|
779
|
+
downsample_fs = fs
|
780
|
+
downsampled = x
|
781
|
+
else:
|
782
|
+
num = int(T * downsample_fs / fs)
|
783
|
+
downsample_fs = num / T * fs
|
784
|
+
downsampled, t = signal.resample(x, num=num, t=t, axis=axis)
|
785
|
+
downsampled = np.moveaxis(downsampled, axis, -1)
|
786
|
+
sxx, _, f, coif = cwt_spectrogram(downsampled, downsample_fs, **cwt_kwargs)
|
787
|
+
sxx = np.moveaxis(sxx, 0, -2) # shape (... , freq, time)
|
788
|
+
if channel_coords is None:
|
789
|
+
channel_coords = {f'dim_{i:d}': range(d) for i, d in enumerate(sxx.shape[:-2])}
|
790
|
+
sxx = xr.DataArray(sxx, coords={**channel_coords, 'frequency': f, 'time': t}).to_dataset(name='PSD')
|
791
|
+
sxx.update(dict(cone_of_influence_frequency=xr.DataArray(coif, coords={'time': t})))
|
792
|
+
return sxx
|
793
|
+
|
794
|
+
|
795
|
+
# will probs move to bmplot later
|
796
|
+
def plot_spectrogram(sxx_xarray, remove_aperiodic=None, log_power=False,
|
797
|
+
plt_range=None, clr_freq_range=None, pad=0.03, ax=None):
|
798
|
+
"""Plot spectrogram. Determine color limits using value in frequency band clr_freq_range"""
|
799
|
+
sxx = sxx_xarray.PSD.values.copy()
|
800
|
+
t = sxx_xarray.time.values.copy()
|
801
|
+
f = sxx_xarray.frequency.values.copy()
|
802
|
+
|
803
|
+
cbar_label = 'PSD' if remove_aperiodic is None else 'PSD Residual'
|
804
|
+
if log_power:
|
805
|
+
with np.errstate(divide='ignore'):
|
806
|
+
sxx = np.log10(sxx)
|
807
|
+
cbar_label += ' dB' if log_power == 'dB' else ' log(power)'
|
808
|
+
|
809
|
+
if remove_aperiodic is not None:
|
810
|
+
f1_idx = 0 if f[0] else 1
|
811
|
+
ap_fit = gen_aperiodic(f[f1_idx:], remove_aperiodic.aperiodic_params)
|
812
|
+
sxx[f1_idx:, :] -= (ap_fit if log_power else 10 ** ap_fit)[:, None]
|
813
|
+
sxx[:f1_idx, :] = 0.
|
814
|
+
|
815
|
+
if log_power == 'dB':
|
816
|
+
sxx *= 10
|
817
|
+
|
818
|
+
if ax is None:
|
819
|
+
_, ax = plt.subplots(1, 1)
|
820
|
+
plt_range = np.array(f[-1]) if plt_range is None else np.array(plt_range)
|
821
|
+
if plt_range.size == 1:
|
822
|
+
plt_range = [f[0 if f[0] else 1] if log_power else 0., plt_range.item()]
|
823
|
+
f_idx = (f >= plt_range[0]) & (f <= plt_range[1])
|
824
|
+
if clr_freq_range is None:
|
825
|
+
vmin, vmax = None, None
|
826
|
+
else:
|
827
|
+
c_idx = (f >= clr_freq_range[0]) & (f <= clr_freq_range[1])
|
828
|
+
vmin, vmax = sxx[c_idx, :].min(), sxx[c_idx, :].max()
|
829
|
+
|
830
|
+
f = f[f_idx]
|
831
|
+
pcm = ax.pcolormesh(t, f, sxx[f_idx, :], shading='gouraud', vmin=vmin, vmax=vmax)
|
832
|
+
if 'cone_of_influence_frequency' in sxx_xarray:
|
833
|
+
coif = sxx_xarray.cone_of_influence_frequency
|
834
|
+
ax.plot(t, coif)
|
835
|
+
ax.fill_between(t, coif, step='mid', alpha=0.2)
|
836
|
+
ax.set_xlim(t[0], t[-1])
|
837
|
+
#ax.set_xlim(t[0],0.2)
|
838
|
+
ax.set_ylim(f[0], f[-1])
|
839
|
+
plt.colorbar(mappable=pcm, ax=ax, label=cbar_label, pad=pad)
|
840
|
+
ax.set_xlabel('Time (sec)')
|
841
|
+
ax.set_ylabel('Frequency (Hz)')
|
842
|
+
return sxx
|
652
843
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
bmtool/SLURM.py,sha256=
|
1
|
+
bmtool/SLURM.py,sha256=Rp63uxy3Z2yJtMPIb3D8mEG618nxf98SIstlW3J5BdE,19988
|
2
2
|
bmtool/__init__.py,sha256=ZStTNkAJHJxG7Pwiy5UgCzC4KlhMS5pUNPtUJZVwL_Y,136
|
3
3
|
bmtool/__main__.py,sha256=TmFkmDxjZ6250nYD4cgGhn-tbJeEm0u-EMz2ajAN9vE,650
|
4
4
|
bmtool/bmplot.py,sha256=ymyrNBctZoglsNA28733mXzM_l4b_3JIchXkQa2V2XE,54099
|
@@ -9,7 +9,7 @@ bmtool/plot_commands.py,sha256=Tqujyf0c0u8olhiHOMwgUSJXIIE1hgjv6otb25G9cA0,12298
|
|
9
9
|
bmtool/singlecell.py,sha256=XZAT_2n44EhwqVLnk3qur9aO7oJ-10axJZfwPBslM88,27219
|
10
10
|
bmtool/synapses.py,sha256=gIkfLhKDG2dHHCVJJoKuQrFn_Qut843bfk_-s97wu6c,54553
|
11
11
|
bmtool/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
bmtool/analysis/lfp.py,sha256=
|
12
|
+
bmtool/analysis/lfp.py,sha256=TfPSLIh2LqbWTRFnYwBMBpfbj6yxyhC7EAQM1e2H4qs,33609
|
13
13
|
bmtool/analysis/spikes.py,sha256=qqJ4zD8xfvSwltlWm_Bhicdngzl6uBqH6Kn5wOMKRc8,11507
|
14
14
|
bmtool/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
bmtool/debug/commands.py,sha256=AwtcR7BUUheM0NxvU1Nu234zCdpobhJv5noX8x5K2vY,583
|
@@ -19,9 +19,9 @@ bmtool/util/commands.py,sha256=zJF-fiLk0b8LyzHDfvewUyS7iumOxVnj33IkJDzux4M,64396
|
|
19
19
|
bmtool/util/util.py,sha256=00vOAwTVIifCqouBoFoT0lBashl4fCalrk8fhg_Uq4c,56654
|
20
20
|
bmtool/util/neuron/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
21
|
bmtool/util/neuron/celltuner.py,sha256=xSRpRN6DhPFz4q5buq_W8UmsD7BbUrkzYBEbKVloYss,87194
|
22
|
-
bmtool-0.6.8.
|
23
|
-
bmtool-0.6.8.
|
24
|
-
bmtool-0.6.8.
|
25
|
-
bmtool-0.6.8.
|
26
|
-
bmtool-0.6.8.
|
27
|
-
bmtool-0.6.8.
|
22
|
+
bmtool-0.6.8.9.dist-info/licenses/LICENSE,sha256=qrXg2jj6kz5d0EnN11hllcQt2fcWVNumx0xNbV05nyM,1068
|
23
|
+
bmtool-0.6.8.9.dist-info/METADATA,sha256=9u2yHG4S7kpb9LAwHANQArIVyxv9D_el9vxSTPSlQo0,20478
|
24
|
+
bmtool-0.6.8.9.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
25
|
+
bmtool-0.6.8.9.dist-info/entry_points.txt,sha256=0-BHZ6nUnh0twWw9SXNTiRmKjDnb1VO2DfG_-oprhAc,45
|
26
|
+
bmtool-0.6.8.9.dist-info/top_level.txt,sha256=gpd2Sj-L9tWbuJEd5E8C8S8XkNm5yUE76klUYcM-eWM,7
|
27
|
+
bmtool-0.6.8.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|