ibl-neuropixel 1.9.3__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.3
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
@@ -1,19 +1,20 @@
1
- neuropixel.py,sha256=P7sIBAtGIqKReK7OqMBqdwPaTeHjhHMyfyBRL_AvuQY,37987
2
- spikeglx.py,sha256=4TPXnFGhJahClxr4fA9HwTeiiHBQS9ZEfkWl6t20q2s,41068
3
- ibl_neuropixel-1.9.3.dist-info/licenses/LICENSE,sha256=JJCjBeS78UPiX7TZpE-FnMjNNpCyrFb4s8VDGG2wD10,1087
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
4
  ibldsp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  ibldsp/cadzow.py,sha256=pAtxDxBwoNhoxFNc2R5WLwUrmKsq4rQuaglRNgW2Lj8,7251
6
6
  ibldsp/cuda_tools.py,sha256=6LpVhYOCuOXEEg8kJ3aOCE4hzA1Yq1dojsbbBQmQCF4,2387
7
7
  ibldsp/destripe_gpu.py,sha256=I5jzFocpsYw36kMMd533YThbrQaZix5e1sHqsUjHvO4,2824
8
8
  ibldsp/filter_gpu.py,sha256=DPrPBLRXeCh_6BcJWJnPFaxS9Q6kX4nPENZg-c2q5rc,5789
9
- ibldsp/fourier.py,sha256=RI58nhs4ZZXx1M6EtuhA0vbtkNaBRS2QNv7tPkVomao,10608
9
+ ibldsp/fourier.py,sha256=mKg9Inv7N1vi-IxAArlwNOCHsuX7Dre02664K5Uy4ws,10648
10
10
  ibldsp/icsd.py,sha256=y9NWOXBB4Nfb5A1fQMKlOu0PdVDVOZ39v2pwk2zzB84,44923
11
11
  ibldsp/plots.py,sha256=XmYC4yca_seZYNEmC5hE5wBiJAl_fi_KU00DbNcM6jI,4577
12
12
  ibldsp/raw_metrics.py,sha256=Ie4b7unuFc-XiFc9-tpTsUkph29G-20NvM7iJ25jAPI,5198
13
13
  ibldsp/smooth.py,sha256=m_mByXHG_JyFErnYsZ27gXjcqpfwCEuWa6eOb9eFuyg,8033
14
14
  ibldsp/spiketrains.py,sha256=lYP1PD4l6T-4KhFu8ZXlbnUUnEQLOriGxN1szacolPY,6878
15
- ibldsp/utils.py,sha256=Ku1pdymbiCyQU5iZX8Akfq47YXM4xegW6G3_aomh6WA,18580
16
- ibldsp/voltage.py,sha256=Pb1ZYlr8av2XDUspZaSUtF2wOoTy92fnrsxnW9sTIFA,47708
15
+ ibldsp/sync.py,sha256=wBZgHVS3y3ih7zRcHy1KavITwV1VQz1F62Dy9iiehUQ,5210
16
+ ibldsp/utils.py,sha256=1-K7J7zzJtyYntwUylDTQmzzxpAQTQque9TAuIVYmVQ,17062
17
+ ibldsp/voltage.py,sha256=aSTl8v1gApkE7CCWjxdlHAuA_LK0M-tDkTMAffYPgMA,52622
17
18
  ibldsp/waveform_extraction.py,sha256=yKrldgHqpwQ_Dq6xdoSCceKkfrL9FUXnpwKJUM3R41M,26570
18
19
  ibldsp/waveforms.py,sha256=XKWO0sSEhZR1mBsXCdGpVU3ga96HX3CViXIgpl3bml8,35280
19
20
  neurowaveforms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -23,14 +24,15 @@ tests/integration/csd_experiments.py,sha256=bddMl2SCzeEM_QnBrZGypUYMKxFVDc6qdery
23
24
  tests/integration/test_destripe.py,sha256=ZV7gasuFib2AbVb63WczgQvc13PbIX9t4pQgamBMgRY,6161
24
25
  tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
26
  tests/unit/test_cadzow.py,sha256=Ky16lMe0guSQznlA1w76wSqQJ52P7LeYl7catAIb3Pg,681
26
- tests/unit/test_ephys_np2.py,sha256=1wsgS_C5W8tUO_qDyORBRUKGsrB0Gq3wMLAjJcjrNZ4,15599
27
- tests/unit/test_neuropixel.py,sha256=ZFKrvTYaYgK5WgOfoHa5x9BNUpRomACPiIm6Kr-A3gw,2511
27
+ tests/unit/test_ephys_np2.py,sha256=dFPe53Btqxc0n2lScEUQ0gR-S0vkwnaH1YOsdA5zaG8,14198
28
+ tests/unit/test_neuropixel.py,sha256=c27B_jHhOH9vk8Rk-rAA7vGdeaw93SrS3YtmezrwTnU,3903
28
29
  tests/unit/test_plots.py,sha256=PhCxrEN1Zd1jTgmiwd16_dEghcI7kwmHT3AQmAPpzkA,850
29
- tests/unit/test_spikeglx.py,sha256=9PrSOPGrYAAQEeJPAOmqc3Rhgia6ftv-zihVWXglhqw,34388
30
- tests/unit/test_utils.py,sha256=37XQDUqcABYrrsdX17kX54H4e5jld7GOn1ISxtgoa5U,21859
31
- tests/unit/test_voltage.py,sha256=miLjw7gAAvOj-o0Jxn7xZpyXcA499WTyY8DjB1xapyg,5917
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
32
34
  tests/unit/test_waveforms.py,sha256=VnFvUi1pteROwwbC5Ebp2lqSxF3a8a7eXHpD8OUeuTg,16237
33
- ibl_neuropixel-1.9.3.dist-info/METADATA,sha256=UzCU-I7_KkyqA2QLFYDo3euwvGrhl3nzopr-CRPUEsI,3746
34
- ibl_neuropixel-1.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
- ibl_neuropixel-1.9.3.dist-info/top_level.txt,sha256=WtVcEUptnwU6BT72cgGmrWYFGM9d9qCEqe3LwR9FIw4,48
36
- ibl_neuropixel-1.9.3.dist-info/RECORD,,
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/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):