pstutils 0.1.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.
pstutils/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ from .pstutils import DEBUG
2
+ from .pstutils import extract_wb_data_to_bin
3
+ from .pstutils import extract_pl2_data_to_nex5_stash
4
+ from .pstutils import robocopy
5
+ from .pstutils import archive
6
+ from .pstutils import get_disk_usage
7
+ from .pstutils import build_final_nex5_from_npy_and_pl2
8
+ from .pstutils import build_final_nex5_from_npy_and_stash
9
+ from .pstutils import extract_pl2_headers_to_json
10
+ # from .pstutils import _get_channel_out_of_tune_durations as get_oot_durs
11
+ from .pstutils import get_bad_channels
12
+ from .pstutils import get_probe_names
13
+ from .pstutils import get_unit_id_to_neuron_name_mapping
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,278 @@
1
+ """
2
+ Data classes for .nex and .nex5 files.
3
+ """
4
+ import numpy as np
5
+ from typing import List
6
+
7
+
8
+ def CalcScaleFloatsToShorts(numbers: 'np.ndarray[np.float32]') -> float:
9
+ """Calculates coefficient that can be used to convert float values to shorts (16-bit integers).
10
+
11
+ Args:
12
+ numbers (np.ndarray[np.float32]): array of float values
13
+
14
+ Returns:
15
+ float: coefficient such that float_value*coefficient is within 16-bit
16
+ integer range for all values in array.
17
+ """
18
+ theMax = np.amax(numbers)
19
+ theMin = np.amin(numbers)
20
+ absMax = max(abs(theMin), abs(theMax))
21
+ if absMax == 0.0:
22
+ return 1.0
23
+ else:
24
+ return 32767.0 / absMax
25
+
26
+
27
+ class Variable:
28
+ """Base variable class: name only."""
29
+
30
+ def __init__(self, name: str = "") -> None:
31
+ self.Name: str = name
32
+ """Variable name."""
33
+
34
+
35
+ class Event(Variable):
36
+ """Event class: name and array of timestamps in seconds."""
37
+
38
+ def __init__(self, name: str = "", timestamps: List[float] = []) -> None:
39
+ self.Name: str = name
40
+ """Variable name."""
41
+
42
+ self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
43
+ """Timestamps in seconds."""
44
+
45
+ def MaxTimestamp(self) -> float:
46
+ """Returns maximum timestamp in seconds."""
47
+ if self.Timestamps is None or len(self.Timestamps) == 0:
48
+ return 0
49
+ return self.Timestamps[-1]
50
+
51
+
52
+ class Neuron(Event):
53
+ """
54
+ Neuron class: event data plus wire, unit and position data.
55
+ """
56
+
57
+ def __init__(self, name: str = "", timestamps: List[float] = []) -> None:
58
+ self.Name: str = name
59
+ """Variable name."""
60
+
61
+ self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
62
+ """Timestamps in seconds."""
63
+
64
+ self.WireNumber: int = 0
65
+ """Wire (electrode) number."""
66
+
67
+ self.UnitNumber: int = 0
68
+ """Unit or cluster number."""
69
+
70
+ self.XPos: float = 0
71
+ """X axis electrode position in (0,100) range, used in 3D display."""
72
+
73
+ self.YPos: float = 0
74
+ """Y axis electrode position in (0,100) range, used in 3D display."""
75
+
76
+
77
+ class Marker(Event):
78
+ """Markers are events with associated strings or integers."""
79
+
80
+ def __init__(self, name: str = "", timestamps: List[float] = [],
81
+ fieldNames: List[str] = [], markerValues: List[List[str]] = []) -> None:
82
+ self.Name: str = name
83
+ """Variable name."""
84
+
85
+ self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
86
+ """Timestamps in seconds."""
87
+
88
+ self.FieldNames: List[str] = fieldNames
89
+ """Each timestamp can have several strings or integers associated with it (several fields). These are filed names."""
90
+
91
+ self.MarkerValues: List[List[str]] = markerValues
92
+ """Each timestamp can have several strings or integers associated with it (several fields).
93
+ Each sublist contains string values for a specific marker field."""
94
+
95
+ self.MarkerValuesAsUnsignedIntegers = [[]]
96
+ """List of np arrays. Each list element contains numeric values for a specific marker field."""
97
+
98
+ def MaxMarkerLength(self):
99
+ """Returns maximum length of all string marker values."""
100
+ maxLen = 0
101
+ if len(self.MarkerValues) > 0:
102
+ for fieldValues in self.MarkerValues:
103
+ for m in fieldValues:
104
+ maxLen = max(maxLen, len(m))
105
+ return maxLen
106
+ else:
107
+ for fieldValues in self.MarkerValuesAsUnsignedIntegers:
108
+ for m in fieldValues:
109
+ maxLen = max(maxLen, len(str(m)))
110
+ return maxLen
111
+
112
+
113
+ class Interval(Variable):
114
+ """Interval class. Has two arrays: interval starts and interval ends in seconds."""
115
+
116
+ def __init__(self, name: str = "", starts: List[float] = [], ends: List[float] = []) -> None:
117
+ self.Name: str = name
118
+ """Variable name."""
119
+
120
+ self.IntervalStarts: 'np.ndarray[np.float64]' = np.asarray(starts).astype(np.float64)
121
+ """Interval start times in seconds."""
122
+
123
+ self.IntervalEnds: 'np.ndarray[np.float64]' = np.asarray(ends).astype(np.float64)
124
+ """Interval end times in seconds."""
125
+
126
+ def MaxTimestamp(self) -> float:
127
+ """Returns maximum timestamp in seconds."""
128
+ if self.IntervalEnds is None or len(self.IntervalEnds) == 0:
129
+ return 0
130
+ return self.IntervalEnds[-1]
131
+
132
+
133
+ class Continuous(Variable):
134
+ """ Continuous class.
135
+ Contains: (1) Sampling rate in Hz. (2) Array of timestamps in seconds (each timestamp is for the beginning of the fragment).
136
+ (3) Array of indexes (each index is the position of the first data point of the fragment in the a/d array).
137
+ (4) Array of all a/d values in milliVolts.
138
+ """
139
+
140
+ def __init__(self, name: str = "", samplingRate: float = 0, fragmentStarts: List[float] = [],
141
+ fragmentStartIndexes: List[int] = [],
142
+ values: List[float] = []) -> None:
143
+ self.Name: str = name
144
+ """Variable name."""
145
+
146
+ self.SamplingRate: float = samplingRate
147
+ """Sampling rate in Hz."""
148
+
149
+ self.FragmentTimestamps: 'np.ndarray[np.float64]' = np.asarray(fragmentStarts).astype(np.float64)
150
+ """Array of timestamps in seconds (each timestamp is for the beginning of the fragment)."""
151
+
152
+ self.FragmentStartIndexes: 'np.ndarray[np.uint32]' = np.asarray(fragmentStartIndexes).astype(np.uint32)
153
+ """Array of indexes (each index is the position of the first data point of the fragment in the a/d array)."""
154
+
155
+ self.Values: 'np.ndarray[np.float32]' = np.asarray(values).astype(np.float32)
156
+ """Array of all a/d values in milliVolts."""
157
+
158
+ self.CalculatedScaleFloatsToShorts = 1
159
+ """When saving data to .nex file, this field contains coefficient to convert floats to shorts."""
160
+
161
+ def MaxTimestamp(self) -> float:
162
+ """Returns maximum timestamp in seconds (the last timestamp of all continuous values)."""
163
+ if self.FragmentTimestamps is None or len(self.FragmentTimestamps) == 0 or self.SamplingRate <= 0:
164
+ return 0
165
+ if self.FragmentStartIndexes is None or len(self.FragmentStartIndexes) == 0 or self.Values is None or len(self.Values) == 0:
166
+ return 0
167
+ step = 1.0/self.SamplingRate
168
+ return self.FragmentTimestamps[-1] + step * (len(self.Values) - self.FragmentStartIndexes[-1] - 1.0)
169
+
170
+
171
+ class Waveform(Event):
172
+ """ Waveform class.
173
+ Contains: Sampling rate in Hz
174
+ Number of points in each wave
175
+ Array of timestamps in seconds (each timestamp is for the beginning of the waveform).
176
+ Array of all waveform a/d values in milliVolts.
177
+ """
178
+
179
+ def __init__(self, name: str = "", samplingRate: float = 0, timestamps: List[float] = [],
180
+ numPointsWave: int = 0, values: List[float] = []):
181
+ self.Name: str = name
182
+ """Variable name."""
183
+
184
+ self.SamplingRate: float = samplingRate
185
+ """Sampling rate in Hz."""
186
+
187
+ self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
188
+ """Timestamps in seconds."""
189
+
190
+ self.WireNumber: int = 0
191
+ """Wire (electrode) number."""
192
+
193
+ self.UnitNumber: int = 0
194
+ """Unit or cluster number."""
195
+
196
+ self.NumPointsWave: int = numPointsWave
197
+ """Number of data point in each wave."""
198
+
199
+ self.Values: 'np.ndarray[np.float32]' = np.asarray(values).astype(np.float32)
200
+ """Waveform values in milliVolts."""
201
+
202
+ self.Values = self.Values.reshape((len(self.Timestamps), self.NumPointsWave))
203
+
204
+ self.CalculatedScaleFloatsToShorts = 1
205
+ """When saving data to .nex file, this field contains coefficient to convert floats to shorts."""
206
+
207
+ def MaxTimestamp(self) -> float:
208
+ """Returns maximum timestamp in seconds (the last timestamp of the last waveform)."""
209
+ if self.Timestamps is None or len(self.Timestamps) == 0 or self.SamplingRate <= 0 or self.NumPointsWave == 0:
210
+ return 0
211
+ step = 1.0/self.SamplingRate
212
+ return self.Timestamps[-1] + step * (self.NumPointsWave - 1.0)
213
+
214
+
215
+ class FileData:
216
+ """
217
+ FileData object. Contains arrays of data variables.
218
+ """
219
+
220
+ def __init__(self) -> None:
221
+ self.Comment: str = ""
222
+ """File comment."""
223
+
224
+ self.TimestampFrequency: float = 0
225
+ """Timestamps frequency, in Hertz. Timestamp values in .net and .nex5 files are stored in ticks, where tick = 1/TimestampFrequency."""
226
+
227
+ self.StartTimeSeconds: float = 0
228
+ """Start time in seconds."""
229
+
230
+ self.EndTimeSeconds: float = 0
231
+ """End time in seconds."""
232
+
233
+ self.Events: List[Event] = []
234
+ """List of Events."""
235
+
236
+ self.Neurons: List[Neuron] = []
237
+ """List of Neurons."""
238
+
239
+ self.Intervals: List[Interval] = []
240
+ """List of intervals."""
241
+
242
+ self.Markers: List[Marker] = []
243
+ """List of Markers."""
244
+
245
+ self.Continuous: List[Continuous] = []
246
+ """List of Continuous variables."""
247
+
248
+ self.Waveforms: List[Waveform] = []
249
+ """List of Waveform variables."""
250
+
251
+ def NumberOfVariables(self) -> int:
252
+ """Returns number of all variables"""
253
+ return len(self.Neurons) + len(self.Events) + len(self.Intervals) + len(self.Markers) + len(self.Continuous) + len(self.Waveforms)
254
+
255
+ def MaxTimestamp(self) -> float:
256
+ """Returns maximum timestamp of all variables in seconds."""
257
+ maxTs = 0
258
+ for v in self.Neurons:
259
+ maxTs = max(maxTs, v.MaxTimestamp())
260
+ for v in self.Events:
261
+ maxTs = max(maxTs, v.MaxTimestamp())
262
+ for v in self.Intervals:
263
+ maxTs = max(maxTs, v.MaxTimestamp())
264
+ for v in self.Markers:
265
+ maxTs = max(maxTs, v.MaxTimestamp())
266
+ for v in self.Continuous:
267
+ maxTs = max(maxTs, v.MaxTimestamp())
268
+ for v in self.Waveforms:
269
+ maxTs = max(maxTs, v.MaxTimestamp())
270
+ return maxTs
271
+
272
+ def TicksToSeconds(self, ticks: int) -> float:
273
+ """Converts time in thicks to time in seconds."""
274
+ return ticks/self.TimestampFrequency
275
+
276
+ def SecondsToTicks(self, seconds: float) -> int:
277
+ """Converts time in seconds to time in ticks."""
278
+ return round(seconds * self.TimestampFrequency)