ibl-neuropixel 1.9.2__py3-none-any.whl → 1.10.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibl-neuropixel
3
- Version: 1.9.2
3
+ Version: 1.10.0
4
4
  Summary: Collection of tools for Neuropixel 1.0 and 2.0 probes data
5
5
  Home-page: https://github.com/int-brain-lab/ibl-neuropixel
6
6
  Author: The International Brain Laboratory
@@ -36,7 +36,7 @@ Collection of tools to handle Neuropixel 1.0 and 2.0 data
36
36
 
37
37
  ## Installation
38
38
  Minimum Python version supported is 3.10
39
- `pip install ibl-neuropixel`
39
+ `uv pip install ibl-neuropixel`
40
40
 
41
41
 
42
42
  ## Destriping
@@ -0,0 +1,38 @@
1
+ neuropixel.py,sha256=Fp5J09CBtWtyyl4kuuJf3UE9KxS5YYbGMskfEck4A7M,39850
2
+ spikeglx.py,sha256=iyw-Gxqhi_ioVHqEhZmQgCSFiW1ysEkhwPxQmP70bPg,41939
3
+ ibl_neuropixel-1.10.0.dist-info/licenses/LICENSE,sha256=JJCjBeS78UPiX7TZpE-FnMjNNpCyrFb4s8VDGG2wD10,1087
4
+ ibldsp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ ibldsp/cadzow.py,sha256=pAtxDxBwoNhoxFNc2R5WLwUrmKsq4rQuaglRNgW2Lj8,7251
6
+ ibldsp/cuda_tools.py,sha256=6LpVhYOCuOXEEg8kJ3aOCE4hzA1Yq1dojsbbBQmQCF4,2387
7
+ ibldsp/destripe_gpu.py,sha256=I5jzFocpsYw36kMMd533YThbrQaZix5e1sHqsUjHvO4,2824
8
+ ibldsp/filter_gpu.py,sha256=DPrPBLRXeCh_6BcJWJnPFaxS9Q6kX4nPENZg-c2q5rc,5789
9
+ ibldsp/fourier.py,sha256=mKg9Inv7N1vi-IxAArlwNOCHsuX7Dre02664K5Uy4ws,10648
10
+ ibldsp/icsd.py,sha256=y9NWOXBB4Nfb5A1fQMKlOu0PdVDVOZ39v2pwk2zzB84,44923
11
+ ibldsp/plots.py,sha256=XmYC4yca_seZYNEmC5hE5wBiJAl_fi_KU00DbNcM6jI,4577
12
+ ibldsp/raw_metrics.py,sha256=Ie4b7unuFc-XiFc9-tpTsUkph29G-20NvM7iJ25jAPI,5198
13
+ ibldsp/smooth.py,sha256=m_mByXHG_JyFErnYsZ27gXjcqpfwCEuWa6eOb9eFuyg,8033
14
+ ibldsp/spiketrains.py,sha256=lYP1PD4l6T-4KhFu8ZXlbnUUnEQLOriGxN1szacolPY,6878
15
+ ibldsp/sync.py,sha256=wBZgHVS3y3ih7zRcHy1KavITwV1VQz1F62Dy9iiehUQ,5210
16
+ ibldsp/utils.py,sha256=1-K7J7zzJtyYntwUylDTQmzzxpAQTQque9TAuIVYmVQ,17062
17
+ ibldsp/voltage.py,sha256=aSTl8v1gApkE7CCWjxdlHAuA_LK0M-tDkTMAffYPgMA,52622
18
+ ibldsp/waveform_extraction.py,sha256=yKrldgHqpwQ_Dq6xdoSCceKkfrL9FUXnpwKJUM3R41M,26570
19
+ ibldsp/waveforms.py,sha256=XKWO0sSEhZR1mBsXCdGpVU3ga96HX3CViXIgpl3bml8,35280
20
+ neurowaveforms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ neurowaveforms/model.py,sha256=YOPWMMNNS_Op5TyK4Br1i9_Ni41jLSqHie5r1vb5VjY,6729
22
+ tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ tests/integration/csd_experiments.py,sha256=bddMl2SCzeEM_QnBrZGypUYMKxFVDc6qderyUyX-iew,3158
24
+ tests/integration/test_destripe.py,sha256=ZV7gasuFib2AbVb63WczgQvc13PbIX9t4pQgamBMgRY,6161
25
+ tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ tests/unit/test_cadzow.py,sha256=Ky16lMe0guSQznlA1w76wSqQJ52P7LeYl7catAIb3Pg,681
27
+ tests/unit/test_ephys_np2.py,sha256=dFPe53Btqxc0n2lScEUQ0gR-S0vkwnaH1YOsdA5zaG8,14198
28
+ tests/unit/test_neuropixel.py,sha256=c27B_jHhOH9vk8Rk-rAA7vGdeaw93SrS3YtmezrwTnU,3903
29
+ tests/unit/test_plots.py,sha256=PhCxrEN1Zd1jTgmiwd16_dEghcI7kwmHT3AQmAPpzkA,850
30
+ tests/unit/test_spikeglx.py,sha256=BQ8dgCe-DczCmsa_xI0R7H-b33k49SWcucXiFWcSgEw,35110
31
+ tests/unit/test_sync.py,sha256=ak282Ela5SRRT3gohjOvj8xQQvttSvNuGcYQCUH2ABE,3779
32
+ tests/unit/test_utils.py,sha256=H2PSS5Y31-ZSJC-7n_PK6kOuJ9zdHZ_qIVTT4fM3K_o,20071
33
+ tests/unit/test_voltage.py,sha256=Za9bpHmpHrTyQz4KrUcstPOtQ5_tre9jgpJzQPIcChE,11469
34
+ tests/unit/test_waveforms.py,sha256=VnFvUi1pteROwwbC5Ebp2lqSxF3a8a7eXHpD8OUeuTg,16237
35
+ ibl_neuropixel-1.10.0.dist-info/METADATA,sha256=bdbrWgWJM0h3VjqDvF2ipPD88w7dpMPRjcIkjFgaKSQ,3750
36
+ ibl_neuropixel-1.10.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
37
+ ibl_neuropixel-1.10.0.dist-info/top_level.txt,sha256=WtVcEUptnwU6BT72cgGmrWYFGM9d9qCEqe3LwR9FIw4,48
38
+ ibl_neuropixel-1.10.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
ibldsp/cadzow.py CHANGED
@@ -17,8 +17,6 @@ for N-spatial dimensions.
17
17
  }
18
18
  """
19
19
 
20
- from functools import lru_cache
21
-
22
20
  import numpy as np
23
21
  import scipy.fft
24
22
  from iblutil.numerical import ismember2d
@@ -48,7 +46,6 @@ def traj_matrix_indices(n):
48
46
  return itraj
49
47
 
50
48
 
51
- @lru_cache(maxsize=24)
52
49
  def trajectory(x, y, dtype=np.complex128):
53
50
  """
54
51
  Computes the 2 spatial dimensions block-Toeplitz indices from x and y trace coordinates.
ibldsp/fourier.py CHANGED
@@ -224,6 +224,8 @@ def fshift(w, s, axis=-1, ns=None):
224
224
  :return: w
225
225
  """
226
226
  # create a vector that contains a 1 sample shift on the axis
227
+ if np.all(s == 0):
228
+ return w
227
229
  ns = ns or w.shape[axis]
228
230
  shape = np.array(w.shape) * 0 + 1
229
231
  shape[axis] = ns
ibldsp/sync.py ADDED
@@ -0,0 +1,147 @@
1
+ from pathlib import Path
2
+ import uuid
3
+
4
+ import numpy as np
5
+ import scipy.interpolate
6
+
7
+ import one.alf.io as alfio
8
+
9
+ import spikeglx
10
+ import ibldsp.utils
11
+
12
+
13
+ def extract_spikeglx_sync(raw_ephys_apfile, output_path=None, save=False, parts=""):
14
+ """
15
+ Extracts sync.times, sync.channels and sync.polarities from binary ephys dataset
16
+
17
+ :param raw_ephys_apfile: bin file containing ephys data or spike
18
+ :param output_path: output directory
19
+ :param save: bool write to disk only if True
20
+ :param parts: string or list of strings that will be appended to the filename before extension
21
+ :return:
22
+ """
23
+ # handles input argument: support ibllib.io.spikeglx.Reader, str and pathlib.Path
24
+ if isinstance(raw_ephys_apfile, spikeglx.Reader):
25
+ sr = raw_ephys_apfile
26
+ else:
27
+ raw_ephys_apfile = Path(raw_ephys_apfile)
28
+ sr = spikeglx.Reader(raw_ephys_apfile)
29
+
30
+ SYNC_BATCH_SIZE_SECS = 100
31
+
32
+ if not (opened := sr.is_open):
33
+ sr.open()
34
+ # if no output, need a temp folder to swap for big files
35
+ if output_path is None:
36
+ output_path = sr.file_bin.parent
37
+ file_ftcp = Path(output_path).joinpath(
38
+ f"fronts_times_channel_polarity{uuid.uuid4()}.bin"
39
+ )
40
+
41
+ # loop over chunks of the raw ephys file
42
+ wg = ibldsp.utils.WindowGenerator(
43
+ sr.ns, int(SYNC_BATCH_SIZE_SECS * sr.fs), overlap=1
44
+ )
45
+ fid_ftcp = open(file_ftcp, "wb")
46
+ for sl in wg.slice:
47
+ ss = sr.read_sync(sl)
48
+ ind, fronts = ibldsp.utils.fronts(ss, axis=0)
49
+ # a = sr.read_sync_analog(sl)
50
+ sav = np.c_[(ind[0, :] + sl.start) / sr.fs, ind[1, :], fronts.astype(np.double)]
51
+ sav.tofile(fid_ftcp)
52
+ # close temp file, read from it and delete
53
+ fid_ftcp.close()
54
+ tim_chan_pol = np.fromfile(str(file_ftcp))
55
+ tim_chan_pol = tim_chan_pol.reshape((int(tim_chan_pol.size / 3), 3))
56
+ file_ftcp.unlink()
57
+ sync = {
58
+ "times": tim_chan_pol[:, 0],
59
+ "channels": tim_chan_pol[:, 1],
60
+ "polarities": tim_chan_pol[:, 2],
61
+ }
62
+ # If opened Reader was passed into function, leave open
63
+ if not opened:
64
+ sr.close()
65
+ if save:
66
+ out_files = alfio.save_object_npy(
67
+ output_path, sync, "sync", namespace="spikeglx", parts=parts
68
+ )
69
+ return sync, out_files
70
+ else:
71
+ return sync
72
+
73
+
74
+ def sync_timestamps(tsa, tsb, tbin=0.1, return_indices=False, linear=False):
75
+ """
76
+ Sync two arrays of time stamps
77
+ :param tsa: vector of timestamps
78
+ :param tsb: vector of timestamps
79
+ :param tbin: time bin length
80
+ :param return_indices (bool), if True returns 2 sets of indices for tsa and tsb with
81
+ :param linear: (bool) if True, restricts the fit to linear
82
+ identified matches
83
+ :return:
84
+ function: interpolation function such as fnc(tsa) = tsb
85
+ float: drift in ppm
86
+ numpy array: of indices ia
87
+ numpy array: of indices ib
88
+ """
89
+
90
+ def _interp_fcn(tsa, tsb, ib, linear=linear):
91
+ # now compute the bpod/fpga drift and precise time shift
92
+ ab = np.polyfit(tsa[ib >= 0], tsb[ib[ib >= 0]] - tsa[ib >= 0], 1)
93
+ drift_ppm = ab[0] * 1e6
94
+ if linear:
95
+ fcn_a2b = lambda x: x * (1 + ab[0]) + ab[1] # noqa
96
+ else:
97
+ fcn_a2b = scipy.interpolate.interp1d(
98
+ tsa[ib >= 0], tsb[ib[ib >= 0]], fill_value="extrapolate"
99
+ )
100
+ return fcn_a2b, drift_ppm
101
+
102
+ # assert sorted inputs
103
+ tmin = np.min([np.min(tsa), np.min(tsb)])
104
+ tmax = np.max([np.max(tsa), np.max(tsb)])
105
+ # brute force correlation to get an estimate of the delta_t between series
106
+ x = np.zeros(int(np.ceil(tmax - tmin) / tbin))
107
+ y = np.zeros_like(x)
108
+ x[np.int32(np.floor((tsa - tmin) / tbin))] = 1
109
+ y[np.int32(np.floor((tsb - tmin) / tbin))] = 1
110
+ delta_t = (
111
+ ibldsp.utils.parabolic_max(scipy.signal.correlate(x, y, mode="full"))[0]
112
+ - x.shape[0]
113
+ + 1
114
+ ) * tbin
115
+ # do a first assignment at a DT threshold
116
+ ib = np.zeros(tsa.shape, dtype=np.int32) - 1
117
+ threshold = tbin
118
+ for m in np.arange(tsa.shape[0]):
119
+ dt = np.abs(tsa[m] - delta_t - tsb)
120
+ inds = np.where(dt < threshold)[0]
121
+ if inds.size == 1:
122
+ ib[m] = inds[0]
123
+ elif inds.size > 1:
124
+ candidates = inds[~np.isin(inds, ib[:m])]
125
+ if candidates.size == 1:
126
+ ib[m] = candidates[0]
127
+ elif candidates.size > 1:
128
+ ib[m] = inds[np.argmin(dt[inds])]
129
+
130
+ fcn_a2b, _ = _interp_fcn(tsa, tsb, ib)
131
+ # do a second assignment - this time a full matrix of candidate matches is computed
132
+ # the most obvious matches are assigned first and then one by one
133
+ iamiss = np.where(ib < 0)[0]
134
+ ibmiss = np.setxor1d(np.arange(tsb.size), ib[ib >= 0])
135
+ dt = np.abs(fcn_a2b(tsa[iamiss]) - tsb[ibmiss][:, np.newaxis])
136
+ dt[dt > tbin] = np.nan
137
+ while ~np.all(np.isnan(dt)):
138
+ _b, _a = np.unravel_index(np.nanargmin(dt), dt.shape)
139
+ ib[iamiss[_a]] = ibmiss[_b]
140
+ dt[:, _a] = np.nan
141
+ dt[_b, :] = np.nan
142
+ fcn_a2b, drift_ppm = _interp_fcn(tsa, tsb, ib, linear=linear)
143
+
144
+ if return_indices:
145
+ return fcn_a2b, drift_ppm, np.where(ib >= 0)[0], ib[ib >= 0]
146
+ else:
147
+ return fcn_a2b, drift_ppm
ibldsp/utils.py CHANGED
@@ -2,13 +2,21 @@
2
2
  Window generator, front detections, rms
3
3
  """
4
4
 
5
+ import warnings
6
+
5
7
  import numpy as np
6
8
  import scipy
7
9
 
8
10
 
9
11
  def sync_timestamps(tsa, tsb, tbin=0.1, return_indices=False, linear=False):
10
12
  """
13
+ .. deprecated:: X.Y.Z
14
+ `sync_timestamps` has been moved to `ibldsp.sync` module.
15
+ Use `from ibldsp.sync import sync_timestamps` instead.
16
+ This function will be removed in version X.Y.Z.
17
+
11
18
  Sync two arrays of time stamps
19
+
12
20
  :param tsa: vector of timestamps
13
21
  :param tsb: vector of timestamps
14
22
  :param tbin: time bin length
@@ -18,66 +26,23 @@ def sync_timestamps(tsa, tsb, tbin=0.1, return_indices=False, linear=False):
18
26
  :return:
19
27
  function: interpolation function such as fnc(tsa) = tsb
20
28
  float: drift in ppm
21
- numpy array: of indices ia
22
- numpy array: of indices ib
29
+ numpy array: of indices ia (if return_indices=True)
30
+ numpy array: of indices ib (if return_indices=True)
23
31
  """
32
+ warnings.warn(
33
+ "sync_timestamps has been moved to ibldsp.sync module and will be removed "
34
+ "from ibldsp.utils in version X.Y.Z. "
35
+ "Please use 'from ibldsp.sync import sync_timestamps' instead.",
36
+ DeprecationWarning,
37
+ stacklevel=2,
38
+ )
24
39
 
25
- def _interp_fcn(tsa, tsb, ib, linear=linear):
26
- # now compute the bpod/fpga drift and precise time shift
27
- ab = np.polyfit(tsa[ib >= 0], tsb[ib[ib >= 0]] - tsa[ib >= 0], 1)
28
- drift_ppm = ab[0] * 1e6
29
- if linear:
30
- fcn_a2b = lambda x: x * (1 + ab[0]) + ab[1] # noqa
31
- else:
32
- fcn_a2b = scipy.interpolate.interp1d(
33
- tsa[ib >= 0], tsb[ib[ib >= 0]], fill_value="extrapolate"
34
- )
35
- return fcn_a2b, drift_ppm
36
-
37
- # assert sorted inputs
38
- tmin = np.min([np.min(tsa), np.min(tsb)])
39
- tmax = np.max([np.max(tsa), np.max(tsb)])
40
- # brute force correlation to get an estimate of the delta_t between series
41
- x = np.zeros(int(np.ceil(tmax - tmin) / tbin))
42
- y = np.zeros_like(x)
43
- x[np.int32(np.floor((tsa - tmin) / tbin))] = 1
44
- y[np.int32(np.floor((tsb - tmin) / tbin))] = 1
45
- delta_t = (
46
- parabolic_max(scipy.signal.correlate(x, y, mode="full"))[0] - x.shape[0] + 1
47
- ) * tbin
48
- # do a first assignment at a DT threshold
49
- ib = np.zeros(tsa.shape, dtype=np.int32) - 1
50
- threshold = tbin
51
- for m in np.arange(tsa.shape[0]):
52
- dt = np.abs(tsa[m] - delta_t - tsb)
53
- inds = np.where(dt < threshold)[0]
54
- if inds.size == 1:
55
- ib[m] = inds[0]
56
- elif inds.size > 1:
57
- candidates = inds[~np.isin(inds, ib[:m])]
58
- if candidates.size == 1:
59
- ib[m] = candidates[0]
60
- elif candidates.size > 1:
61
- ib[m] = inds[np.argmin(dt[inds])]
62
-
63
- fcn_a2b, _ = _interp_fcn(tsa, tsb, ib)
64
- # do a second assignment - this time a full matrix of candidate matches is computed
65
- # the most obvious matches are assigned first and then one by one
66
- iamiss = np.where(ib < 0)[0]
67
- ibmiss = np.setxor1d(np.arange(tsb.size), ib[ib >= 0])
68
- dt = np.abs(fcn_a2b(tsa[iamiss]) - tsb[ibmiss][:, np.newaxis])
69
- dt[dt > tbin] = np.nan
70
- while ~np.all(np.isnan(dt)):
71
- _b, _a = np.unravel_index(np.nanargmin(dt), dt.shape)
72
- ib[iamiss[_a]] = ibmiss[_b]
73
- dt[:, _a] = np.nan
74
- dt[_b, :] = np.nan
75
- fcn_a2b, drift_ppm = _interp_fcn(tsa, tsb, ib, linear=linear)
76
-
77
- if return_indices:
78
- return fcn_a2b, drift_ppm, np.where(ib >= 0)[0], ib[ib >= 0]
79
- else:
80
- return fcn_a2b, drift_ppm
40
+ # Import and delegate to the new location
41
+ from ibldsp.sync import sync_timestamps as _sync_timestamps
42
+
43
+ return _sync_timestamps(
44
+ tsa, tsb, tbin=tbin, return_indices=return_indices, linear=linear
45
+ )
81
46
 
82
47
 
83
48
  def parabolic_max(x):