imspy-core 0.4.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.
@@ -0,0 +1,189 @@
1
+ from typing import Union, List
2
+
3
+ from imspy_core.data.spectrum import MzSpectrum
4
+ from imspy_core.timstof.frame import TimsFrame
5
+ from numpy.typing import NDArray
6
+
7
+ import imspy_connector
8
+ ims = imspy_connector.py_quadrupole
9
+
10
+ class PasefMeta:
11
+ def __init__(self, frame: int, scan_start: int, scan_end: int, isolation_mz: float, isolation_width: float,
12
+ collision_energy: float, precursor: int):
13
+ self.__py_ptr = ims.PyPasefMeta(frame, scan_start, scan_end, isolation_mz, isolation_width, collision_energy, precursor)
14
+
15
+ @property
16
+ def frame(self) -> int:
17
+ return self.__py_ptr.frame
18
+
19
+ @property
20
+ def scan_start(self) -> int:
21
+ return self.__py_ptr.scan_start
22
+
23
+ @property
24
+ def scan_end(self) -> int:
25
+ return self.__py_ptr.scan_end
26
+
27
+ @property
28
+ def isolation_mz(self) -> float:
29
+ return self.__py_ptr.isolation_mz
30
+
31
+ @property
32
+ def isolation_width(self) -> float:
33
+ return self.__py_ptr.isolation_width
34
+
35
+ @property
36
+ def collision_energy(self) -> float:
37
+ return self.__py_ptr.collision_energy
38
+
39
+ @property
40
+ def precursor(self) -> int:
41
+ return self.__py_ptr.precursor
42
+
43
+ def __repr__(self):
44
+ return f"PasefMeta(frame={self.frame}, scan_start={self.scan_start}, scan_end={self.scan_end}, " \
45
+ f"isolation_mz={self.isolation_mz}, isolation_width={self.isolation_width}, " \
46
+ f"collision_energy={self.collision_energy}, precursor={self.precursor})"
47
+
48
+ @classmethod
49
+ def from_py_ptr(cls, py_ptr):
50
+ self = cls.__new__(cls)
51
+ self.__py_ptr = py_ptr
52
+ return self
53
+
54
+ def get_py_ptr(self):
55
+ return self.__py_ptr
56
+
57
+
58
+ class TimsTofQuadrupoleDDA:
59
+ def __init__(self, pasef_meta: List[PasefMeta], k: float | None = None):
60
+ self.__py_ptr = ims.PyTimsTransmissionDDA(
61
+ [pasef_meta[i].get_py_ptr() for i in range(len(pasef_meta))], k
62
+ )
63
+ def apply_transmission(self, frame_id: int, scan_id: int, mz: NDArray) -> NDArray:
64
+ return self.__py_ptr.apply_transmission(frame_id, scan_id, mz)
65
+
66
+ def transmit_spectrum(self, frame_id: int, scan_id: int, spectrum: MzSpectrum, min_probability: float | None = None) -> MzSpectrum:
67
+ return MzSpectrum.from_py_ptr(self.__py_ptr.transmit_spectrum(frame_id, scan_id, spectrum.get_py_ptr(), min_probability))
68
+
69
+ def transmit_frame(self, frame: TimsFrame, min_probability: float | None = None) -> TimsFrame:
70
+ return TimsFrame.from_py_ptr(self.__py_ptr.transmit_tims_frame(frame.get_py_ptr(), min_probability))
71
+
72
+ def transmit_ion(self, frames: NDArray, scans: NDArray, spectrum: MzSpectrum, min_probability: float | None = None) -> List[List[MzSpectrum]]:
73
+ transmission_profile = self.__py_ptr.transmit_ion(frames, scans, spectrum.get_py_ptr(), min_probability)
74
+ result = []
75
+ for i in enumerate(frames):
76
+ scan_list = []
77
+ for j in enumerate(scans):
78
+ scan_list.append(MzSpectrum.from_py_ptr(transmission_profile[i][j]))
79
+ result.append(scan_list)
80
+
81
+ return result
82
+
83
+ def is_transmitted(self, frame_id: int, scan_id: int, mz: float, min_probability: float | None = None) -> bool:
84
+ return self.__py_ptr.is_transmitted(frame_id, scan_id, mz, min_probability)
85
+
86
+ def any_transmitted(self, frame_id: int, scan_id: int, mz: NDArray, min_probability: float | None = None) -> bool:
87
+ return self.__py_ptr.any_transmitted(frame_id, scan_id, mz, min_probability)
88
+
89
+ def all_transmitted(self, frame_id: int, scan_id: int, mz: NDArray, min_probability: float | None) -> bool:
90
+ return self.__py_ptr.all_transmitted(frame_id, scan_id, mz, min_probability)
91
+
92
+ def get_transmission_set(self, frame_id: int, scan_id: int, mz: NDArray, min_probability: float | None) -> set[int]:
93
+ return self.__py_ptr.get_transmission_set(frame_id, scan_id, mz, min_probability)
94
+
95
+ def isotopes_transmitted(self, frame_id: int, scan_id: int, mz_mono: float, mz: NDArray, min_probability: float | None) -> tuple[float, list[tuple[float, float]]]:
96
+ return self.__py_ptr.isotopes_transmitted(frame_id, scan_id, mz_mono, mz, min_probability)
97
+
98
+ def __repr__(self):
99
+ return f"TimsTofQuadrupoleDDA()"
100
+
101
+ @classmethod
102
+ def from_py_ptr(cls, py_ptr):
103
+ self = cls.__new__(cls)
104
+ self.__py_ptr = py_ptr
105
+ return self
106
+
107
+ def get_py_ptr(self):
108
+ return self.__py_ptr
109
+
110
+ class TimsTofQuadrupoleDIA:
111
+ def __init__(self, frame: NDArray, frame_window_group: NDArray, window_group: NDArray, scan_start: NDArray,
112
+ scan_end: NDArray, isolation_mz: NDArray, isolation_width: NDArray, k: float | None = None):
113
+ self.handle = ims.PyTimsTransmissionDIA(
114
+ frame, frame_window_group, window_group, scan_start, scan_end, isolation_mz, isolation_width, k
115
+ )
116
+
117
+ def apply_transmission(self, frame_id: int, scan_id: int, mz: NDArray) -> NDArray:
118
+ return self.handle.apply_transmission(frame_id, scan_id, mz)
119
+
120
+ def transmit_spectrum(self, frame_id: int, scan_id: int, spectrum: MzSpectrum,
121
+ min_probability: float | None = None) -> MzSpectrum:
122
+ return MzSpectrum.from_py_ptr(self.handle.transmit_spectrum(frame_id, scan_id,
123
+ spectrum.get_py_ptr(), min_probability))
124
+
125
+ def transmit_frame(self, frame: TimsFrame, min_probability: float | None = None) -> TimsFrame:
126
+ return TimsFrame.from_py_ptr(self.handle.transmit_tims_frame(frame.get_py_ptr(), min_probability))
127
+
128
+ def frame_to_window_group(self, frame_id: int) -> int:
129
+ return self.handle.frame_to_window_group(frame_id)
130
+
131
+ def is_transmitted(self, frame_id: int, scan_id: int, mz: float, min_proba: float | None = None) -> bool:
132
+ return self.handle.is_transmitted(frame_id, scan_id, mz, min_proba)
133
+
134
+ def any_transmitted(self, frame_id: int, scan_id: int, mz: NDArray, min_proba: float | None = None) -> bool:
135
+ return self.handle.any_transmitted(frame_id, scan_id, mz, min_proba)
136
+
137
+ def all_transmitted(self, frame_id: int, scan_id: int, mz: NDArray, min_proba: float | None = None) -> bool:
138
+ return self.handle.all_transmitted(frame_id, scan_id, mz, min_proba)
139
+
140
+ def get_transmission_set(self, frame_id: int, scan_id: int, mz: NDArray, min_proba: float | None = None) -> set[int]:
141
+ return self.handle.get_transmission_set(frame_id, scan_id, mz, min_proba)
142
+
143
+ def transmit_ion(self, frame_ids: NDArray, scan_ids: NDArray, spectrum: MzSpectrum, min_probability: Union[float, None]) -> List[List[MzSpectrum]]:
144
+ transmission_profile = self.handle.transmit_ion(frame_ids, scan_ids, spectrum.get_py_ptr(), min_probability)
145
+ result = []
146
+ for i in enumerate(frame_ids):
147
+ scan_list = []
148
+ for j in enumerate(scan_ids):
149
+ scan_list.append(MzSpectrum.from_py_ptr(transmission_profile[i][j]))
150
+ result.append(scan_list)
151
+
152
+ return result
153
+
154
+ def is_precursor(self, frame_id: int) -> bool:
155
+ return self.handle.is_precursor(frame_id)
156
+
157
+ def isotopes_transmitted(
158
+ self,
159
+ frame_id: int,
160
+ scan_id: int,
161
+ mz_mono: float,
162
+ mz: NDArray,
163
+ min_proba: float | None = None
164
+ ) -> tuple[float, list[tuple[float, float]]]:
165
+ """
166
+ Get the transmission probability for a list of isotopes
167
+ Args:
168
+ frame_id:
169
+ scan_id:
170
+ mz_mono:
171
+ mz:
172
+ min_proba:
173
+
174
+ Returns:
175
+
176
+ """
177
+ return self.handle.isotopes_transmitted(frame_id, scan_id, mz_mono, mz, min_proba)
178
+
179
+ def __repr__(self):
180
+ return f"TimsTofQuadrupoleDIA()"
181
+
182
+ @classmethod
183
+ def from_py_ptr(cls, py_ptr):
184
+ self = cls.__new__(cls)
185
+ self.handle = py_ptr
186
+ return self
187
+
188
+ def get_py_ptr(self):
189
+ return self.handle
@@ -0,0 +1,506 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from typing import List
4
+
5
+ from numpy.typing import NDArray
6
+
7
+ from imspy_core.utility.utilities import re_index_indices
8
+ from imspy_core.timstof.frame import TimsFrame, TimsFrameVectorized
9
+ from imspy_core.data.spectrum import TimsSpectrum
10
+
11
+ import imspy_connector
12
+ ims = imspy_connector.py_tims_slice
13
+
14
+
15
+ class TimsSlice:
16
+ def __init__(self,
17
+ frame_id: NDArray[np.int32],
18
+ scan: NDArray[np.int32],
19
+ tof: NDArray[np.int32],
20
+ retention_time: NDArray[np.float64],
21
+ mobility: NDArray[np.float64],
22
+ mz: NDArray[np.float64],
23
+ intensity: NDArray[np.float64]):
24
+ """Create a TimsSlice.
25
+
26
+ Args:
27
+ frame_id (NDArray[np.int32]): Frame ID.
28
+ scan (NDArray[np.int32]): Scan.
29
+ tof (NDArray[np.int32]): TOF.
30
+ retention_time (NDArray[np.float64]): Retention time.
31
+ mobility (NDArray[np.float64]): Mobility.
32
+ mz (NDArray[np.float64]): m/z.
33
+ intensity (NDArray[np.float64]): Intensity.
34
+ """
35
+
36
+ assert len(frame_id) == len(scan) == len(tof) == len(retention_time) == len(mobility) == len(mz) == len(
37
+ intensity), "All arrays must have the same length."
38
+
39
+ self.__slice_ptr = ims.PyTimsSlice(
40
+ frame_id, scan, tof, retention_time, mobility, mz, intensity
41
+ )
42
+ self.__current_index = 0
43
+
44
+ @classmethod
45
+ def from_py_tims_slice(cls, tims_slice: ims.PyTimsSlice):
46
+ """Create a TimsSlice from a PyTimsSlice.
47
+
48
+ Args:
49
+ tims_slice (pims.PyTimsSlice): PyTimsSlice to create the TimsSlice from.
50
+
51
+ Returns:
52
+ TimsSlice: TimsSlice created from the PyTimsSlice.
53
+ """
54
+ instance = cls.__new__(cls)
55
+ instance.__slice_ptr = tims_slice
56
+ instance.__current_index = 0
57
+ return instance
58
+
59
+ @classmethod
60
+ def from_frames(cls, frames: List[TimsFrame]):
61
+ """Create a TimsSlice from a list of TimsFrames.
62
+
63
+ Args:
64
+ frames (List[TimsFrame]): List of TimsFrames.
65
+
66
+ Returns:
67
+ TimsSlice: TimsSlice created from the list of TimsFrames.
68
+ """
69
+ return cls.from_py_tims_slice(ims.PyTimsSlice.from_frames([frame.get_py_ptr() for frame in frames]))
70
+
71
+ @property
72
+ def first_frame_id(self) -> int:
73
+ """First frame ID.
74
+
75
+ Returns:
76
+ int: First frame ID.
77
+ """
78
+ return self.__slice_ptr.first_frame_id
79
+
80
+ @property
81
+ def last_frame_id(self) -> int:
82
+ """Last frame ID.
83
+
84
+ Returns:
85
+ int: Last frame ID.
86
+ """
87
+ return self.__slice_ptr.last_frame_id
88
+
89
+ def __repr__(self):
90
+ return f"TimsSlice({self.first_frame_id}, {self.last_frame_id})"
91
+
92
+ @property
93
+ def precursors(self):
94
+ return TimsSlice.from_py_tims_slice(self.__slice_ptr.get_precursors())
95
+
96
+ @property
97
+ def fragments(self):
98
+ return TimsSlice.from_py_tims_slice(self.__slice_ptr.get_fragments_dda())
99
+
100
+ def filter(self, mz_min: float = 0.0, mz_max: float = 2000.0, scan_min: int = 0, scan_max: int = 1000,
101
+ mobility_min: float = 0.0,
102
+ mobility_max: float = 2.0,
103
+ intensity_min: float = 0.0, intensity_max: float = 1e9, num_threads: int = 4) -> 'TimsSlice':
104
+ """Filter the slice by m/z, scan and intensity.
105
+
106
+ Args:
107
+ mz_min (float): Minimum m/z value.
108
+ mz_max (float): Maximum m/z value.
109
+ scan_min (int, optional): Minimum scan value. Defaults to 0.
110
+ scan_max (int, optional): Maximum scan value. Defaults to 1000.
111
+ mobility_min (float, optional): Minimum inverse mobility value. Defaults to 0.0.
112
+ mobility_max (float, optional): Maximum inverse mobility value. Defaults to 2.0.
113
+ intensity_min (float, optional): Minimum intensity value. Defaults to 0.0.
114
+ intensity_max (float, optional): Maximum intensity value. Defaults to 1e9.
115
+ num_threads (int, optional): Number of threads to use. Defaults to 4.
116
+
117
+ Returns:
118
+ TimsSlice: Filtered slice.
119
+ """
120
+ return TimsSlice.from_py_tims_slice(
121
+ self.__slice_ptr.filter_ranged(mz_min, mz_max, scan_min, scan_max, mobility_min, mobility_max,
122
+ intensity_min, intensity_max, num_threads))
123
+
124
+ def filter_by_type(self,
125
+ mz_min_ms1: float = 0,
126
+ mz_max_ms1: float = 2000,
127
+ scan_min_ms1: int = 0,
128
+ scan_max_ms1: int = 1000,
129
+ inv_mob_min_ms1: float = 0,
130
+ inv_mob_max_ms1: float = 2,
131
+ intensity_min_ms1: float = 0,
132
+ intensity_max_ms1: float = 1e9,
133
+ mz_min_ms2: float = 0,
134
+ mz_max_ms2: float = 2000,
135
+ scan_min_ms2: int = 0,
136
+ scan_max_ms2: int = 1000,
137
+ inv_mob_min_ms2: float = 0,
138
+ inv_mob_max_ms2: float = 2,
139
+ intensity_min_ms2: float = 0,
140
+ intensity_max_ms2: float = 1e9,
141
+ num_threads: int = 4) -> 'TimsSlice':
142
+
143
+ """Filter the slice by m/z, scan and intensity, for MS1 and MS2 with different ranges.
144
+
145
+ Args:
146
+ mz_min_ms1 (float, optional): Minimum m/z value for MS1. Defaults to 0.
147
+ mz_max_ms1 (float, optional): Maximum m/z value for MS1. Defaults to 2000.
148
+ scan_min_ms1 (int, optional): Minimum scan value for MS1. Defaults to 0.
149
+ scan_max_ms1 (int, optional): Maximum scan value for MS1. Defaults to 1000.
150
+ inv_mob_min_ms1 (float, optional): Minimum inverse mobility value for MS1. Defaults to 0.
151
+ inv_mob_max_ms1 (float, optional): Maximum inverse mobility value for MS1. Defaults to 2.
152
+ intensity_min_ms1 (float, optional): Minimum intensity value for MS1. Defaults to 0.
153
+ intensity_max_ms1 (float, optional): Maximum intensity value for MS1. Defaults to 1e9.
154
+ mz_min_ms2 (float, optional): Minimum m/z value for MS2. Defaults to 0.
155
+ mz_max_ms2 (float, optional): Maximum m/z value for MS2. Defaults to 2000.
156
+ scan_min_ms2 (int, optional): Minimum scan value for MS2. Defaults to 0.
157
+ scan_max_ms2 (int, optional): Maximum scan value for MS2. Defaults to 1000.
158
+ inv_mob_min_ms2 (float, optional): Minimum inverse mobility value for MS2. Defaults to 0.
159
+ inv_mob_max_ms2 (float, optional): Maximum inverse mobility value for MS2. Defaults to 2.
160
+ intensity_min_ms2 (float, optional): Minimum intensity value for MS2. Defaults to 0.
161
+ intensity_max_ms2 (float, optional): Maximum intensity value for MS2. Defaults to 1e9.
162
+ num_threads (int, optional): Number of threads to use. Defaults to 4.
163
+
164
+ Returns:
165
+ TimsSlice: Filtered slice.
166
+ """
167
+ return TimsSlice.from_py_tims_slice(
168
+ self.__slice_ptr.filter_ranged_ms_type_specific(
169
+ mz_min_ms1, mz_max_ms1, scan_min_ms1, scan_max_ms1, inv_mob_min_ms1,
170
+ inv_mob_max_ms1, intensity_min_ms1, intensity_max_ms1, mz_min_ms2, mz_max_ms2,
171
+ scan_min_ms2, scan_max_ms2, inv_mob_min_ms2, inv_mob_max_ms2, intensity_min_ms2,
172
+ intensity_max_ms2, num_threads))
173
+
174
+ @property
175
+ def frames(self) -> List[TimsFrame]:
176
+ """Get the frames.
177
+
178
+ Returns:
179
+ List[TimsFrame]: Frames.
180
+ """
181
+ return [TimsFrame.from_py_ptr(frame) for frame in self.__slice_ptr.get_frames()]
182
+
183
+ def to_resolution(self, resolution: int, num_threads: int = 4) -> 'TimsSlice':
184
+ """Convert the slice to a given resolution.
185
+
186
+ Args:
187
+ resolution (int): Resolution.
188
+ num_threads (int, optional): Number of threads to use. Defaults to 4.
189
+
190
+ Returns:
191
+ TimsSlice: Slice with given resolution.
192
+ """
193
+ return TimsSlice.from_py_tims_slice(self.__slice_ptr.to_resolution(resolution, num_threads))
194
+
195
+ def to_windows(self, window_length: float = 10, overlapping: bool = True, min_num_peaks: int = 5,
196
+ min_intensity: float = 1, num_threads: int = 4) -> List[TimsSpectrum]:
197
+ """Convert the slice to a list of windows.
198
+
199
+ Args:
200
+ window_length (float, optional): Window length. Defaults to 10.
201
+ overlapping (bool, optional): Whether the windows should overlap. Defaults to True.
202
+ min_num_peaks (int, optional): Minimum number of peaks in a window. Defaults to 5.
203
+ min_intensity (float, optional): Minimum intensity of a peak in a window. Defaults to 1.
204
+ num_threads (int, optional): Number of threads to use. Defaults to 1.
205
+
206
+ Returns:
207
+ List[MzSpectrum]: List of windows.
208
+ """
209
+ return [TimsSpectrum.from_py_tims_spectrum(spec) for spec in self.__slice_ptr.to_windows(
210
+ window_length, overlapping, min_num_peaks, min_intensity, num_threads)]
211
+
212
+ def to_dense_windows(self, window_length: float = 10, resolution: int = 1, overlapping: bool = True,
213
+ min_num_peaks: int = 5, min_intensity: float = 0.0, num_theads: int = 4) -> (
214
+ tuple)[list[NDArray], list[NDArray], list[NDArray]]:
215
+
216
+ DW = self.__slice_ptr.to_dense_windows(window_length, overlapping, min_num_peaks, min_intensity, resolution,
217
+ num_theads)
218
+
219
+ scan_list, window_indices_list, values_list = [], [], []
220
+
221
+ for values, scans, bins, row, col in DW:
222
+ W = np.reshape(values, (row, col))
223
+ scan_list.append(scans)
224
+ window_indices_list.append(bins)
225
+ values_list.append(W)
226
+
227
+ return scan_list, window_indices_list, values_list
228
+
229
+ @property
230
+ def df(self) -> pd.DataFrame:
231
+ """Get the data as a pandas DataFrame.
232
+
233
+ Returns:
234
+ pd.DataFrame: Data.
235
+ """
236
+ columns = ['frame', 'scan', 'tof', 'retention_time', 'mobility', 'mz', 'intensity']
237
+ return pd.DataFrame({c: v for c, v in zip(columns, self.__slice_ptr.to_arrays())})
238
+
239
+ def __iter__(self):
240
+ return self
241
+
242
+ def __next__(self):
243
+ if self.__current_index < self.__slice_ptr.frame_count:
244
+ frame_ptr = self.__slice_ptr.get_frame_at_index(self.__current_index)
245
+ self.__current_index += 1
246
+ if frame_ptr is not None:
247
+ return TimsFrame.from_py_ptr(frame_ptr)
248
+ else:
249
+ raise ValueError("Frame pointer is None for valid index.")
250
+ else:
251
+ self.__current_index = 0 # Reset for next iteration
252
+ raise StopIteration
253
+
254
+ def vectorized(self, resolution: int = 2, num_threads: int = 4) -> 'TimsSliceVectorized':
255
+ """Get a vectorized version of the slice.
256
+
257
+ Args:
258
+ resolution (int, optional): Resolution. Defaults to 2.
259
+ num_threads (int, optional): Number of threads to use. Defaults to 4.
260
+
261
+ Returns:
262
+ TimsSliceVectorized: Vectorized version of the slice.
263
+ """
264
+ return TimsSliceVectorized.from_vectorized_py_tims_slice(self.__slice_ptr.vectorized(resolution, num_threads))
265
+
266
+ def get_tims_planes(self, tof_max_value: int = 400_000, num_chunks: int = 7, num_threads: int = 4) -> List[
267
+ 'TimsPlane']:
268
+ return [TimsPlane.from_py_tims_plane(plane) for plane in
269
+ self.__slice_ptr.to_tims_planes(tof_max_value, num_chunks, num_threads)]
270
+
271
+
272
+ class TimsSliceVectorized:
273
+ def __init__(self):
274
+ self.__slice_ptr = None
275
+ self.__current_index = 0
276
+
277
+ @classmethod
278
+ def from_vectorized_py_tims_slice(cls, tims_slice: ims.PyTimsSliceVectorized):
279
+ """Create a TimsSlice from a PyTimsSlice.
280
+
281
+ Args:
282
+ tims_slice (pims.PyTimsSlice): PyTimsSlice to create the TimsSlice from.
283
+
284
+ Returns:
285
+ TimsSlice: TimsSlice created from the PyTimsSlice.
286
+ """
287
+ instance = cls.__new__(cls)
288
+ instance.__slice_ptr = tims_slice
289
+ instance.__current_index = 0
290
+ return instance
291
+
292
+ @property
293
+ def first_frame_id(self) -> int:
294
+ """First frame ID.
295
+
296
+ Returns:
297
+ int: First frame ID.
298
+ """
299
+ return self.__slice_ptr.first_frame_id
300
+
301
+ @property
302
+ def last_frame_id(self) -> int:
303
+ """Last frame ID.
304
+
305
+ Returns:
306
+ int: Last frame ID.
307
+ """
308
+ return self.__slice_ptr.last_frame_id
309
+
310
+ @property
311
+ def precursors(self):
312
+ return TimsSlice.from_py_tims_slice(self.__slice_ptr.get_precursors())
313
+
314
+ @property
315
+ def fragments(self):
316
+ return TimsSlice.from_py_tims_slice(self.__slice_ptr.get_fragments_dda())
317
+
318
+ @property
319
+ def frames(self) -> List[TimsFrameVectorized]:
320
+ """Get the frames.
321
+
322
+ Returns:
323
+ List[TimsFrame]: Frames.
324
+ """
325
+ return [TimsFrameVectorized.from_py_ptr(frame) for frame in
326
+ self.__slice_ptr.get_vectorized_frames]
327
+
328
+ @property
329
+ def df(self) -> pd.DataFrame:
330
+ """Get the data as a pandas DataFrame.
331
+
332
+ Returns:
333
+ pd.DataFrame: Data.
334
+ """
335
+ columns = ['frame', 'scan', 'tof', 'retention_time', 'mobility', 'index', 'intensity']
336
+ return pd.DataFrame({c: v for c, v in zip(columns, self.__slice_ptr.to_arrays())})
337
+
338
+ def __iter__(self):
339
+ return self
340
+
341
+ def __next__(self):
342
+ if self.__current_index < self.__slice_ptr.frame_count:
343
+ frame_ptr = self.__slice_ptr.get_frame_at_index(self.__current_index)
344
+ self.__current_index += 1
345
+ if frame_ptr is not None:
346
+ return TimsFrameVectorized.from_py_ptr(frame_ptr)
347
+ else:
348
+ raise ValueError("Frame pointer is None for valid index.")
349
+ else:
350
+ self.__current_index = 0
351
+ raise StopIteration
352
+
353
+ def __repr__(self):
354
+ return f"TimsSliceVectorized({self.first_frame_id}, {self.last_frame_id})"
355
+
356
+ def get_tensor_repr(self, dense=True, zero_index=True, re_index=True, frame_max=None, scan_max=None,
357
+ index_max=None):
358
+ """Get a tensor representation of the slice.
359
+
360
+ Note:
361
+ Requires tensorflow to be installed.
362
+ """
363
+ try:
364
+ from tensorflow import sparse as sp
365
+ except ImportError:
366
+ raise ImportError(
367
+ "get_tensor_repr requires tensorflow. "
368
+ "Install it with: pip install tensorflow"
369
+ )
370
+
371
+ frames, scans, _, _, _, indices, intensities = self.__slice_ptr.to_arrays()
372
+
373
+ if zero_index:
374
+ scans = scans - np.min(scans)
375
+ frames = frames - np.min(frames)
376
+ indices = indices - np.min(indices)
377
+
378
+ if re_index:
379
+ frames = re_index_indices(frames)
380
+
381
+ if scan_max is None:
382
+ m_s = np.max(scans) + 1
383
+ else:
384
+ m_s = scan_max + 1
385
+
386
+ if index_max is None:
387
+ m_i = np.max(indices) + 1
388
+ else:
389
+ m_i = index_max + 1
390
+
391
+ if frame_max is None:
392
+ m_f = np.max(frames) + 1
393
+ else:
394
+ m_f = frame_max + 1
395
+
396
+ sv = sp.reorder(
397
+ sp.SparseTensor(indices=np.c_[frames, scans, indices], values=intensities, dense_shape=(m_f, m_s, m_i)))
398
+
399
+ if dense:
400
+ return sp.to_dense(sv)
401
+ else:
402
+ return sv
403
+
404
+ def filter(self, mz_min: float = 0.0, mz_max: float = 2000.0, scan_min: int = 0, scan_max: int = 1000,
405
+ mobility_min: float = 0.0,
406
+ mobility_max: float = 2.0,
407
+ intensity_min: float = 0.0, intensity_max: float = 1e9, num_threads: int = 4) -> 'TimsSliceVectorized':
408
+ """Filter the slice by m/z, scan and intensity.
409
+
410
+ Args:
411
+ mz_min (float): Minimum m/z value.
412
+ mz_max (float): Maximum m/z value.
413
+ scan_min (int, optional): Minimum scan value. Defaults to 0.
414
+ scan_max (int, optional): Maximum scan value. Defaults to 1000.
415
+ mobility_min (float, optional): Minimum inverse mobility value. Defaults to 0.0.
416
+ mobility_max (float, optional): Maximum inverse mobility value. Defaults to 2.0.
417
+ intensity_min (float, optional): Minimum intensity value. Defaults to 0.0.
418
+ intensity_max (float, optional): Maximum intensity value. Defaults to 1e9.
419
+ num_threads (int, optional): Number of threads to use. Defaults to 4.
420
+
421
+ Returns:
422
+ TimsSlice: Filtered slice.
423
+ """
424
+ return TimsSliceVectorized.from_vectorized_py_tims_slice(
425
+ self.__slice_ptr.filter_ranged(mz_min, mz_max, scan_min, scan_max, mobility_min, mobility_max,
426
+ intensity_min, intensity_max, num_threads))
427
+
428
+ def get_py_ptr(self):
429
+ return self.__slice_ptr
430
+
431
+
432
+ class TimsPlane:
433
+ def __init__(self):
434
+ self.__plane_ptr = None
435
+
436
+ @classmethod
437
+ def from_py_tims_plane(cls, plane: ims.PyTimsPlane):
438
+ """Create a TimsPlane from a PyTimsPlane.
439
+
440
+ Args:
441
+ plane (pims.PyTimsPlane): PyTimsPlane to create the TimsPlane from.
442
+
443
+ Returns:
444
+ TimsPlane: TimsPlane created from the PyTimsPlane.
445
+ """
446
+ instance = cls.__new__(cls)
447
+ instance.__plane_ptr = plane
448
+ return instance
449
+
450
+ @property
451
+ def mz_mean(self):
452
+ return self.__plane_ptr.mz_mean
453
+
454
+ @property
455
+ def mz_std(self):
456
+ return self.__plane_ptr.mz_std
457
+
458
+ @property
459
+ def tof_mean(self):
460
+ return self.__plane_ptr.tof_mean
461
+
462
+ @property
463
+ def tof_std(self):
464
+ return self.__plane_ptr.tof_std
465
+
466
+ @property
467
+ def frame_ids(self):
468
+ return self.__plane_ptr.frame_ids
469
+
470
+ @property
471
+ def scans(self):
472
+ return self.__plane_ptr.scans
473
+
474
+ @property
475
+ def intensities(self):
476
+ return self.__plane_ptr.intensity
477
+
478
+ @property
479
+ def retention_times(self):
480
+ return self.__plane_ptr.retention_times
481
+
482
+ @property
483
+ def mobilities(self):
484
+ return self.__plane_ptr.mobilities
485
+
486
+ @property
487
+ def num_points(self):
488
+ return len(self.frame_ids)
489
+
490
+ @property
491
+ def df(self):
492
+ return pd.DataFrame({
493
+ 'frame': self.frame_ids,
494
+ 'scan': self.scans,
495
+ 'retention_time': self.retention_times,
496
+ 'mobility': self.mobilities,
497
+ 'intensity': self.intensities
498
+ })
499
+
500
+ def __repr__(self):
501
+ return (f"TimsPlane(mz_mean: "
502
+ f"{np.round(self.mz_mean, 4)}, "
503
+ f"mz_std: {np.round(self.mz_std, 4)},"
504
+ f" tof_mean: {np.round(self.tof_mean, 4)}, "
505
+ f"tof_std: {np.round(self.tof_std, 4)}, "
506
+ f"num_points: {len(self.frame_ids)})")