pymodaq_data 5.0.4__tar.gz → 5.0.5__tar.gz
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.
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/PKG-INFO +1 -1
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/_version.py +2 -2
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/data.py +225 -39
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/data_saving.py +2 -2
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/numpy_func.py +14 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/slicing.py +8 -5
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/.gitignore +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/LICENSE +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/README.rst +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/pyproject.toml +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/backends.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/browsing.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/exporter.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/exporters/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/exporters/base.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/exporters/flimj.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/exporters/hyperspy.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/saving.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/h5modules/utils.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/icon.ico +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/plotting/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/plotting/plotter/plotter.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/plotting/plotter/plotters/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/plotting/plotter/plotters/matplotlib_plotters.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/post_treatment/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/post_treatment/process_to_scalar.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/splash.png +0 -0
|
@@ -20,6 +20,7 @@ import warnings
|
|
|
20
20
|
from time import time
|
|
21
21
|
import copy
|
|
22
22
|
import pint
|
|
23
|
+
from pint.compat import upcast_type_map
|
|
23
24
|
|
|
24
25
|
from multipledispatch import dispatch
|
|
25
26
|
|
|
@@ -158,6 +159,24 @@ class DataDistribution(BaseEnum):
|
|
|
158
159
|
spread = 1
|
|
159
160
|
|
|
160
161
|
|
|
162
|
+
def _compute_slices_from_axis(axis: Axis, _slice, *ignored, is_index=True, **ignored_also):
|
|
163
|
+
if not is_index:
|
|
164
|
+
if isinstance(_slice, numbers.Number):
|
|
165
|
+
if not is_index:
|
|
166
|
+
_slice = axis.find_index(_slice)
|
|
167
|
+
elif _slice is Ellipsis:
|
|
168
|
+
return _slice
|
|
169
|
+
elif isinstance(_slice, slice):
|
|
170
|
+
if not (_slice.start is None and
|
|
171
|
+
_slice.stop is None and _slice.step is None):
|
|
172
|
+
start = axis.find_index(
|
|
173
|
+
_slice.start if _slice.start is not None else axis.get_data()[0])
|
|
174
|
+
stop = axis.find_index(
|
|
175
|
+
_slice.stop if _slice.stop is not None else axis.get_data()[-1])
|
|
176
|
+
_slice = slice(start, stop)
|
|
177
|
+
return _slice
|
|
178
|
+
|
|
179
|
+
|
|
161
180
|
class Axis:
|
|
162
181
|
"""Object holding info and data about physical axis of some data
|
|
163
182
|
|
|
@@ -195,6 +214,7 @@ class Axis:
|
|
|
195
214
|
super().__init__()
|
|
196
215
|
|
|
197
216
|
self.iaxis: Axis = SpecialSlicersData(self, False)
|
|
217
|
+
self.vaxis: Axis = SpecialSlicersData(self, False, False)
|
|
198
218
|
|
|
199
219
|
self._size = size
|
|
200
220
|
self._data = None
|
|
@@ -212,13 +232,22 @@ class Axis:
|
|
|
212
232
|
if (scaling is None or offset is None or size is None) and data is not None:
|
|
213
233
|
self.get_scale_offset_from_data(data)
|
|
214
234
|
|
|
235
|
+
@staticmethod
|
|
236
|
+
def from_quantity(quantity: Q_[np.ndarray], label='axis', index=0) -> Axis:
|
|
237
|
+
return Axis(label, str(quantity.units), data=quantity.magnitude,
|
|
238
|
+
index=index)
|
|
239
|
+
|
|
215
240
|
def copy(self):
|
|
216
241
|
return copy.copy(self)
|
|
217
242
|
|
|
218
|
-
def as_dwa(self) -> DataWithAxes:
|
|
219
|
-
dwa = DataRaw(self.label,
|
|
243
|
+
def as_dwa(self, set_itself_as_axis=False) -> DataWithAxes:
|
|
244
|
+
dwa = DataRaw(self.label, units=self.units,
|
|
245
|
+
data=[self.get_data()],
|
|
220
246
|
labels=[f'{self.label}_{self.units}'])
|
|
221
|
-
|
|
247
|
+
if not set_itself_as_axis:
|
|
248
|
+
dwa.create_missing_axes()
|
|
249
|
+
else:
|
|
250
|
+
dwa.axes = [self.copy()]
|
|
222
251
|
return dwa
|
|
223
252
|
|
|
224
253
|
@property
|
|
@@ -234,16 +263,54 @@ class Axis:
|
|
|
234
263
|
|
|
235
264
|
@property
|
|
236
265
|
def units(self) -> str:
|
|
237
|
-
"""str: get/set the units for this axis
|
|
266
|
+
"""str: get/set the units for this axis without conversion (equivalent to
|
|
267
|
+
force_units)"""
|
|
238
268
|
return self._units
|
|
239
269
|
|
|
240
270
|
@units.setter
|
|
241
271
|
def units(self, units: str):
|
|
242
|
-
|
|
243
|
-
|
|
272
|
+
self.force_units(units)
|
|
273
|
+
|
|
274
|
+
def force_units(self, units: str):
|
|
275
|
+
""" Change immediately the units to whatever else. Use this with care!"""
|
|
244
276
|
units = check_units(units)
|
|
245
277
|
self._units = units
|
|
246
278
|
|
|
279
|
+
def units_as(self, units: str, inplace=True, context: str = None, **context_kwargs):
|
|
280
|
+
if inplace:
|
|
281
|
+
#self._units = units
|
|
282
|
+
if context is None:
|
|
283
|
+
self.data = self.get_quantity().to(units).magnitude
|
|
284
|
+
else:
|
|
285
|
+
self.data = self.get_quantity().to(units, context, **context_kwargs).magnitude
|
|
286
|
+
self.force_units(units)
|
|
287
|
+
return
|
|
288
|
+
else:
|
|
289
|
+
if context is not None:
|
|
290
|
+
return Axis(self.label, units,
|
|
291
|
+
data=self.get_quantity().to(units, context, **context_kwargs).magnitude)
|
|
292
|
+
else:
|
|
293
|
+
return Axis(self.label, units,
|
|
294
|
+
data=self.get_quantity().to(units))
|
|
295
|
+
|
|
296
|
+
def to_reduced_units(self, inplace=False):
|
|
297
|
+
quantity = self.get_quantity().to_reduced_units()
|
|
298
|
+
if inplace:
|
|
299
|
+
self.data = quantity.magnitude
|
|
300
|
+
self.force_units(str(quantity.units))
|
|
301
|
+
else:
|
|
302
|
+
return Axis(self.label, units=str(quantity.units),
|
|
303
|
+
data=quantity.magnitude)
|
|
304
|
+
|
|
305
|
+
def to_base_units(self, inplace=False):
|
|
306
|
+
quantity = self.get_quantity().to_base_units()
|
|
307
|
+
if inplace:
|
|
308
|
+
self.data = quantity.magnitude
|
|
309
|
+
self.force_units(str(quantity.units))
|
|
310
|
+
else:
|
|
311
|
+
return Axis(self.label, units=str(quantity.units),
|
|
312
|
+
data=quantity.magnitude)
|
|
313
|
+
|
|
247
314
|
@property
|
|
248
315
|
def index(self) -> int:
|
|
249
316
|
"""int: get/set the index this axis corresponds to in a DataWithAxis object"""
|
|
@@ -260,7 +327,7 @@ class Axis:
|
|
|
260
327
|
return self._data
|
|
261
328
|
|
|
262
329
|
@data.setter
|
|
263
|
-
def data(self, data: np.ndarray):
|
|
330
|
+
def data(self, data: Union[np.ndarray, Q_]):
|
|
264
331
|
if data is not None:
|
|
265
332
|
self._check_data_valid(data)
|
|
266
333
|
self.get_scale_offset_from_data(data)
|
|
@@ -273,6 +340,10 @@ class Axis:
|
|
|
273
340
|
"""Convenience method to obtain the axis data (usually None because scaling and offset are used)"""
|
|
274
341
|
return self._data if self._data is not None else self._linear_data(self.size)
|
|
275
342
|
|
|
343
|
+
def get_quantity(self) -> Q_:
|
|
344
|
+
""" Convenience method to obtain the numerical data as a quantity array"""
|
|
345
|
+
return Q_(self.get_data(), self.units)
|
|
346
|
+
|
|
276
347
|
def get_data_at(self, indexes: Union[int, IterableType, slice]) -> np.ndarray:
|
|
277
348
|
""" Get data at specified indexes
|
|
278
349
|
|
|
@@ -346,8 +417,10 @@ class Axis:
|
|
|
346
417
|
elif index < 0:
|
|
347
418
|
raise ValueError('index for the Axis class should be a positive integer')
|
|
348
419
|
|
|
349
|
-
|
|
350
|
-
|
|
420
|
+
def _check_data_valid(self, data: Union[np.ndarray, Q_]):
|
|
421
|
+
if isinstance(data, Q_):
|
|
422
|
+
self.units = str(data.units)
|
|
423
|
+
data = data.magnitude
|
|
351
424
|
if not isinstance(data, np.ndarray):
|
|
352
425
|
raise TypeError(f'data for the Axis class should be a 1D numpy array')
|
|
353
426
|
elif len(data.shape) != 1:
|
|
@@ -368,12 +441,14 @@ class Axis:
|
|
|
368
441
|
def __len__(self):
|
|
369
442
|
return self.size
|
|
370
443
|
|
|
371
|
-
def _compute_slices(self,
|
|
372
|
-
|
|
444
|
+
def _compute_slices(self, _slice, *ignored, is_index=True, **ignored_also):
|
|
445
|
+
_slice = _compute_slices_from_axis(self, _slice, is_index=is_index)
|
|
446
|
+
return _slice, _slice
|
|
373
447
|
|
|
374
|
-
def _slicer(self, _slice, *ignored, **ignored_also):
|
|
448
|
+
def _slicer(self, _slice, *ignored, is_index=True, **ignored_also):
|
|
375
449
|
ax: Axis = copy.deepcopy(self)
|
|
376
|
-
|
|
450
|
+
_slice, _slice = self._compute_slices(_slice, is_index=is_index)
|
|
451
|
+
if isinstance(_slice, numbers.Number):
|
|
377
452
|
ax.data = np.array([ax.get_data()[_slice]])
|
|
378
453
|
return ax
|
|
379
454
|
elif _slice is Ellipsis:
|
|
@@ -442,6 +517,11 @@ class Axis:
|
|
|
442
517
|
else:
|
|
443
518
|
return self.offset + self.size / 2 * self.scaling
|
|
444
519
|
|
|
520
|
+
def flip(self):
|
|
521
|
+
""" flip the direction of the axis"""
|
|
522
|
+
self.data = self.get_data()[::-1]
|
|
523
|
+
|
|
524
|
+
|
|
445
525
|
def min(self):
|
|
446
526
|
if self._data is not None:
|
|
447
527
|
return np.min(self._data)
|
|
@@ -533,8 +613,9 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
533
613
|
distribution: DataDistribution or str
|
|
534
614
|
The distribution type of the data: uniform if distributed on a regular grid or spread if on
|
|
535
615
|
specific unordered points
|
|
536
|
-
data: list of ndarray
|
|
537
|
-
The data the object is storing
|
|
616
|
+
data: list of ndarray or Quantities
|
|
617
|
+
The data the object is storing. In case of Quantities, the object units attribute will
|
|
618
|
+
be forced to the unit of this quantity, ignoring the units argument.
|
|
538
619
|
labels: list of str
|
|
539
620
|
The labels of the data nd-arrays
|
|
540
621
|
origin: str
|
|
@@ -601,6 +682,13 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
601
682
|
|
|
602
683
|
base_type = 'Data'
|
|
603
684
|
|
|
685
|
+
def __new__(cls, *args, **kwargs):
|
|
686
|
+
if not pint.compat.is_upcast_type(cls):
|
|
687
|
+
upcast_type_map.update({f'{cls.__module__}.{cls.__name__}': None})
|
|
688
|
+
# this will make sure, these
|
|
689
|
+
# objects are not wrapped by pint
|
|
690
|
+
return super().__new__(cls)
|
|
691
|
+
|
|
604
692
|
def __init__(self, name: str,
|
|
605
693
|
source: DataSource = None, dim: DataDim = None,
|
|
606
694
|
distribution: DataDistribution = DataDistribution.uniform,
|
|
@@ -642,7 +730,8 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
642
730
|
units = check_units(units)
|
|
643
731
|
self.units_as(units, inplace=True)
|
|
644
732
|
|
|
645
|
-
def units_as(self, units: str, inplace=True
|
|
733
|
+
def units_as(self, units: str, inplace=True, context: str = None,
|
|
734
|
+
**context_kwargs) -> 'DataBase':
|
|
646
735
|
""" Set the object units to the new one (if possible)
|
|
647
736
|
|
|
648
737
|
Parameters
|
|
@@ -653,11 +742,18 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
653
742
|
default True.
|
|
654
743
|
If True replace the data's arrays by array in the new units
|
|
655
744
|
If False, return a new data object
|
|
745
|
+
context: str
|
|
746
|
+
See pint documentation
|
|
656
747
|
"""
|
|
657
748
|
arrays = []
|
|
658
749
|
try:
|
|
659
750
|
for ind_array in range(len(self)):
|
|
660
|
-
|
|
751
|
+
if context is None:
|
|
752
|
+
arrays.append(
|
|
753
|
+
self.quantities[ind_array].to(units).magnitude)
|
|
754
|
+
else:
|
|
755
|
+
arrays.append(
|
|
756
|
+
self.quantities[ind_array].to(units, context, **context_kwargs).magnitude)
|
|
661
757
|
|
|
662
758
|
except pint.errors.DimensionalityError as e:
|
|
663
759
|
raise DataUnitError(
|
|
@@ -788,6 +884,7 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
788
884
|
units = dwa.units
|
|
789
885
|
ufunc_results = [ufunc(*zipped, **kwargs) for zipped in list(zip(*elts))]
|
|
790
886
|
if isinstance(ufunc_results[0], Q_):
|
|
887
|
+
ufunc_results = [ufunc_result.to_reduced_units() for ufunc_result in ufunc_results]
|
|
791
888
|
units = str(ufunc_results[0].units)
|
|
792
889
|
ufunc_results = [ufunc_result.magnitude for ufunc_result in ufunc_results]
|
|
793
890
|
dwa.data = ufunc_results
|
|
@@ -881,7 +978,13 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
881
978
|
|
|
882
979
|
def angle(self):
|
|
883
980
|
""" Take the phase value of itself"""
|
|
884
|
-
|
|
981
|
+
dwa_angle = np.angle(self)
|
|
982
|
+
dwa_angle.force_units('rad')
|
|
983
|
+
return dwa_angle
|
|
984
|
+
|
|
985
|
+
def unwrap(self):
|
|
986
|
+
""" unwrap the underlying array (should be angles otherwise meaningless)"""
|
|
987
|
+
return np.unwrap(self)
|
|
885
988
|
|
|
886
989
|
def real(self):
|
|
887
990
|
""" Take the real part of itself"""
|
|
@@ -1002,15 +1105,17 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
1002
1105
|
"""Get the data by its index in the list, same as self[index]"""
|
|
1003
1106
|
return self.data[index]
|
|
1004
1107
|
|
|
1005
|
-
|
|
1006
|
-
def _check_data_type(data: List[np.ndarray]) -> List[np.ndarray]:
|
|
1108
|
+
def _check_data_type(self, data: List[Union[np.ndarray, Q_]]) -> List[np.ndarray]:
|
|
1007
1109
|
"""make sure data is a list of nd-arrays"""
|
|
1008
1110
|
is_valid = True
|
|
1009
1111
|
if data is None:
|
|
1010
1112
|
is_valid = False
|
|
1011
1113
|
if not isinstance(data, list):
|
|
1012
1114
|
# try to transform the data to regular type
|
|
1013
|
-
if isinstance(data,
|
|
1115
|
+
if isinstance(data, Q_):
|
|
1116
|
+
self.force_units(str(data.units))
|
|
1117
|
+
data = [data.magnitude]
|
|
1118
|
+
elif isinstance(data, np.ndarray):
|
|
1014
1119
|
warnings.warn(DataTypeWarning(f'Your data should be a list of numpy arrays not just a single numpy'
|
|
1015
1120
|
f' array, wrapping them with a list'))
|
|
1016
1121
|
data = [data]
|
|
@@ -1023,12 +1128,16 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
1023
1128
|
if isinstance(data, list):
|
|
1024
1129
|
if len(data) == 0:
|
|
1025
1130
|
is_valid = False
|
|
1026
|
-
elif not isinstance(data[0], np.ndarray)
|
|
1131
|
+
elif not (isinstance(data[0], np.ndarray) or
|
|
1132
|
+
isinstance(data[0], Q_)):
|
|
1027
1133
|
is_valid = False
|
|
1028
1134
|
elif len(data[0].shape) == 0:
|
|
1029
1135
|
is_valid = False
|
|
1030
1136
|
if not is_valid:
|
|
1031
1137
|
raise TypeError(f'Data should be an non-empty list of non-empty numpy arrays')
|
|
1138
|
+
if isinstance(data[0], Q_):
|
|
1139
|
+
self.force_units(str(data[0].units))
|
|
1140
|
+
data = [array.magnitude for array in data]
|
|
1032
1141
|
return data
|
|
1033
1142
|
|
|
1034
1143
|
def check_shape_from_data(self, data: List[np.ndarray]):
|
|
@@ -1093,7 +1202,7 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
1093
1202
|
return self._data
|
|
1094
1203
|
|
|
1095
1204
|
@data.setter
|
|
1096
|
-
def data(self, data: List[np.ndarray]):
|
|
1205
|
+
def data(self, data: List[Union[np.ndarray, Q_]]):
|
|
1097
1206
|
data = self._check_data_type(data)
|
|
1098
1207
|
self._check_shape_dim_consistency(data)
|
|
1099
1208
|
self._check_same_shape(data)
|
|
@@ -1704,8 +1813,11 @@ class DataWithAxes(DataBase):
|
|
|
1704
1813
|
|
|
1705
1814
|
self.set_axes_manager(self.shape, axes=axes, nav_indexes=nav_indexes, **other_kwargs)
|
|
1706
1815
|
|
|
1707
|
-
self.inav
|
|
1708
|
-
self.isig
|
|
1816
|
+
self.inav = SpecialSlicersData(self, True)
|
|
1817
|
+
self.isig = SpecialSlicersData(self, False)
|
|
1818
|
+
|
|
1819
|
+
self.vnav = SpecialSlicersData(self, True, is_index=False)
|
|
1820
|
+
self.vsig = SpecialSlicersData(self, False, is_index=False)
|
|
1709
1821
|
|
|
1710
1822
|
self.get_dim_from_data_axes() # in DataBase, dim is processed from the shape of data, but if axes are provided
|
|
1711
1823
|
#then use get_dim_from axes
|
|
@@ -1891,7 +2003,32 @@ class DataWithAxes(DataBase):
|
|
|
1891
2003
|
mean = np.array([mean])
|
|
1892
2004
|
dat_mean.append(mean)
|
|
1893
2005
|
return self.deepcopy_with_new_data(dat_mean, remove_axes_index=axis)
|
|
1894
|
-
|
|
2006
|
+
|
|
2007
|
+
def moment(self) -> Tuple[DataWithAxes, DataWithAxes]:
|
|
2008
|
+
""" returns the two first moments of the data over the axis
|
|
2009
|
+
|
|
2010
|
+
only valid for Data1D data
|
|
2011
|
+
|
|
2012
|
+
Returns
|
|
2013
|
+
-------
|
|
2014
|
+
DataCalculated: containing the moment of order 0 (mean)
|
|
2015
|
+
DataCalculated: containing the moment of order 1 (std)
|
|
2016
|
+
"""
|
|
2017
|
+
if self.dim != DataDim.Data1D:
|
|
2018
|
+
raise DataDimError('the moment method is only valid for 1D data')
|
|
2019
|
+
arrays_mean = []
|
|
2020
|
+
arrays_std = []
|
|
2021
|
+
for data in self:
|
|
2022
|
+
mean, std = mutils.my_moment(self.axes[0].get_data(), data)
|
|
2023
|
+
arrays_mean.append(np.array([mean]))
|
|
2024
|
+
arrays_std.append(np.array([std]))
|
|
2025
|
+
|
|
2026
|
+
return (DataCalculated('mean', data=arrays_mean),
|
|
2027
|
+
DataCalculated('std', data=arrays_std))
|
|
2028
|
+
|
|
2029
|
+
|
|
2030
|
+
|
|
2031
|
+
|
|
1895
2032
|
def sum(self, axis: int = 0) -> DataWithAxes:
|
|
1896
2033
|
"""Process the sum of the data on the specified axis and returns the new data
|
|
1897
2034
|
|
|
@@ -1905,7 +2042,7 @@ class DataWithAxes(DataBase):
|
|
|
1905
2042
|
"""
|
|
1906
2043
|
dat_sum = []
|
|
1907
2044
|
for dat in self.data:
|
|
1908
|
-
dat_sum.append(np.sum(dat, axis=axis))
|
|
2045
|
+
dat_sum.append(np.atleast_1d(np.sum(dat, axis=axis)))
|
|
1909
2046
|
return self.deepcopy_with_new_data(dat_sum, remove_axes_index=axis)
|
|
1910
2047
|
|
|
1911
2048
|
def interp(self, new_axis_data: Union[Axis, np.ndarray], **kwargs) -> DataWithAxes:
|
|
@@ -1945,12 +2082,20 @@ class DataWithAxes(DataBase):
|
|
|
1945
2082
|
labels=self.labels)
|
|
1946
2083
|
return new_data
|
|
1947
2084
|
|
|
1948
|
-
def ft(self, axis: int = 0
|
|
2085
|
+
def ft(self, axis: int = 0, axis_label: str = None,
|
|
2086
|
+
axis_units: str = None, labels: List[str] = None) -> DataWithAxes:
|
|
1949
2087
|
"""Process the Fourier Transform of the data on the specified axis and returns the new data
|
|
1950
2088
|
|
|
1951
2089
|
Parameters
|
|
1952
2090
|
----------
|
|
1953
2091
|
axis: int
|
|
2092
|
+
Apply the FT on this axis index
|
|
2093
|
+
axis_label: str
|
|
2094
|
+
A new label for the FT computed axis
|
|
2095
|
+
axis_units: str
|
|
2096
|
+
New units (without conversion on top of the one from the FT) for the computed axis
|
|
2097
|
+
labels: List[str]
|
|
2098
|
+
list of string for new labels
|
|
1954
2099
|
|
|
1955
2100
|
Returns
|
|
1956
2101
|
-------
|
|
@@ -1967,19 +2112,34 @@ class DataWithAxes(DataBase):
|
|
|
1967
2112
|
for dat in self.data:
|
|
1968
2113
|
dat_ft.append(mutils.ft(dat, dim=axis))
|
|
1969
2114
|
new_data = self.deepcopy_with_new_data(dat_ft)
|
|
2115
|
+
if labels is not None:
|
|
2116
|
+
new_data.labels = labels
|
|
1970
2117
|
axis_obj = new_data.get_axis_from_index(axis)[0]
|
|
1971
2118
|
axis_obj.data = omega_grid
|
|
1972
|
-
|
|
1973
|
-
|
|
2119
|
+
if axis_label is not None:
|
|
2120
|
+
axis_obj.label = axis_label
|
|
2121
|
+
else:
|
|
2122
|
+
axis_obj.label = f'ft({axis_obj.label})'
|
|
2123
|
+
if axis_units is not None:
|
|
2124
|
+
axis_obj.force_units(axis_units)
|
|
2125
|
+
else:
|
|
2126
|
+
axis_obj.units = f'rad/{axis_obj.units}'
|
|
1974
2127
|
return new_data
|
|
1975
2128
|
|
|
1976
|
-
def ift(self, axis: int = 0
|
|
2129
|
+
def ift(self, axis: int = 0, axis_label: str = None,
|
|
2130
|
+
axis_units: str = None, labels: List[str] = None) -> DataWithAxes:
|
|
1977
2131
|
"""Process the inverse Fourier Transform of the data on the specified axis and returns the
|
|
1978
2132
|
new data
|
|
1979
2133
|
|
|
1980
2134
|
Parameters
|
|
1981
2135
|
----------
|
|
1982
2136
|
axis: int
|
|
2137
|
+
axis_label: str
|
|
2138
|
+
A new label for the FT computed axis
|
|
2139
|
+
axis_units: str
|
|
2140
|
+
New units (without conversion on top of the one from the FT) for the computed axis
|
|
2141
|
+
labels: List[str]
|
|
2142
|
+
list of string for new labels
|
|
1983
2143
|
|
|
1984
2144
|
Returns
|
|
1985
2145
|
-------
|
|
@@ -1996,10 +2156,18 @@ class DataWithAxes(DataBase):
|
|
|
1996
2156
|
for dat in self.data:
|
|
1997
2157
|
dat_ift.append(mutils.ift(dat, dim=axis))
|
|
1998
2158
|
new_data = self.deepcopy_with_new_data(dat_ift)
|
|
2159
|
+
if labels is not None:
|
|
2160
|
+
new_data.labels = labels
|
|
1999
2161
|
axis_obj = new_data.get_axis_from_index(axis)[0]
|
|
2000
2162
|
axis_obj.data = omega_grid
|
|
2001
|
-
|
|
2002
|
-
|
|
2163
|
+
if axis_label is not None:
|
|
2164
|
+
axis_obj.label = axis_label
|
|
2165
|
+
else:
|
|
2166
|
+
axis_obj.label = f'ift({axis_obj.label})'
|
|
2167
|
+
if axis_units is not None:
|
|
2168
|
+
axis_obj.force_units(axis_units)
|
|
2169
|
+
else:
|
|
2170
|
+
axis_obj.units = str(Unit(f'rad/({axis_obj.units})'))
|
|
2003
2171
|
return new_data
|
|
2004
2172
|
|
|
2005
2173
|
def fit(self, function: Callable, initial_guess: IterableType, data_index: int = None,
|
|
@@ -2199,11 +2367,22 @@ class DataWithAxes(DataBase):
|
|
|
2199
2367
|
axes.append(ax)
|
|
2200
2368
|
self.axes = axes
|
|
2201
2369
|
|
|
2202
|
-
def _compute_slices(self, slices, is_navigation=True):
|
|
2370
|
+
def _compute_slices(self, slices, is_navigation=True, is_index=True):
|
|
2203
2371
|
"""Compute the total slice to apply to the data
|
|
2204
2372
|
|
|
2205
2373
|
Filling in Ellipsis when no slicing should be done
|
|
2374
|
+
Parameters
|
|
2375
|
+
----------
|
|
2376
|
+
slices: List of slice
|
|
2377
|
+
is_navigation: bool
|
|
2378
|
+
is_index: bool
|
|
2379
|
+
if False, the slice are on the values of the underlying axes
|
|
2380
|
+
Returns
|
|
2381
|
+
-------
|
|
2382
|
+
list(slice): the computed slices as index (eventually for all axes)
|
|
2383
|
+
list(slice): a version as index of the input argument
|
|
2206
2384
|
"""
|
|
2385
|
+
_slices_as_index = []
|
|
2207
2386
|
if isinstance(slices, numbers.Number) or isinstance(slices, slice):
|
|
2208
2387
|
slices = [slices]
|
|
2209
2388
|
if is_navigation:
|
|
@@ -2214,13 +2393,18 @@ class DataWithAxes(DataBase):
|
|
|
2214
2393
|
slices = list(slices)
|
|
2215
2394
|
for ind in range(len(self.shape)):
|
|
2216
2395
|
if ind in indexes:
|
|
2217
|
-
|
|
2396
|
+
_slice = slices.pop(0)
|
|
2397
|
+
if not is_index:
|
|
2398
|
+
axis = self.get_axis_from_index(ind)[0]
|
|
2399
|
+
_slice = _compute_slices_from_axis(axis, _slice, is_index=is_index)
|
|
2400
|
+
_slices_as_index.append(_slice)
|
|
2401
|
+
total_slices.append(_slice)
|
|
2218
2402
|
elif len(total_slices) == 0:
|
|
2219
2403
|
total_slices.append(Ellipsis)
|
|
2220
2404
|
elif not (Ellipsis in total_slices and total_slices[-1] is Ellipsis):
|
|
2221
2405
|
total_slices.append(slice(None))
|
|
2222
2406
|
total_slices = tuple(total_slices)
|
|
2223
|
-
return total_slices
|
|
2407
|
+
return total_slices, _slices_as_index
|
|
2224
2408
|
|
|
2225
2409
|
def check_squeeze(self, total_slices: List[slice], is_navigation: bool):
|
|
2226
2410
|
|
|
@@ -2232,7 +2416,7 @@ class DataWithAxes(DataBase):
|
|
|
2232
2416
|
do_squeeze = False
|
|
2233
2417
|
return do_squeeze
|
|
2234
2418
|
|
|
2235
|
-
def _slicer(self, slices, is_navigation=True):
|
|
2419
|
+
def _slicer(self, slices, is_navigation=True, is_index=True):
|
|
2236
2420
|
"""Apply a given slice to the data either navigation or signal dimension
|
|
2237
2421
|
|
|
2238
2422
|
Parameters
|
|
@@ -2241,6 +2425,8 @@ class DataWithAxes(DataBase):
|
|
|
2241
2425
|
the slices to apply to the data
|
|
2242
2426
|
is_navigation: bool
|
|
2243
2427
|
if True apply the slices to the navigation dimension else to the signal ones
|
|
2428
|
+
is_index: bool
|
|
2429
|
+
if True the slices are indexes otherwise the slices are axes values to be indexed first
|
|
2244
2430
|
|
|
2245
2431
|
Returns
|
|
2246
2432
|
-------
|
|
@@ -2248,10 +2434,10 @@ class DataWithAxes(DataBase):
|
|
|
2248
2434
|
Object of the same type as the initial data, derived from DataWithAxes. But with lower
|
|
2249
2435
|
data size due to the slicing and with eventually less axes.
|
|
2250
2436
|
"""
|
|
2251
|
-
|
|
2252
2437
|
if isinstance(slices, numbers.Number) or isinstance(slices, slice):
|
|
2253
2438
|
slices = [slices]
|
|
2254
|
-
|
|
2439
|
+
|
|
2440
|
+
total_slices, slices = self._compute_slices(slices, is_navigation, is_index=is_index)
|
|
2255
2441
|
|
|
2256
2442
|
do_squeeze = self.check_squeeze(total_slices, is_navigation)
|
|
2257
2443
|
new_arrays_data = [squeeze(dat[total_slices], do_squeeze) for dat in self.data]
|
|
@@ -737,7 +737,7 @@ class DataToExportSaver:
|
|
|
737
737
|
def __init__(self, h5saver: Union[H5SaverLowLevel, Path, str]):
|
|
738
738
|
if isinstance(h5saver, Path) or isinstance(h5saver, str):
|
|
739
739
|
h5saver_tmp = H5SaverLowLevel()
|
|
740
|
-
h5saver_tmp.init_file(
|
|
740
|
+
h5saver_tmp.init_file(file_name=Path(h5saver))
|
|
741
741
|
h5saver = h5saver_tmp
|
|
742
742
|
|
|
743
743
|
self._h5saver = h5saver
|
|
@@ -748,7 +748,7 @@ class DataToExportSaver:
|
|
|
748
748
|
return self._h5saver.get_node(where)
|
|
749
749
|
|
|
750
750
|
def close(self):
|
|
751
|
-
self.
|
|
751
|
+
self.close_file()
|
|
752
752
|
|
|
753
753
|
def close_file(self):
|
|
754
754
|
self._h5saver.close_file()
|
|
@@ -84,11 +84,17 @@ def _min(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] =
|
|
|
84
84
|
def _std(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] = None, **kwargs):
|
|
85
85
|
return process_with_reduced_dimensions(np.std, dwa, *args, axis=axis, **kwargs)
|
|
86
86
|
|
|
87
|
+
|
|
87
88
|
@implements("mean")
|
|
88
89
|
def _mean(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] = None, **kwargs):
|
|
89
90
|
return process_with_reduced_dimensions(np.mean, dwa, *args, axis=axis, **kwargs)
|
|
90
91
|
|
|
91
92
|
|
|
93
|
+
@implements("sum")
|
|
94
|
+
def _sum(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] = None, **kwargs):
|
|
95
|
+
return process_with_reduced_dimensions(np.sum, dwa, *args, axis=axis, **kwargs)
|
|
96
|
+
|
|
97
|
+
|
|
92
98
|
# ************* FUNCTIONS that apply with units ********
|
|
93
99
|
|
|
94
100
|
@implements('angle')
|
|
@@ -100,6 +106,14 @@ def _angle(dwa: 'DataWithAxes', *args, **kwargs):
|
|
|
100
106
|
return dwa_func
|
|
101
107
|
|
|
102
108
|
|
|
109
|
+
@implements('unwrap')
|
|
110
|
+
def _unwrap(dwa: 'DataWithAxes', *args, **kwargs):
|
|
111
|
+
dwa_func = dwa.deepcopy_with_new_data(
|
|
112
|
+
data=[np.unwrap(array, *args, **kwargs) for array in dwa.data])
|
|
113
|
+
dwa_func.name += f"_{'unwrap'}"
|
|
114
|
+
return dwa_func
|
|
115
|
+
|
|
116
|
+
|
|
103
117
|
@implements('real')
|
|
104
118
|
def _real(dwa: 'DataWithAxes', *args, **kwargs):
|
|
105
119
|
|
|
@@ -13,12 +13,15 @@ if TYPE_CHECKING:
|
|
|
13
13
|
|
|
14
14
|
class SpecialSlicers(object):
|
|
15
15
|
"""make it elegant to apply a slice to navigation or signal dimensions"""
|
|
16
|
-
def __init__(self, obj: Union['DataWithAxes', 'Axis'], is_navigation):
|
|
16
|
+
def __init__(self, obj: Union['DataWithAxes', 'Axis'], is_navigation, is_index=True):
|
|
17
17
|
self.is_navigation = is_navigation
|
|
18
|
+
self.is_index = is_index
|
|
18
19
|
self.obj = obj
|
|
19
20
|
|
|
20
21
|
def __getitem__(self, slices) -> Union['DataWithAxes', 'Axis']:
|
|
21
|
-
|
|
22
|
+
if hasattr(self.obj, 'axes') and self.obj.distribution.name == 'spread' and not self.is_index:
|
|
23
|
+
raise NotImplementedError('Impossible to slice by value a spread data object')
|
|
24
|
+
return self.obj._slicer(slices, self.is_navigation, is_index=self.is_index)
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class SpecialSlicersData(SpecialSlicers):
|
|
@@ -26,7 +29,7 @@ class SpecialSlicersData(SpecialSlicers):
|
|
|
26
29
|
def __setitem__(self, slices, data: Union[np.ndarray, 'DataWithAxes', 'Axis']):
|
|
27
30
|
"""x.__setitem__(slices, data) <==> x[slices]=data
|
|
28
31
|
"""
|
|
29
|
-
|
|
32
|
+
total_slices, slices_index = self.obj._compute_slices(slices, self.is_navigation, is_index=self.is_index)
|
|
30
33
|
|
|
31
34
|
if hasattr(self.obj, 'base_type') and self.obj.base_type == 'Axis':
|
|
32
35
|
if isinstance(data, np.ndarray):
|
|
@@ -35,14 +38,14 @@ class SpecialSlicersData(SpecialSlicers):
|
|
|
35
38
|
data_to_replace = data.get_data()
|
|
36
39
|
if hasattr(self.obj, 'units') and self.obj.data is None:
|
|
37
40
|
self.obj.create_linear_data(len(self.obj))
|
|
38
|
-
self.obj.data[
|
|
41
|
+
self.obj.data[total_slices] = data_to_replace
|
|
39
42
|
else:
|
|
40
43
|
for ind in range(len(self.obj)):
|
|
41
44
|
if isinstance(data, np.ndarray):
|
|
42
45
|
data_to_replace = data
|
|
43
46
|
else: # means it's a DataWithAxes
|
|
44
47
|
data_to_replace = data[ind]
|
|
45
|
-
self.obj[ind][
|
|
48
|
+
self.obj[ind][total_slices] = data_to_replace
|
|
46
49
|
|
|
47
50
|
def __len__(self):
|
|
48
51
|
return self.obj.axes_manager.sig_shape[0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/plotting/plotter/plotters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_data-5.0.4 → pymodaq_data-5.0.5}/src/pymodaq_data/post_treatment/process_to_scalar.py
RENAMED
|
File without changes
|
|
File without changes
|