From bae6bc94b7ddbed9a38b5fb5b735dfefcb6f4ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20K=C3=BCderle?= Date: Fri, 19 Apr 2024 10:12:32 +0200 Subject: [PATCH 1/4] Implemented "rotated_data_" object for all orientation methods --- gaitmap/base.py | 9 + .../orientation_methods/_madgwick.py | 6 +- .../_simple_gyro_integration.py | 8 +- ..._regression_MadgwickAHRS_rotated_data.json | 1975 +++++++++++++++++ .../test_ori_method_mixin.py | 1 + 5 files changed, 1991 insertions(+), 8 deletions(-) create mode 100644 tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_MadgwickAHRS_rotated_data.json diff --git a/gaitmap/base.py b/gaitmap/base.py index a16f158c..886b4acf 100644 --- a/gaitmap/base.py +++ b/gaitmap/base.py @@ -22,6 +22,7 @@ StrideList, VelocityList, ) +from gaitmap.utils.rotations import rotate_dataset_series BaseType = TypeVar("BaseType", bound="_BaseSerializable") # pylint: disable=invalid-name @@ -229,6 +230,9 @@ class BaseOrientationMethod(BaseAlgorithm): _action_methods = ("estimate",) orientation_object_: Rotation + data: SingleSensorData + sampling_rate_hz: float + @property def orientation_(self) -> SingleSensorOrientationList: """Orientations as pd.DataFrame.""" @@ -236,6 +240,11 @@ def orientation_(self) -> SingleSensorOrientationList: df.index.name = "sample" return df + @property + def rotated_data_(self) -> SingleSensorData: + """Rotated data.""" + return rotate_dataset_series(self.data, self.orientation_object_[:-1]) + def estimate( self, data: SingleSensorData, diff --git a/gaitmap/trajectory_reconstruction/orientation_methods/_madgwick.py b/gaitmap/trajectory_reconstruction/orientation_methods/_madgwick.py index 9b19c97f..dde22de2 100644 --- a/gaitmap/trajectory_reconstruction/orientation_methods/_madgwick.py +++ b/gaitmap/trajectory_reconstruction/orientation_methods/_madgwick.py @@ -49,6 +49,9 @@ class MadgwickAHRS(BaseOrientationMethod): This means the there are len(data) + 1 orientations. orientation_object_ The orientations as a single scipy Rotation object + rotated_data_ + The rotated data after applying the estimated orientation to the data. + The first sample of the data remain unrotated (initial orientation). Other Parameters ---------------- @@ -96,9 +99,6 @@ class MadgwickAHRS(BaseOrientationMethod): beta: float memory: Optional[Memory] - data: SingleSensorData - sampling_rate_hz: float - def __init__( self, beta: float = 0.2, diff --git a/gaitmap/trajectory_reconstruction/orientation_methods/_simple_gyro_integration.py b/gaitmap/trajectory_reconstruction/orientation_methods/_simple_gyro_integration.py index 5d71c77a..5862be80 100644 --- a/gaitmap/trajectory_reconstruction/orientation_methods/_simple_gyro_integration.py +++ b/gaitmap/trajectory_reconstruction/orientation_methods/_simple_gyro_integration.py @@ -33,6 +33,9 @@ class SimpleGyroIntegration(BaseOrientationMethod): This means the there are len(data) + 1 orientations. orientation_object_ The orientations as a single scipy Rotation object + rotated_data_ + The rotated data after applying the estimated orientation to the data. + The first sample of the data remain unrotated (initial orientation). Other Parameters ---------------- @@ -74,11 +77,6 @@ class SimpleGyroIntegration(BaseOrientationMethod): initial_orientation: Union[np.ndarray, Rotation] memory: Optional[Memory] - orientation_: Rotation - - data: SingleSensorData - sampling_rate_hz: float - def __init__( self, initial_orientation: Union[np.ndarray, Rotation] = cf(np.array([0, 0, 0, 1.0])), diff --git a/tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_MadgwickAHRS_rotated_data.json b/tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_MadgwickAHRS_rotated_data.json new file mode 100644 index 00000000..c43cbf6c --- /dev/null +++ b/tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_MadgwickAHRS_rotated_data.json @@ -0,0 +1,1975 @@ +{ + "schema":{ + "fields":[ + { + "name":"index", + "type":"number" + }, + { + "name":"acc_x", + "type":"number" + }, + { + "name":"acc_y", + "type":"number" + }, + { + "name":"acc_z", + "type":"number" + }, + { + "name":"gyr_x", + "type":"number" + }, + { + "name":"gyr_y", + "type":"number" + }, + { + "name":"gyr_z", + "type":"number" + } + ], + "primaryKey":[ + "index" + ], + "pandas_version":"1.4.0" + }, + "data":[ + { + "index":2.412109375, + "acc_x":0.8376466171, + "acc_y":2.2936597178, + "acc_z":9.6547557343, + "gyr_x":1.5850653072, + "gyr_y":4.5470933517, + "gyr_z":-7.7914702018 + }, + { + "index":2.4169921875, + "acc_x":0.7976569534, + "acc_y":2.2955652422, + "acc_z":9.7358701513, + "gyr_x":3.053994784, + "gyr_y":4.85358799, + "gyr_z":-7.7074762789 + }, + { + "index":2.421875, + "acc_x":0.8101332572, + "acc_y":2.2107640599, + "acc_z":9.8164154351, + "gyr_x":4.9464067871, + "gyr_y":4.7258988784, + "gyr_z":-7.4459467095 + }, + { + "index":2.4267578125, + "acc_x":0.7803232262, + "acc_y":2.1749959891, + "acc_z":9.7969765348, + "gyr_x":7.2150162266, + "gyr_y":5.1769731493, + "gyr_z":-8.609630614 + }, + { + "index":2.431640625, + "acc_x":0.8401339762, + "acc_y":2.3207479864, + "acc_z":9.6446248268, + "gyr_x":9.1720410691, + "gyr_y":4.9469266672, + "gyr_z":-9.1268866737 + }, + { + "index":2.4365234375, + "acc_x":0.8426221508, + "acc_y":2.5063812223, + "acc_z":9.5463561029, + "gyr_x":9.9719198676, + "gyr_y":4.731058538, + "gyr_z":-9.7119985146 + }, + { + "index":2.44140625, + "acc_x":0.7761823801, + "acc_y":2.4464694728, + "acc_z":9.4651435707, + "gyr_x":10.1603545262, + "gyr_y":4.570810713, + "gyr_z":-9.8808911594 + }, + { + "index":2.4462890625, + "acc_x":0.67784447, + "acc_y":2.5188466667, + "acc_z":9.3921503659, + "gyr_x":9.61429236, + "gyr_y":4.7495190998, + "gyr_z":-9.0850571827 + }, + { + "index":2.451171875, + "acc_x":0.7340799747, + "acc_y":2.4874467162, + "acc_z":9.6007428712, + "gyr_x":9.0048979073, + "gyr_y":4.6877162849, + "gyr_z":-8.4180852866 + }, + { + "index":2.4560546875, + "acc_x":0.7739055453, + "acc_y":2.418174771, + "acc_z":9.4292636937, + "gyr_x":8.3382300011, + "gyr_y":4.5933882674, + "gyr_z":-8.474146608 + }, + { + "index":2.4609375, + "acc_x":0.7838019498, + "acc_y":2.4707581267, + "acc_z":9.4866982428, + "gyr_x":7.9188425808, + "gyr_y":4.9107925736, + "gyr_z":-8.2722248124 + }, + { + "index":2.4658203125, + "acc_x":0.7641328807, + "acc_y":2.4212817745, + "acc_z":9.5204379007, + "gyr_x":7.186299253, + "gyr_y":4.6715496154, + "gyr_z":-7.7948470925 + }, + { + "index":2.470703125, + "acc_x":0.7084633036, + "acc_y":2.4467151481, + "acc_z":9.4176756686, + "gyr_x":6.5803941472, + "gyr_y":4.7443302045, + "gyr_z":-7.5445731484 + }, + { + "index":2.4755859375, + "acc_x":0.672182007, + "acc_y":2.467265288, + "acc_z":9.329359623, + "gyr_x":6.0836403492, + "gyr_y":4.1218663152, + "gyr_z":-6.8451290446 + }, + { + "index":2.48046875, + "acc_x":0.7098100853, + "acc_y":2.5101921536, + "acc_z":9.4829494428, + "gyr_x":5.7250654258, + "gyr_y":4.3293864815, + "gyr_z":-6.9482448362 + }, + { + "index":2.4853515625, + "acc_x":0.7508033364, + "acc_y":2.3859816359, + "acc_z":9.5131922361, + "gyr_x":5.2998855423, + "gyr_y":4.0598391229, + "gyr_z":-7.1961678717 + }, + { + "index":2.490234375, + "acc_x":0.7006128286, + "acc_y":2.3002719988, + "acc_z":9.5577312283, + "gyr_x":5.9050343879, + "gyr_y":3.7843439935, + "gyr_z":-6.5938132024 + }, + { + "index":2.4951171875, + "acc_x":0.7067888952, + "acc_y":2.4054487594, + "acc_z":9.5515006899, + "gyr_x":6.3990402981, + "gyr_y":3.9162532498, + "gyr_z":-6.6918561673 + }, + { + "index":2.5, + "acc_x":0.7546914006, + "acc_y":2.3982868881, + "acc_z":9.5161268041, + "gyr_x":5.9101284235, + "gyr_y":3.4716299577, + "gyr_z":-7.0134775591 + }, + { + "index":2.5048828125, + "acc_x":0.7377479109, + "acc_y":2.3347847674, + "acc_z":9.5623757256, + "gyr_x":5.9804577734, + "gyr_y":3.6285310106, + "gyr_z":-7.3549946739 + }, + { + "index":2.509765625, + "acc_x":0.8275256566, + "acc_y":2.2983611765, + "acc_z":9.535360372, + "gyr_x":5.3883788353, + "gyr_y":4.067680562, + "gyr_z":-8.0463500637 + }, + { + "index":2.5146484375, + "acc_x":0.7236744794, + "acc_y":2.3934547839, + "acc_z":9.5131007725, + "gyr_x":4.8406399159, + "gyr_y":3.6904550947, + "gyr_z":-8.3680939996 + }, + { + "index":2.51953125, + "acc_x":0.7235297534, + "acc_y":2.3825840614, + "acc_z":9.4955138791, + "gyr_x":3.8770881907, + "gyr_y":3.7753008219, + "gyr_z":-9.0270918049 + }, + { + "index":2.5244140625, + "acc_x":0.6917678704, + "acc_y":2.4156409825, + "acc_z":9.4225744423, + "gyr_x":4.0698135371, + "gyr_y":3.8239415037, + "gyr_z":-9.4948281159 + }, + { + "index":2.529296875, + "acc_x":0.7328148455, + "acc_y":2.3206673877, + "acc_z":9.4475395404, + "gyr_x":2.7322431998, + "gyr_y":4.3216197237, + "gyr_z":-8.2033986543 + }, + { + "index":2.5341796875, + "acc_x":0.6435041665, + "acc_y":2.2248041389, + "acc_z":9.5267025455, + "gyr_x":2.5009898692, + "gyr_y":4.7064685981, + "gyr_z":-8.1693818512 + }, + { + "index":2.5390625, + "acc_x":0.6032321442, + "acc_y":2.1396280423, + "acc_z":9.5784212879, + "gyr_x":1.6407160454, + "gyr_y":4.6043763162, + "gyr_z":-7.2158730535 + }, + { + "index":2.5439453125, + "acc_x":0.6894423168, + "acc_y":2.1471934038, + "acc_z":9.6250887292, + "gyr_x":1.8238519925, + "gyr_y":5.0436444571, + "gyr_z":-5.7855121718 + }, + { + "index":2.548828125, + "acc_x":0.6259543762, + "acc_y":2.0720476628, + "acc_z":9.5805367945, + "gyr_x":0.3014914316, + "gyr_y":5.274978089, + "gyr_z":-5.0548034053 + }, + { + "index":2.5537109375, + "acc_x":0.5490304172, + "acc_y":1.9135576542, + "acc_z":9.681795623, + "gyr_x":-0.5345205777, + "gyr_y":5.8626639842, + "gyr_z":-5.1290391595 + }, + { + "index":2.55859375, + "acc_x":0.461081064, + "acc_y":1.7602847991, + "acc_z":9.5904159842, + "gyr_x":-0.6738945587, + "gyr_y":5.6709796756, + "gyr_z":-3.5105794799 + }, + { + "index":2.5634765625, + "acc_x":0.6729774475, + "acc_y":1.9952299405, + "acc_z":9.667811203, + "gyr_x":-0.187476989, + "gyr_y":5.9402740386, + "gyr_z":-2.5066704149 + }, + { + "index":2.568359375, + "acc_x":0.6096049336, + "acc_y":1.7962956865, + "acc_z":9.6975090501, + "gyr_x":-0.8039730187, + "gyr_y":6.3344064464, + "gyr_z":-0.6018646565 + }, + { + "index":2.5732421875, + "acc_x":0.4407606138, + "acc_y":1.7153232131, + "acc_z":9.6242205807, + "gyr_x":0.4850220897, + "gyr_y":6.718416916, + "gyr_z":-0.4786534005 + }, + { + "index":2.578125, + "acc_x":0.2373460007, + "acc_y":0.9894317633, + "acc_z":10.2262224123, + "gyr_x":0.3192980348, + "gyr_y":6.8919326203, + "gyr_z":4.732323972 + }, + { + "index":2.5830078125, + "acc_x":0.9753591419, + "acc_y":1.260881957, + "acc_z":11.1453373634, + "gyr_x":7.2801489696, + "gyr_y":8.1504867346, + "gyr_z":6.7675046226 + }, + { + "index":2.587890625, + "acc_x":1.8238359723, + "acc_y":3.7709397673, + "acc_z":11.246117432, + "gyr_x":11.7806441328, + "gyr_y":10.0576662796, + "gyr_z":5.4083350192 + }, + { + "index":2.5927734375, + "acc_x":1.6052173086, + "acc_y":2.9667430789, + "acc_z":10.2571119574, + "gyr_x":5.4667794071, + "gyr_y":13.5105957353, + "gyr_z":4.2402033251 + }, + { + "index":2.59765625, + "acc_x":2.2298416298, + "acc_y":3.5689552486, + "acc_z":10.9836243772, + "gyr_x":0.3834385864, + "gyr_y":18.8958541897, + "gyr_z":0.1567278167 + }, + { + "index":2.6025390625, + "acc_x":1.5623289334, + "acc_y":3.2933577172, + "acc_z":10.8039391977, + "gyr_x":-9.0520178794, + "gyr_y":24.2877283647, + "gyr_z":-1.1338816891 + }, + { + "index":2.607421875, + "acc_x":0.8698704668, + "acc_y":1.388972277, + "acc_z":10.9900991628, + "gyr_x":-7.3414376511, + "gyr_y":27.2732682394, + "gyr_z":-0.2288230613 + }, + { + "index":2.6123046875, + "acc_x":0.7269594541, + "acc_y":1.4596621897, + "acc_z":11.130858395, + "gyr_x":-2.9293009021, + "gyr_y":28.8648831005, + "gyr_z":2.1335530605 + }, + { + "index":2.6171875, + "acc_x":1.3267384948, + "acc_y":1.5607784609, + "acc_z":11.6204975792, + "gyr_x":-0.7240718542, + "gyr_y":32.5901783754, + "gyr_z":5.0108796668 + }, + { + "index":2.6220703125, + "acc_x":1.5488384382, + "acc_y":1.9546994968, + "acc_z":11.8025133198, + "gyr_x":1.4465851866, + "gyr_y":37.5523193723, + "gyr_z":7.7209325209 + }, + { + "index":2.626953125, + "acc_x":1.5544474667, + "acc_y":2.0549709938, + "acc_z":11.8827552237, + "gyr_x":1.8619659774, + "gyr_y":43.1154313813, + "gyr_z":10.321948938 + }, + { + "index":2.6318359375, + "acc_x":1.479367776, + "acc_y":2.1251182551, + "acc_z":11.8334302867, + "gyr_x":2.1477692522, + "gyr_y":48.3850410779, + "gyr_z":12.7886971198 + }, + { + "index":2.63671875, + "acc_x":1.6665473397, + "acc_y":2.1264983684, + "acc_z":12.3560620607, + "gyr_x":2.546186242, + "gyr_y":53.0014926252, + "gyr_z":16.0480725863 + }, + { + "index":2.6416015625, + "acc_x":2.0362880537, + "acc_y":1.9308612529, + "acc_z":12.5277290976, + "gyr_x":6.6560299464, + "gyr_y":58.2973184671, + "gyr_z":16.9657386378 + }, + { + "index":2.646484375, + "acc_x":1.7788602445, + "acc_y":2.1944753409, + "acc_z":12.0612361312, + "gyr_x":8.7403252374, + "gyr_y":63.9218729981, + "gyr_z":17.0885929416 + }, + { + "index":2.6513671875, + "acc_x":1.7405333861, + "acc_y":2.4687405721, + "acc_z":12.002921961, + "gyr_x":5.553024826, + "gyr_y":69.5526497122, + "gyr_z":19.5365665029 + }, + { + "index":2.65625, + "acc_x":2.1014795358, + "acc_y":2.3131707493, + "acc_z":12.2116768912, + "gyr_x":3.9640808487, + "gyr_y":75.9301536043, + "gyr_z":19.122252252 + }, + { + "index":2.6611328125, + "acc_x":2.2248723424, + "acc_y":2.451909024, + "acc_z":12.4062490518, + "gyr_x":3.8299572618, + "gyr_y":82.1737075473, + "gyr_z":19.0991972652 + }, + { + "index":2.666015625, + "acc_x":2.3109386512, + "acc_y":2.7574967948, + "acc_z":12.0807876219, + "gyr_x":3.2687419263, + "gyr_y":88.3852790668, + "gyr_z":15.5944881639 + }, + { + "index":2.6708984375, + "acc_x":2.3304536249, + "acc_y":2.8676743538, + "acc_z":11.4614939096, + "gyr_x":0.0338672116, + "gyr_y":94.8787805975, + "gyr_z":14.6040013632 + }, + { + "index":2.67578125, + "acc_x":2.4496953698, + "acc_y":2.9523376755, + "acc_z":11.6875368183, + "gyr_x":-5.8309735863, + "gyr_y":100.8848640309, + "gyr_z":11.1567837609 + }, + { + "index":2.6806640625, + "acc_x":2.7298264789, + "acc_y":3.1528329323, + "acc_z":11.647491502, + "gyr_x":-11.4401779171, + "gyr_y":107.8526887162, + "gyr_z":6.0504470703 + }, + { + "index":2.685546875, + "acc_x":3.0307589708, + "acc_y":3.4688644006, + "acc_z":11.8270675121, + "gyr_x":-16.8617209646, + "gyr_y":116.331734441, + "gyr_z":2.567850872 + }, + { + "index":2.6904296875, + "acc_x":3.4468882349, + "acc_y":3.4635743048, + "acc_z":12.0036804199, + "gyr_x":-20.6614292288, + "gyr_y":124.3447972086, + "gyr_z":-1.6997428262 + }, + { + "index":2.6953125, + "acc_x":3.3439824917, + "acc_y":3.2234432019, + "acc_z":12.4123702037, + "gyr_x":-25.2620348764, + "gyr_y":132.8891665014, + "gyr_z":-5.6337053448 + }, + { + "index":2.7001953125, + "acc_x":3.8770681052, + "acc_y":2.575074742, + "acc_z":12.8597925082, + "gyr_x":-27.1695293738, + "gyr_y":142.4155720081, + "gyr_z":-11.3349755309 + }, + { + "index":2.705078125, + "acc_x":3.9062317747, + "acc_y":1.7904373345, + "acc_z":13.7398546258, + "gyr_x":-27.6406383065, + "gyr_y":152.332198083, + "gyr_z":-15.148505773 + }, + { + "index":2.7099609375, + "acc_x":4.3599230212, + "acc_y":1.7213161854, + "acc_z":14.0155201022, + "gyr_x":-27.3405057262, + "gyr_y":164.270384685, + "gyr_z":-22.5475459426 + }, + { + "index":2.71484375, + "acc_x":4.7360104264, + "acc_y":1.4934593124, + "acc_z":14.1920023795, + "gyr_x":-26.3005568504, + "gyr_y":178.2170151557, + "gyr_z":-27.6870877054 + }, + { + "index":2.7197265625, + "acc_x":5.3007994103, + "acc_y":1.7087755044, + "acc_z":14.3272226475, + "gyr_x":-26.767870642, + "gyr_y":193.5213814122, + "gyr_z":-32.144668828 + }, + { + "index":2.724609375, + "acc_x":5.7186289875, + "acc_y":1.7825533147, + "acc_z":14.6382370555, + "gyr_x":-27.1815684484, + "gyr_y":209.7066666441, + "gyr_z":-35.8399480049 + }, + { + "index":2.7294921875, + "acc_x":5.792181346, + "acc_y":2.3027351317, + "acc_z":14.19556452, + "gyr_x":-33.853963932, + "gyr_y":227.1741718245, + "gyr_z":-31.6400013527 + }, + { + "index":2.734375, + "acc_x":6.5400193353, + "acc_y":3.301401808, + "acc_z":14.466553589, + "gyr_x":-46.9113263417, + "gyr_y":246.6743146428, + "gyr_z":-20.818868582 + }, + { + "index":2.7392578125, + "acc_x":7.2344187858, + "acc_y":5.6226225845, + "acc_z":15.0713964349, + "gyr_x":-64.7016059689, + "gyr_y":265.6295272073, + "gyr_z":-1.0882480054 + }, + { + "index":2.744140625, + "acc_x":8.4104400986, + "acc_y":5.7179078497, + "acc_z":15.4881417958, + "gyr_x":-78.8201462836, + "gyr_y":287.3744903047, + "gyr_z":18.8413201916 + }, + { + "index":2.7490234375, + "acc_x":8.5210548609, + "acc_y":4.1370703862, + "acc_z":15.3007398846, + "gyr_x":-86.0884300493, + "gyr_y":309.2092289654, + "gyr_z":30.8805013241 + }, + { + "index":2.75390625, + "acc_x":8.6316251515, + "acc_y":2.4927448018, + "acc_z":16.3934284193, + "gyr_x":-86.1461827144, + "gyr_y":330.0899050456, + "gyr_z":46.9946496359 + }, + { + "index":2.7587890625, + "acc_x":9.0270512362, + "acc_y":2.618182859, + "acc_z":17.0489477626, + "gyr_x":-84.0384971469, + "gyr_y":351.5204814559, + "gyr_z":57.8799932364 + }, + { + "index":2.763671875, + "acc_x":10.844207213, + "acc_y":4.1644495612, + "acc_z":18.6823183799, + "gyr_x":-80.9391998658, + "gyr_y":370.4599629225, + "gyr_z":47.0717325443 + }, + { + "index":2.7685546875, + "acc_x":13.9565956368, + "acc_y":5.9287454654, + "acc_z":21.1536569019, + "gyr_x":-61.2264276575, + "gyr_y":384.4559545361, + "gyr_z":12.4033881262 + }, + { + "index":2.7734375, + "acc_x":12.5909705867, + "acc_y":5.8417251887, + "acc_z":21.6334882672, + "gyr_x":-38.3994227858, + "gyr_y":395.2856553531, + "gyr_z":-23.1440365139 + }, + { + "index":2.7783203125, + "acc_x":14.2020804239, + "acc_y":4.0939964788, + "acc_z":22.196602073, + "gyr_x":-22.0437110375, + "gyr_y":407.4711095418, + "gyr_z":-48.0104600698 + }, + { + "index":2.783203125, + "acc_x":14.4813561845, + "acc_y":3.7018125737, + "acc_z":22.3653666595, + "gyr_x":-2.0822613898, + "gyr_y":417.8173626276, + "gyr_z":-80.9172174581 + }, + { + "index":2.7880859375, + "acc_x":12.7785430596, + "acc_y":3.502372044, + "acc_z":24.1234898593, + "gyr_x":16.3201724156, + "gyr_y":427.1282555543, + "gyr_z":-105.4971103311 + }, + { + "index":2.79296875, + "acc_x":14.3247267245, + "acc_y":4.7297495943, + "acc_z":27.0166527775, + "gyr_x":37.357720944, + "gyr_y":416.0884408, + "gyr_z":-128.0080469883 + }, + { + "index":2.7978515625, + "acc_x":16.5120322031, + "acc_y":1.2796707571, + "acc_z":23.1121967805, + "gyr_x":52.9497646535, + "gyr_y":421.4346210697, + "gyr_z":-137.3240837533 + }, + { + "index":2.802734375, + "acc_x":17.4072960363, + "acc_y":0.4243218393, + "acc_z":20.1604725274, + "gyr_x":61.5612596959, + "gyr_y":423.3469160323, + "gyr_z":-145.4891872169 + }, + { + "index":2.8076171875, + "acc_x":19.4401053636, + "acc_y":1.061641376, + "acc_z":20.1266978327, + "gyr_x":63.9120372796, + "gyr_y":422.0202587733, + "gyr_z":-154.6489574423 + }, + { + "index":2.8125, + "acc_x":24.1280115467, + "acc_y":-0.7096304645, + "acc_z":19.8425644251, + "gyr_x":70.7120592294, + "gyr_y":417.1877774977, + "gyr_z":-176.6615409921 + }, + { + "index":2.8173828125, + "acc_x":25.5703827959, + "acc_y":-1.996998794, + "acc_z":18.490215727, + "gyr_x":79.9201068849, + "gyr_y":413.7219125113, + "gyr_z":-201.9517032915 + }, + { + "index":2.822265625, + "acc_x":28.9805395008, + "acc_y":-4.5694156778, + "acc_z":16.801900431, + "gyr_x":86.1784925197, + "gyr_y":411.3804978655, + "gyr_z":-217.5299110725 + }, + { + "index":2.8271484375, + "acc_x":31.2835883996, + "acc_y":-7.2129856128, + "acc_z":13.9183192608, + "gyr_x":91.6009652593, + "gyr_y":415.2248088264, + "gyr_z":-221.3031959945 + }, + { + "index":2.83203125, + "acc_x":30.2967952467, + "acc_y":-9.4021961735, + "acc_z":10.8021394768, + "gyr_x":96.53229758, + "gyr_y":419.2124486087, + "gyr_z":-218.4349515711 + }, + { + "index":2.8369140625, + "acc_x":30.9388758032, + "acc_y":-11.2152434559, + "acc_z":4.3939072816, + "gyr_x":91.42797546, + "gyr_y":427.1153352604, + "gyr_z":-208.6845463338 + }, + { + "index":2.841796875, + "acc_x":30.7128215482, + "acc_y":-13.4732649604, + "acc_z":0.6707125135, + "gyr_x":100.5844743919, + "gyr_y":444.4194150767, + "gyr_z":-191.3885922946 + }, + { + "index":2.8466796875, + "acc_x":30.3047133564, + "acc_y":-20.513915927, + "acc_z":-1.7040214136, + "gyr_x":149.0393934275, + "gyr_y":471.8166256824, + "gyr_z":-184.2825676488 + }, + { + "index":2.8515625, + "acc_x":18.1381003255, + "acc_y":-15.4508073893, + "acc_z":-8.3606324583, + "gyr_x":188.6005105202, + "gyr_y":530.3643006546, + "gyr_z":-176.0651707645 + }, + { + "index":2.8564453125, + "acc_x":-4.2495705677, + "acc_y":-10.1203373926, + "acc_z":-0.2482259574, + "gyr_x":209.8642576194, + "gyr_y":459.2000686169, + "gyr_z":-103.6661686688 + }, + { + "index":2.861328125, + "acc_x":-14.7096619274, + "acc_y":-4.3653730818, + "acc_z":-4.4386753696, + "gyr_x":173.1799673619, + "gyr_y":353.0563657236, + "gyr_z":-65.3039844658 + }, + { + "index":2.8662109375, + "acc_x":-19.9492005441, + "acc_y":-3.4014416989, + "acc_z":-11.7280743338, + "gyr_x":144.6906360117, + "gyr_y":246.902903509, + "gyr_z":51.3810128686 + }, + { + "index":2.87109375, + "acc_x":-6.8772725699, + "acc_y":-5.7931221437, + "acc_z":-10.1922601318, + "gyr_x":133.0326433052, + "gyr_y":125.5932328703, + "gyr_z":94.2392009285 + }, + { + "index":2.8759765625, + "acc_x":2.4887644227, + "acc_y":-5.9258630775, + "acc_z":-8.1020488038, + "gyr_x":125.3252395419, + "gyr_y":16.5939372072, + "gyr_z":135.4578632345 + }, + { + "index":2.880859375, + "acc_x":8.7038803427, + "acc_y":-6.4881884872, + "acc_z":-7.6306848762, + "gyr_x":116.0872689164, + "gyr_y":-72.315739269, + "gyr_z":150.1361269085 + }, + { + "index":2.8857421875, + "acc_x":13.8117138594, + "acc_y":-7.2240623395, + "acc_z":-7.3538126924, + "gyr_x":120.1257598921, + "gyr_y":-142.2905931875, + "gyr_z":143.1324853989 + }, + { + "index":2.890625, + "acc_x":16.4852827805, + "acc_y":-6.1078894669, + "acc_z":-7.8711218604, + "gyr_x":126.0802369033, + "gyr_y":-198.8422555317, + "gyr_z":114.0398212605 + }, + { + "index":2.8955078125, + "acc_x":17.5916585865, + "acc_y":-5.1265900225, + "acc_z":-7.807363997, + "gyr_x":132.0863932126, + "gyr_y":-237.6337151814, + "gyr_z":83.2187899564 + }, + { + "index":2.900390625, + "acc_x":18.4997426793, + "acc_y":-5.6078837389, + "acc_z":-6.7753032021, + "gyr_x":141.4389378011, + "gyr_y":-260.5472507588, + "gyr_z":46.0134254965 + }, + { + "index":2.9052734375, + "acc_x":17.8005337116, + "acc_y":-3.5180393942, + "acc_z":-6.7286883344, + "gyr_x":144.457804523, + "gyr_y":-269.7438455156, + "gyr_z":12.7271981307 + }, + { + "index":2.91015625, + "acc_x":17.6476483533, + "acc_y":-2.7694711864, + "acc_z":-6.3922141784, + "gyr_x":143.3780645231, + "gyr_y":-270.5586170955, + "gyr_z":-9.0219045971 + }, + { + "index":2.9150390625, + "acc_x":16.8571634409, + "acc_y":-2.9431103954, + "acc_z":-5.6239931337, + "gyr_x":144.0853211441, + "gyr_y":-267.7746211941, + "gyr_z":-31.4666383969 + }, + { + "index":2.919921875, + "acc_x":14.3571551855, + "acc_y":-1.9025613526, + "acc_z":-5.0168254954, + "gyr_x":141.735560796, + "gyr_y":-263.9384746452, + "gyr_z":-51.589175538 + }, + { + "index":2.9248046875, + "acc_x":12.0457464218, + "acc_y":-1.6139970892, + "acc_z":-4.618174134, + "gyr_x":136.805776789, + "gyr_y":-261.0635210144, + "gyr_z":-62.0867487817 + }, + { + "index":2.9296875, + "acc_x":10.4084315952, + "acc_y":-1.6047937502, + "acc_z":-4.4388390659, + "gyr_x":130.6787647141, + "gyr_y":-258.9300942706, + "gyr_z":-69.5428966531 + }, + { + "index":2.9345703125, + "acc_x":9.262254524, + "acc_y":-1.7161476986, + "acc_z":-4.2000871674, + "gyr_x":123.1518839055, + "gyr_y":-258.590794801, + "gyr_z":-71.7800694046 + }, + { + "index":2.939453125, + "acc_x":8.3652539443, + "acc_y":-2.1491809063, + "acc_z":-3.5609621311, + "gyr_x":114.9654664051, + "gyr_y":-259.8151615082, + "gyr_z":-69.542150522 + }, + { + "index":2.9443359375, + "acc_x":7.3297391112, + "acc_y":-2.0550349383, + "acc_z":-3.1834248557, + "gyr_x":106.2118385139, + "gyr_y":-263.3725004854, + "gyr_z":-63.4695883786 + }, + { + "index":2.94921875, + "acc_x":6.3280564863, + "acc_y":-1.8571270754, + "acc_z":-3.0321048655, + "gyr_x":93.5139398473, + "gyr_y":-268.4619653948, + "gyr_z":-51.8540737642 + }, + { + "index":2.9541015625, + "acc_x":5.5998305575, + "acc_y":-1.5799619234, + "acc_z":-3.1047184278, + "gyr_x":78.7431939288, + "gyr_y":-275.5899113441, + "gyr_z":-37.4661628568 + }, + { + "index":2.958984375, + "acc_x":5.2501156431, + "acc_y":-1.4683236671, + "acc_z":-3.1084013668, + "gyr_x":60.6780966994, + "gyr_y":-284.0135798537, + "gyr_z":-20.7636806051 + }, + { + "index":2.9638671875, + "acc_x":5.4273984194, + "acc_y":-1.6189079458, + "acc_z":-2.7261316404, + "gyr_x":41.4510161439, + "gyr_y":-294.0475940569, + "gyr_z":-3.4542586191 + }, + { + "index":2.96875, + "acc_x":6.1104559897, + "acc_y":-1.8029407399, + "acc_z":-2.0816490613, + "gyr_x":24.4814243161, + "gyr_y":-304.6561470061, + "gyr_z":10.7836876556 + }, + { + "index":2.9736328125, + "acc_x":6.9529667195, + "acc_y":-1.9627993001, + "acc_z":-1.3981783307, + "gyr_x":9.723177334, + "gyr_y":-314.3154117033, + "gyr_z":21.5407173398 + }, + { + "index":2.978515625, + "acc_x":7.9121778271, + "acc_y":-2.1848297315, + "acc_z":-0.4769904501, + "gyr_x":-3.6606079787, + "gyr_y":-324.2544439469, + "gyr_z":30.2801493938 + }, + { + "index":2.9833984375, + "acc_x":8.9474680039, + "acc_y":-2.4547107205, + "acc_z":0.5280548707, + "gyr_x":-13.2576115034, + "gyr_y":-331.9546380298, + "gyr_z":34.9976083557 + }, + { + "index":2.98828125, + "acc_x":9.6611922895, + "acc_y":-2.4949250819, + "acc_z":1.6771948116, + "gyr_x":-20.8276034944, + "gyr_y":-338.874208504, + "gyr_z":37.4847439113 + }, + { + "index":2.9931640625, + "acc_x":10.0464415789, + "acc_y":-2.6739282524, + "acc_z":2.7189525503, + "gyr_x":-26.5285649258, + "gyr_y":-344.9310419287, + "gyr_z":37.775235038 + }, + { + "index":2.998046875, + "acc_x":10.2157011315, + "acc_y":-2.8594802067, + "acc_z":3.8861783827, + "gyr_x":-29.4634352982, + "gyr_y":-349.3244848322, + "gyr_z":37.1942528103 + }, + { + "index":3.0029296875, + "acc_x":10.1398216282, + "acc_y":-2.7584525352, + "acc_z":4.8480429638, + "gyr_x":-32.3481123409, + "gyr_y":-353.2620571682, + "gyr_z":36.5270822692 + }, + { + "index":3.0078125, + "acc_x":9.990687281, + "acc_y":-2.7595147863, + "acc_z":5.5926553124, + "gyr_x":-36.0072000353, + "gyr_y":-357.1875646214, + "gyr_z":37.4207915659 + }, + { + "index":3.0126953125, + "acc_x":12.4799373254, + "acc_y":-2.1525572984, + "acc_z":2.4797748499, + "gyr_x":-26.6794706905, + "gyr_y":-360.758384769, + "gyr_z":33.2961015116 + }, + { + "index":3.017578125, + "acc_x":9.9974450085, + "acc_y":-4.4103730769, + "acc_z":7.6797592852, + "gyr_x":-50.0161171592, + "gyr_y":-370.6239803818, + "gyr_z":58.30900851 + }, + { + "index":3.0224609375, + "acc_x":10.6140026349, + "acc_y":1.560838094, + "acc_z":-0.8084125738, + "gyr_x":-70.5945230162, + "gyr_y":-363.9299117289, + "gyr_z":18.8835951888 + }, + { + "index":3.02734375, + "acc_x":16.4695280545, + "acc_y":-5.4413204343, + "acc_z":3.8630870333, + "gyr_x":-35.0463108394, + "gyr_y":-369.8184988636, + "gyr_z":27.3721580663 + }, + { + "index":3.0322265625, + "acc_x":5.3190382887, + "acc_y":-0.9308183376, + "acc_z":11.2215004214, + "gyr_x":-63.360592504, + "gyr_y":-379.2807885046, + "gyr_z":66.8338362279 + }, + { + "index":3.037109375, + "acc_x":7.6839153126, + "acc_y":-3.3749531426, + "acc_z":10.1698034772, + "gyr_x":-79.2153808213, + "gyr_y":-375.8535562155, + "gyr_z":55.0920543095 + }, + { + "index":3.0419921875, + "acc_x":7.2169454326, + "acc_y":-2.4936571736, + "acc_z":10.993014641, + "gyr_x":-99.8116819187, + "gyr_y":-376.8540261781, + "gyr_z":66.4745840808 + }, + { + "index":3.046875, + "acc_x":6.9976191903, + "acc_y":-3.0222162745, + "acc_z":12.4138049919, + "gyr_x":-110.8452087599, + "gyr_y":-373.4249298925, + "gyr_z":66.3985574089 + }, + { + "index":3.0517578125, + "acc_x":6.1922774299, + "acc_y":-2.1532903508, + "acc_z":13.9016800213, + "gyr_x":-125.7372803495, + "gyr_y":-370.6031604244, + "gyr_z":69.4494898672 + }, + { + "index":3.056640625, + "acc_x":5.5060408175, + "acc_y":-1.4464118523, + "acc_z":15.5913138718, + "gyr_x":-139.2594204744, + "gyr_y":-365.4361196683, + "gyr_z":69.960090201 + }, + { + "index":3.0615234375, + "acc_x":4.694601067, + "acc_y":-0.7576544435, + "acc_z":17.2920672447, + "gyr_x":-151.0051621628, + "gyr_y":-360.4265589337, + "gyr_z":67.9375941525 + }, + { + "index":3.06640625, + "acc_x":3.8037850457, + "acc_y":-0.0413642352, + "acc_z":18.9299428548, + "gyr_x":-162.2468861927, + "gyr_y":-352.3489338452, + "gyr_z":63.0248500278 + }, + { + "index":3.0712890625, + "acc_x":2.7969548382, + "acc_y":0.7167024362, + "acc_z":20.7326057889, + "gyr_x":-170.55436043, + "gyr_y":-343.1356908802, + "gyr_z":55.6212972232 + }, + { + "index":3.076171875, + "acc_x":1.8629597726, + "acc_y":1.6558207943, + "acc_z":22.2587968601, + "gyr_x":-176.7267564117, + "gyr_y":-332.6186832964, + "gyr_z":46.5552942716 + }, + { + "index":3.0810546875, + "acc_x":0.661466139, + "acc_y":2.5621172056, + "acc_z":23.2448323554, + "gyr_x":-180.3952842608, + "gyr_y":-322.4825424763, + "gyr_z":34.3903135328 + }, + { + "index":3.0859375, + "acc_x":-0.4880662623, + "acc_y":3.5527949717, + "acc_z":24.3328986404, + "gyr_x":-182.7832543157, + "gyr_y":-314.5179152613, + "gyr_z":20.3141682512 + }, + { + "index":3.0908203125, + "acc_x":-1.4923388909, + "acc_y":4.4284742551, + "acc_z":25.3524738941, + "gyr_x":-180.3158656905, + "gyr_y":-309.2887893297, + "gyr_z":7.2527167264 + }, + { + "index":3.095703125, + "acc_x":-2.822745164, + "acc_y":4.958734971, + "acc_z":26.4086982231, + "gyr_x":-170.2133112273, + "gyr_y":-307.774503782, + "gyr_z":-4.4514200883 + }, + { + "index":3.1005859375, + "acc_x":-4.8366843436, + "acc_y":5.32240125, + "acc_z":26.1987841961, + "gyr_x":-157.311923309, + "gyr_y":-311.9537358452, + "gyr_z":-10.3419791122 + }, + { + "index":3.10546875, + "acc_x":-7.3322505333, + "acc_y":5.521560472, + "acc_z":24.5851697704, + "gyr_x":-148.6722394639, + "gyr_y":-323.16632333, + "gyr_z":-12.4246997972 + }, + { + "index":3.1103515625, + "acc_x":-9.8717810229, + "acc_y":5.4116667027, + "acc_z":22.9715145692, + "gyr_x":-145.4680403116, + "gyr_y":-339.7572693376, + "gyr_z":-9.8512573379 + }, + { + "index":3.115234375, + "acc_x":-12.2348648499, + "acc_y":5.6319430554, + "acc_z":22.044197082, + "gyr_x":-147.1316799852, + "gyr_y":-359.2099503098, + "gyr_z":-5.0863043509 + }, + { + "index":3.1201171875, + "acc_x":-14.158062404, + "acc_y":5.9646938498, + "acc_z":21.9403198987, + "gyr_x":-148.8449254664, + "gyr_y":-376.5799812289, + "gyr_z":0.7453988209 + }, + { + "index":3.125, + "acc_x":-16.3480409345, + "acc_y":6.3566294093, + "acc_z":22.1102812122, + "gyr_x":-150.0865865217, + "gyr_y":-391.1452771543, + "gyr_z":7.1041325119 + }, + { + "index":3.1298828125, + "acc_x":-19.0926419316, + "acc_y":7.0573219118, + "acc_z":22.2378796669, + "gyr_x":-149.0424967023, + "gyr_y":-398.4262826247, + "gyr_z":12.3132752169 + }, + { + "index":3.134765625, + "acc_x":-22.1641547569, + "acc_y":7.7678588284, + "acc_z":21.8499690315, + "gyr_x":-147.906964173, + "gyr_y":-399.7388919225, + "gyr_z":14.691372002 + }, + { + "index":3.1396484375, + "acc_x":-25.1989924347, + "acc_y":8.2822890976, + "acc_z":21.1140657701, + "gyr_x":-143.3408908036, + "gyr_y":-390.7084670915, + "gyr_z":14.6483828415 + }, + { + "index":3.14453125, + "acc_x":-27.8473488898, + "acc_y":8.1720132877, + "acc_z":19.7848048913, + "gyr_x":-135.2726709827, + "gyr_y":-373.6385661835, + "gyr_z":13.0446819888 + }, + { + "index":3.1494140625, + "acc_x":-29.9917941053, + "acc_y":7.9964843983, + "acc_z":18.0014385825, + "gyr_x":-122.4310039133, + "gyr_y":-351.3600512957, + "gyr_z":9.2119194636 + }, + { + "index":3.154296875, + "acc_x":-31.7466956981, + "acc_y":7.8372973951, + "acc_z":15.3033821159, + "gyr_x":-107.2715597519, + "gyr_y":-324.3004759178, + "gyr_z":6.1801019159 + }, + { + "index":3.1591796875, + "acc_x":-33.0804367828, + "acc_y":7.4398122693, + "acc_z":12.364707375, + "gyr_x":-91.4554940025, + "gyr_y":-294.419838526, + "gyr_z":3.5978096989 + }, + { + "index":3.1640625, + "acc_x":-34.2990578745, + "acc_y":6.9211049756, + "acc_z":9.2310436621, + "gyr_x":-76.4221047115, + "gyr_y":-265.1674853833, + "gyr_z":2.1323782753 + }, + { + "index":3.1689453125, + "acc_x":-35.6678217797, + "acc_y":6.1555647007, + "acc_z":6.3369260581, + "gyr_x":-63.6473838869, + "gyr_y":-235.9919248435, + "gyr_z":0.2504429141 + }, + { + "index":3.173828125, + "acc_x":-36.8557507901, + "acc_y":5.9553611066, + "acc_z":2.9975392613, + "gyr_x":-57.8795548772, + "gyr_y":-203.4644366027, + "gyr_z":-2.6081780748 + }, + { + "index":3.1787109375, + "acc_x":-37.5516647312, + "acc_y":5.7130750068, + "acc_z":-0.1992277845, + "gyr_x":-59.712598677, + "gyr_y":-168.5593970134, + "gyr_z":-7.5006741714 + }, + { + "index":3.18359375, + "acc_x":-37.8412061388, + "acc_y":5.9573886081, + "acc_z":-3.3716462886, + "gyr_x":-68.8384445956, + "gyr_y":-131.4041986232, + "gyr_z":-17.0664052133 + }, + { + "index":3.1884765625, + "acc_x":-37.7086496188, + "acc_y":6.6950951607, + "acc_z":-5.9252185751, + "gyr_x":-80.8404350039, + "gyr_y":-97.5231615801, + "gyr_z":-27.3936984 + }, + { + "index":3.193359375, + "acc_x":-37.1036092416, + "acc_y":7.8141000799, + "acc_z":-7.546803989, + "gyr_x":-91.1113806922, + "gyr_y":-68.3005600224, + "gyr_z":-37.3445238871 + }, + { + "index":3.1982421875, + "acc_x":-36.5404847497, + "acc_y":9.3503093216, + "acc_z":-7.8480237858, + "gyr_x":-95.3832083756, + "gyr_y":-44.7538865776, + "gyr_z":-46.3986457232 + }, + { + "index":3.203125, + "acc_x":-36.1616238143, + "acc_y":10.873022886, + "acc_z":-7.2567793101, + "gyr_x":-91.5172299455, + "gyr_y":-24.9141829075, + "gyr_z":-54.2703576211 + }, + { + "index":3.2080078125, + "acc_x":-35.8964369524, + "acc_y":11.8748649217, + "acc_z":-5.9611954756, + "gyr_x":-78.6923496306, + "gyr_y":-8.7670863651, + "gyr_z":-58.080698466 + }, + { + "index":3.212890625, + "acc_x":-35.4515390546, + "acc_y":12.5523159819, + "acc_z":-3.9613402187, + "gyr_x":-53.1485089725, + "gyr_y":5.3360422559, + "gyr_z":-57.5413662056 + }, + { + "index":3.2177734375, + "acc_x":-96.6405075567, + "acc_y":54.6345868218, + "acc_z":122.7698758427, + "gyr_x":167.9355748672, + "gyr_y":229.1148489384, + "gyr_z":61.6587558616 + }, + { + "index":3.22265625, + "acc_x":-25.264562445, + "acc_y":32.9763605549, + "acc_z":-32.91984415, + "gyr_x":150.5852096799, + "gyr_y":173.9002174721, + "gyr_z":-136.5533982796 + }, + { + "index":3.2275390625, + "acc_x":-46.8185004074, + "acc_y":-9.474829245, + "acc_z":-1.1044390199, + "gyr_x":166.3370794559, + "gyr_y":279.533856261, + "gyr_z":-21.276952147 + }, + { + "index":3.232421875, + "acc_x":-30.3724006014, + "acc_y":-6.6853550999, + "acc_z":13.4046914148, + "gyr_x":152.8428256927, + "gyr_y":297.2593263988, + "gyr_z":81.2051271124 + }, + { + "index":3.2373046875, + "acc_x":6.7534999048, + "acc_y":-11.0586366339, + "acc_z":21.8247042511, + "gyr_x":197.7662512105, + "gyr_y":298.165072944, + "gyr_z":68.2402296271 + }, + { + "index":3.2421875, + "acc_x":3.6382479837, + "acc_y":5.594974157, + "acc_z":23.7764678326, + "gyr_x":277.9228999469, + "gyr_y":302.2135056531, + "gyr_z":77.8988698122 + }, + { + "index":3.2470703125, + "acc_x":48.7249568494, + "acc_y":33.8251249927, + "acc_z":-20.9858454493, + "gyr_x":378.9412837349, + "gyr_y":345.8308323137, + "gyr_z":-97.4009225754 + }, + { + "index":3.251953125, + "acc_x":12.5650213883, + "acc_y":23.8166715634, + "acc_z":-18.3943702017, + "gyr_x":154.4889940883, + "gyr_y":345.1598025025, + "gyr_z":-134.6191929266 + }, + { + "index":3.2568359375, + "acc_x":-1.6691752114, + "acc_y":9.1987837803, + "acc_z":5.1775734695, + "gyr_x":-22.6881120671, + "gyr_y":343.5411719214, + "gyr_z":-186.0699344526 + }, + { + "index":3.26171875, + "acc_x":-15.2902244066, + "acc_y":8.974015032, + "acc_z":10.1387293035, + "gyr_x":-64.9826088923, + "gyr_y":326.5399178963, + "gyr_z":-136.7019334575 + }, + { + "index":3.2666015625, + "acc_x":-22.3034950506, + "acc_y":1.7250813455, + "acc_z":23.9237704898, + "gyr_x":-11.0892543411, + "gyr_y":286.5417703971, + "gyr_z":-132.922723414 + }, + { + "index":3.271484375, + "acc_x":-16.6375884568, + "acc_y":1.4490416883, + "acc_z":20.5472731607, + "gyr_x":56.0685644939, + "gyr_y":235.4009515483, + "gyr_z":-135.909943758 + }, + { + "index":3.2763671875, + "acc_x":-10.5383317914, + "acc_y":4.6707518842, + "acc_z":13.09092116, + "gyr_x":66.1145901019, + "gyr_y":208.4901755216, + "gyr_z":-88.022669475 + }, + { + "index":3.28125, + "acc_x":-7.8250783475, + "acc_y":5.7975836773, + "acc_z":9.3559590813, + "gyr_x":55.9526801748, + "gyr_y":195.0060333166, + "gyr_z":-36.2048492467 + }, + { + "index":3.2861328125, + "acc_x":-7.07100035, + "acc_y":6.1823553216, + "acc_z":9.8203180916, + "gyr_x":17.746838043, + "gyr_y":171.2345481225, + "gyr_z":-11.7690758793 + }, + { + "index":3.291015625, + "acc_x":-5.3022295339, + "acc_y":2.1787837862, + "acc_z":12.9700548602, + "gyr_x":13.5188822234, + "gyr_y":148.2051561125, + "gyr_z":-13.4269362621 + }, + { + "index":3.2958984375, + "acc_x":-0.8125553226, + "acc_y":0.0145006362, + "acc_z":11.7171996627, + "gyr_x":28.3096174083, + "gyr_y":139.0184256654, + "gyr_z":-28.0739950964 + }, + { + "index":3.30078125, + "acc_x":-1.4121881841, + "acc_y":1.4506363985, + "acc_z":11.0823564387, + "gyr_x":34.1642487715, + "gyr_y":133.7415003897, + "gyr_z":-23.8840783807 + }, + { + "index":3.3056640625, + "acc_x":-0.7053035961, + "acc_y":0.6968820914, + "acc_z":10.6129733981, + "gyr_x":32.9008743922, + "gyr_y":126.7027918063, + "gyr_z":-22.4981319925 + }, + { + "index":3.310546875, + "acc_x":-1.5412383029, + "acc_y":1.2007928381, + "acc_z":11.0492170414, + "gyr_x":34.0773293538, + "gyr_y":119.6148685983, + "gyr_z":-22.7047059816 + }, + { + "index":3.3154296875, + "acc_x":-1.2828544039, + "acc_y":1.5521836839, + "acc_z":10.5220963477, + "gyr_x":40.36421453, + "gyr_y":115.5299682821, + "gyr_z":-16.5907921974 + }, + { + "index":3.3203125, + "acc_x":-1.6580596082, + "acc_y":2.5185051945, + "acc_z":10.3995878245, + "gyr_x":39.0086755265, + "gyr_y":114.7062318992, + "gyr_z":-13.6159077536 + }, + { + "index":3.3251953125, + "acc_x":-17.8197028758, + "acc_y":2.8722268347, + "acc_z":19.1246775897, + "gyr_x":33.8574863922, + "gyr_y":41.2210736836, + "gyr_z":-17.7164737424 + }, + { + "index":3.330078125, + "acc_x":-5.213119188, + "acc_y":-0.2310738645, + "acc_z":10.8040498058, + "gyr_x":20.5060340041, + "gyr_y":24.2237754251, + "gyr_z":-5.5522650772 + }, + { + "index":3.3349609375, + "acc_x":-1.5952186145, + "acc_y":0.0579092866, + "acc_z":11.6952108958, + "gyr_x":23.0826226296, + "gyr_y":13.1275821032, + "gyr_z":-15.0091383713 + }, + { + "index":3.33984375, + "acc_x":-0.7651899218, + "acc_y":2.1657372072, + "acc_z":9.0693075811, + "gyr_x":25.9407629874, + "gyr_y":7.7228011469, + "gyr_z":-13.5049123153 + }, + { + "index":3.3447265625, + "acc_x":0.9509203666, + "acc_y":-0.2799229551, + "acc_z":10.7343052498, + "gyr_x":22.2956357116, + "gyr_y":7.0637783221, + "gyr_z":-20.9760480028 + }, + { + "index":3.349609375, + "acc_x":-0.0670048882, + "acc_y":0.6012797565, + "acc_z":10.1652681185, + "gyr_x":30.1174692924, + "gyr_y":10.4987212081, + "gyr_z":-24.9985143885 + }, + { + "index":3.3544921875, + "acc_x":-0.7824542391, + "acc_y":1.6411297393, + "acc_z":9.4859663466, + "gyr_x":29.3587815813, + "gyr_y":9.7097029233, + "gyr_z":-19.8139389109 + }, + { + "index":3.359375, + "acc_x":-0.5136129178, + "acc_y":1.1197366431, + "acc_z":9.6898181421, + "gyr_x":23.743629398, + "gyr_y":8.7478510884, + "gyr_z":-20.5178051241 + }, + { + "index":3.3642578125, + "acc_x":-0.1442042014, + "acc_y":0.711277518, + "acc_z":10.1477606967, + "gyr_x":25.4462429698, + "gyr_y":7.2451138455, + "gyr_z":-20.0556115327 + }, + { + "index":3.369140625, + "acc_x":-0.0431070302, + "acc_y":0.7673862312, + "acc_z":9.9310222279, + "gyr_x":28.2735952291, + "gyr_y":6.434225973, + "gyr_z":-21.4028358592 + }, + { + "index":3.3740234375, + "acc_x":-0.3562654683, + "acc_y":1.0205924552, + "acc_z":9.7473631315, + "gyr_x":27.6519236779, + "gyr_y":6.5023550824, + "gyr_z":-21.319987948 + }, + { + "index":3.37890625, + "acc_x":-0.3293132313, + "acc_y":1.163765154, + "acc_z":9.7733913787, + "gyr_x":26.2616351827, + "gyr_y":5.6554498722, + "gyr_z":-20.7310810534 + }, + { + "index":3.3837890625, + "acc_x":-0.2885859973, + "acc_y":1.11539575, + "acc_z":9.7259957842, + "gyr_x":25.1247486374, + "gyr_y":4.8934518762, + "gyr_z":-20.961780203 + }, + { + "index":3.388671875, + "acc_x":-0.2552075833, + "acc_y":1.1689113638, + "acc_z":9.7289718848, + "gyr_x":23.4410589511, + "gyr_y":4.7033859754, + "gyr_z":-20.6539992933 + }, + { + "index":3.3935546875, + "acc_x":-0.1179578652, + "acc_y":1.1708527184, + "acc_z":9.6983686068, + "gyr_x":20.4579778291, + "gyr_y":3.8625911307, + "gyr_z":-20.0144126842 + }, + { + "index":3.3984375, + "acc_x":-0.1243784945, + "acc_y":1.2030261433, + "acc_z":9.5720230889, + "gyr_x":19.1065863376, + "gyr_y":3.7657881793, + "gyr_z":-17.7602414414 + }, + { + "index":3.4033203125, + "acc_x":-0.161638982, + "acc_y":1.3920858668, + "acc_z":9.5592273627, + "gyr_x":16.4890239205, + "gyr_y":3.5734221137, + "gyr_z":-14.2176194082 + }, + { + "index":3.408203125, + "acc_x":-0.1033453571, + "acc_y":1.356335298, + "acc_z":9.689834037, + "gyr_x":12.5550190211, + "gyr_y":3.0773359814, + "gyr_z":-10.9585536133 + }, + { + "index":3.4130859375, + "acc_x":0.0000414228, + "acc_y":1.3808941588, + "acc_z":9.4390664059, + "gyr_x":10.5367715881, + "gyr_y":2.5756320291, + "gyr_z":-7.1732210464 + }, + { + "index":3.41796875, + "acc_x":-0.0641249298, + "acc_y":1.6148914235, + "acc_z":9.4770225757, + "gyr_x":6.160730618, + "gyr_y":2.8448171604, + "gyr_z":-4.5343080246 + }, + { + "index":3.4228515625, + "acc_x":-0.0849017935, + "acc_y":1.1082987366, + "acc_z":9.7470957129, + "gyr_x":3.297971765, + "gyr_y":1.7672539091, + "gyr_z":-1.5377585354 + }, + { + "index":3.427734375, + "acc_x":0.0126431578, + "acc_y":1.2993274899, + "acc_z":9.5255462671, + "gyr_x":0.7844080362, + "gyr_y":2.0780868431, + "gyr_z":-1.494915068 + }, + { + "index":3.4326171875, + "acc_x":0.0606840251, + "acc_y":1.0756063552, + "acc_z":9.8441268756, + "gyr_x":-1.1177542117, + "gyr_y":1.5830521117, + "gyr_z":0.1593391092 + }, + { + "index":3.4375, + "acc_x":0.228644552, + "acc_y":1.0002112567, + "acc_z":9.7955870932, + "gyr_x":-1.562428036, + "gyr_y":1.3063839565, + "gyr_z":-0.3587718655 + }, + { + "index":3.4423828125, + "acc_x":0.2638663458, + "acc_y":0.9277463235, + "acc_z":9.7455099744, + "gyr_x":-2.7592550464, + "gyr_y":1.7905330782, + "gyr_z":-0.7260093257 + }, + { + "index":3.447265625, + "acc_x":0.1357018629, + "acc_y":0.8360800867, + "acc_z":9.7498842642, + "gyr_x":-2.4537820854, + "gyr_y":1.701199887, + "gyr_z":-1.4550785606 + }, + { + "index":3.4521484375, + "acc_x":0.1516301931, + "acc_y":0.8080632265, + "acc_z":9.8289243839, + "gyr_x":-2.4922860182, + "gyr_y":1.6336858576, + "gyr_z":-1.716041301 + }, + { + "index":3.45703125, + "acc_x":-0.0205218577, + "acc_y":0.8405716335, + "acc_z":9.7417438608, + "gyr_x":-2.3075725718, + "gyr_y":1.9904794258, + "gyr_z":-1.0108604279 + } + ] +} \ No newline at end of file diff --git a/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py b/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py index 33121243..df760686 100644 --- a/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py +++ b/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py @@ -78,3 +78,4 @@ def test_single_stride_regression(self, healthy_example_imu_data, healthy_exampl test.estimate(data, sampling_rate_hz=fs) snapshot.assert_match(test.orientation_, test.__class__.__name__) + snapshot.assert_match(test.rotated_data_, f"{test.__class__.__name__}_rotated_data") From 1a00883210573ebb709f23b088a1422aa543ce15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20K=C3=BCderle?= Date: Fri, 19 Apr 2024 10:39:40 +0200 Subject: [PATCH 2/4] Added test for new method, dropped py3.8 and used new object in some places --- .github/workflows/test-and-lint.yml | 6 +- CHANGELOG.md | 11 +- README.md | 4 +- .../_trajectory_wrapper.py | 17 +- .../trajectory_methods/_rts_kalman.py | 6 + poetry.lock | 20 +- pyproject.toml | 4 +- tests/_regression_utils.py | 128 -- tests/conftest.py | 16 +- ...on_SimpleGyroIntegration_rotated_data.json | 1975 +++++++++++++++++ .../test_ori_method_mixin.py | 5 +- 11 files changed, 2014 insertions(+), 178 deletions(-) delete mode 100644 tests/_regression_utils.py create mode 100644 tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_SimpleGyroIntegration_rotated_data.json diff --git a/.github/workflows/test-and-lint.yml b/.github/workflows/test-and-lint.yml index 550138f9..84d5114e 100644 --- a/.github/workflows/test-and-lint.yml +++ b/.github/workflows/test-and-lint.yml @@ -10,7 +10,7 @@ jobs: test: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11"] os: [ windows-latest, ubuntu-latest ] runs-on: ${{ matrix.os }} @@ -21,14 +21,14 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies (all) - if : ${{ matrix.python-version == '3.8' || matrix.python-version == '3.9' }} + if : ${{ matrix.python-version == '3.9' }} run: | python -m pip install --upgrade pip pip install poetry poetry config virtualenvs.in-project true poetry install -E all - name: Install dependencies (no hmm) - if : ${{ matrix.python-version != '3.8' && matrix.python-version != '3.9' }} + if : ${{ matrix.python-version != '3.9' }} run: | python -m pip install --upgrade pip pip install poetry diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b26077b..e3c11253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ project. - Changed resampling function in inverse feature transform of HMM. Resampling of state sequence is now also possible if the `target_sample_rate` is not a multiple of the HMM sampling rate, e.g. `target_sample_rate=200`, `sample_rate=52.1` (https://github.com/mad-lab-fau/gaitmap/pull/62) +- Dropped Python 3.8 support! ## [2.3.0] - 2023-08-03 @@ -41,14 +42,14 @@ project. ### Fixed -- Fixed bug in HMM when uneven sequence length were provided. In newer numpy versions this requires an explicit cast to +- Fixed bug in HMM when uneven sequnece length were provided. In newer numpy versions this requires an explicit cast to an object array. ## [2.2.1] - 2023-06-22 ### Fixed -- Fixed edge case where the output of the stride event method had the events in the wrong order for some strides. +- Fixed edecase where the output of the stride event method had the events in the wrong order for some strides. The reason for that is that a valid segmented stridelist does not always result in a valid min_vel_event list for algorithms that are allowed to search outside the segmented stride region (e.g. `HerzerEventDetection`). We now check for consistency again after the stride list conversion. @@ -64,11 +65,11 @@ Gaitmap is now available as official PyPi package!!! (https://github.com/mad-lab-fau/gaitmap/pull/15) - Certain ZUPT detectors now return the `min_vel_index_` and `min_vel_value_` as additional attributes. These values represent the index in the input data with the lowest velocity and the corresponding velocity value - (according to the internal metric of the respective ZUPT detector). + (according to the internal metric of the repective ZUPT detector). (https://github.com/mad-lab-fau/gaitmap/pull/16) - New example explaining more advanced usage of the `RtsKalman` algorithm. (https://github.com/mad-lab-fau/gaitmap/pull/17) -- The `find_extrema_in_radius` and the `snap_to_min` utility functions gained the ability to define asymmetric search +- The `find_extrema_in_radius` and the `snap_to_min` utility functions gained the ability to define asymetric search windows around the search indices. (https://github.com/mad-lab-fau/gaitmap/pull/21) - Temporal and Spatial Parameter calculation have new options to work with ic-stride lists and with partial input @@ -81,7 +82,7 @@ Gaitmap is now available as official PyPi package!!! ### Changed - We now require Pandas >2.0 as we are using the new pandas dtypes. - It could be that this will require you to perform some explicit type conversion in your code. + It could be that this will require you to perform some sxplicit type conversion in your code. - The Zupt Detector example is updated to use newer tpcp features (https://github.com/mad-lab-fau/gaitmap/pull/17) - The column order of the Spatial Parameter Calculation output has been changed diff --git a/README.md b/README.md index 255bfd2f..c3a1685e 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ You can track the progress in the [pull request](https://github.com/mad-lab-fau/ ### Supported Python versions -*gaitmap* is tested against Python 3.8 and 3.9 at the moment. -We expect most features to work with all Python versions >= 3.8, but because of some known issues +*gaitmap* is tested against Python 3.9 at the moment. +We expect most features to work with all Python versions >= 3.9, but because of some known issues (see specific features above) we do not officially support them. ## Working with Algorithms diff --git a/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py b/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py index 3ca67a13..782cb2fd 100644 --- a/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py +++ b/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py @@ -24,7 +24,7 @@ get_multi_sensor_names, set_correct_index, ) -from gaitmap.utils.rotations import get_gravity_rotation, rotate_dataset_series +from gaitmap.utils.rotations import get_gravity_rotation class _TrajectoryReconstructionWrapperMixin: @@ -136,10 +136,7 @@ def _estimate_single_sensor( } for (r_id, i_region), stride_list in zip(integration_regions.iterrows(), stride_list_list): i_start, i_end = (int(i_region["start"]), int(i_region["end"])) - i_orientation, i_velocity, i_position = self._estimate_region(data, i_start, i_end, stride_list) - orientation[r_id] = pd.DataFrame(i_orientation.as_quat(), columns=GF_ORI) - velocity[r_id] = pd.DataFrame(i_velocity, columns=GF_VEL) - position[r_id] = pd.DataFrame(i_position, columns=GF_POS) + orientation[r_id], velocity[r_id], position[r_id] = self._estimate_region(data, i_start, i_end, stride_list) orientation_df = pd.concat(orientation) orientation_df.index = orientation_df.index.rename(full_index) velocity_df = pd.concat(velocity) @@ -160,11 +157,11 @@ def _estimate_region( assert self.pos_method is not None # Apply the orientation method ori_method = self.ori_method.clone().set_params(initial_orientation=initial_orientation) - orientation = ori_method.estimate( + ori_method.estimate( stride_data, sampling_rate_hz=self.sampling_rate_hz, stride_event_list=stride_event_list - ).orientation_object_ - - rotated_stride_data = rotate_dataset_series(stride_data, orientation[:-1]) + ) + orientation = ori_method.orientation_ + rotated_stride_data = ori_method.rotated_data_ # Apply the Position method pos_method = self.pos_method.clone().estimate( rotated_stride_data, sampling_rate_hz=self.sampling_rate_hz, stride_event_list=stride_event_list @@ -178,7 +175,7 @@ def _estimate_region( trajectory_method = trajectory_method.estimate( stride_data, sampling_rate_hz=self.sampling_rate_hz, stride_event_list=stride_event_list ) - orientation = trajectory_method.orientation_object_ + orientation = trajectory_method.orientation_ velocity = trajectory_method.velocity_ position = trajectory_method.position_ return orientation, velocity, position diff --git a/gaitmap/trajectory_reconstruction/trajectory_methods/_rts_kalman.py b/gaitmap/trajectory_reconstruction/trajectory_methods/_rts_kalman.py index 3e5f3cef..f1646a2e 100644 --- a/gaitmap/trajectory_reconstruction/trajectory_methods/_rts_kalman.py +++ b/gaitmap/trajectory_reconstruction/trajectory_methods/_rts_kalman.py @@ -82,6 +82,9 @@ class RtsKalman(BaseTrajectoryMethod): They can be used as a measure of how good the filter worked and how accurate the results are. zupts_ 2D array indicating the start and the end samples of the detected ZUPTs for debug porpuses. + rotated_data_ + The rotated data after applying the estimated orientation to the data. + The first sample of the data remain unrotated (initial orientation). Other Parameters ---------------- @@ -342,6 +345,9 @@ class MadgwickRtsKalman(RtsKalman): They can be used as a measure of how good the filter worked and how accurate the results are. zupts_ 2D array indicating the start and the end samples of the detected ZUPTs for debug purposes. + rotated_data_ + The rotated data after applying the estimated orientation to the data. + The first sample of the data remain unrotated (initial orientation). Other Parameters ---------------- diff --git a/poetry.lock b/poetry.lock index 195465a5..8abb8a01 100644 --- a/poetry.lock +++ b/poetry.lock @@ -65,9 +65,6 @@ files = [ {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] @@ -1424,7 +1421,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.9\""} llvmlite = "==0.41.*" numpy = ">=1.22,<1.27" @@ -2707,20 +2703,20 @@ files = [ [[package]] name = "tpcp" -version = "0.24.0" +version = "0.32.0" description = "Pipeline and Dataset helpers for complex algorithm evaluation." optional = false -python-versions = ">=3.8,<4.0" +python-versions = "<4.0,>=3.9" files = [ - {file = "tpcp-0.24.0-py3-none-any.whl", hash = "sha256:bf69e47da716dbf1ada779d1e1914af2b14cf4e0a1d06c34d3bebb2e5789e3b1"}, - {file = "tpcp-0.24.0.tar.gz", hash = "sha256:b134fe908f1b752b83d45e5800fc380c82a43baeef79e14f5826a64ab9231bba"}, + {file = "tpcp-0.32.0-py3-none-any.whl", hash = "sha256:52dfe8c6cc00e07a082fd7d033148eba9b17cde26af12cf631f6502084f93c08"}, + {file = "tpcp-0.32.0.tar.gz", hash = "sha256:cfcf348bddc3a2e852c4e1485b0b9cc94cdf487efcd1449b22c237c031a9a30c"}, ] [package.dependencies] joblib = ">=1.3" numpy = ">=1.0" -pandas = ">=1.4" -scikit-learn = ">=1.3" +pandas = ">=1.3" +scikit-learn = ">=1.2.0" tqdm = ">=4.62.3" typing-extensions = ">=4.1.1" @@ -2872,5 +2868,5 @@ stats = ["pingouin"] [metadata] lock-version = "2.0" -python-versions = ">=3.8.0,<4.0" -content-hash = "2762d7255132c41c6cd8379db685e7d7766bd9980209e7b8091cd65681fb0543" +python-versions = ">=3.9.0,<4.0" +content-hash = "6d7f95b2b7e86338256cad5f26eb61148e270dcbed2582997689c7faef818a43" diff --git a/pyproject.toml b/pyproject.toml index 25042502..82023139 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ packages = [ ] [tool.poetry.dependencies] -python = ">=3.8.0,<4.0" +python = ">=3.9.0,<4.0" pandas = ">=2" scipy = ">=1.6.1" typing_extensions = ">=4.1.1" @@ -37,7 +37,7 @@ scikit-learn = ">=1.0.1" # We restrict it to 0.14.6 for now, as later versions don't seem to work on Linux # This version of pomegranate does not support Python 3.10, unfortunately pomegranate = {version = ">=0.14.2,<=0.14.6", python = "<3.10", optional = true} -tpcp = ">=0.15.0" +tpcp = "^0.32.0" pingouin = {version = "^0.5.3", optional = true} pooch = "^1.7.0" diff --git a/tests/_regression_utils.py b/tests/_regression_utils.py deleted file mode 100644 index a6ac5345..00000000 --- a/tests/_regression_utils.py +++ /dev/null @@ -1,128 +0,0 @@ -"""Utils to perform snapshot tests easily. - -This is inspired by github.com/syrusakbary/snapshottest. -Note that it can not be used in combination with this module! -""" - -import re -from pathlib import Path - -import numpy as np -import pandas as pd -from pandas._testing import assert_frame_equal - - -def pytest_addoption(parser) -> None: - group = parser.getgroup("snapshottest") - group.addoption( - "--snapshot-update", action="store_true", default=False, dest="snapshot_update", help="Update the snapshots." - ) - - -class SnapshotNotFound(Exception): - pass - - -class PyTestSnapshotTest: - def __init__(self, request=None) -> None: - self.request = request - self.curr_snapshot_number = 0 - super().__init__() - - @property - def update(self): - return self.request.config.option.snapshot_update - - @property - def module(self): - return Path(self.request.node.fspath.strpath).parent - - @property - def snapshot_folder(self): - return self.module / "snapshot" - - @property - def file_name_json(self): - return self.snapshot_folder / f"{self.test_name}.json" - - @property - def file_name_csv(self): - return self.snapshot_folder / f"{self.test_name}.csv" - - @property - def file_name_txt(self): - return self.snapshot_folder / f"{self.test_name}.txt" - - @property - def test_name(self): - cls_name = getattr(self.request.node.cls, "__name__", "") - flattened_node_name = re.sub(r"\s+", " ", self.request.node.name.replace(r"\n", " ")) - return "{}{}_{}".format(f"{cls_name}." if cls_name else "", flattened_node_name, self.curr_snapshot) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - pass - - def store(self, value) -> None: - self.snapshot_folder.mkdir(parents=True, exist_ok=True) - if isinstance(value, pd.DataFrame): - value.to_json(self.file_name_json, indent=4, orient="table") - elif isinstance(value, np.ndarray): - np.savetxt(self.file_name_csv, value, delimiter=",") - elif isinstance(value, str): - with open(self.file_name_txt, "w") as f: - f.write(value) - else: - raise TypeError(f"The dtype {type(value)} is not supported for snapshot testing") - - def retrieve(self, dtype): - if dtype == pd.DataFrame: - filename = self.file_name_json - if not filename.is_file(): - raise SnapshotNotFound() - return pd.read_json(filename, orient="table") - elif dtype == np.ndarray: - filename = self.file_name_csv - if not filename.is_file(): - raise SnapshotNotFound() - return np.genfromtxt(filename, delimiter=",") - elif dtype == str: - filename = self.file_name_txt - if not filename.is_file(): - raise SnapshotNotFound() - with open(self.file_name_txt) as f: - value = f.read() - return value - else: - raise ValueError(f"The dtype {dtype} is not supported for snapshot testing") - - def assert_match(self, value, name="", **kwargs) -> None: - self.curr_snapshot = name or self.curr_snapshot_number - if self.update: - self.store(value) - else: - value_dtype = type(value) - try: - prev_snapshot = self.retrieve(value_dtype) - except SnapshotNotFound: - self.store(value) # first time this test has been seen - except: - raise - else: - if value_dtype == pd.DataFrame: - assert_frame_equal(value, prev_snapshot, **kwargs) - elif value_dtype == np.ndarray: - np.testing.assert_array_almost_equal(value, prev_snapshot, **kwargs) - elif value_dtype == str: - # Display the string diff line by line as part of error message using difflib - import difflib - - diff = difflib.ndiff(value.splitlines(keepends=True), prev_snapshot.splitlines(keepends=True)) - diff = "".join(diff) - assert value == prev_snapshot, diff - else: - raise ValueError(f"The dtype {value_dtype} is not supported for snapshot testing") - - self.curr_snapshot_number += 1 diff --git a/tests/conftest.py b/tests/conftest.py index 99947899..37f8e5cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,7 @@ import pandas as pd import pytest from numpy.testing import assert_almost_equal, assert_array_equal -from pandas._testing import assert_frame_equal, assert_series_equal +from pandas.testing import assert_frame_equal, assert_series_equal from scipy.spatial.transform import Rotation from tpcp import BaseTpcpObject @@ -18,7 +18,6 @@ get_healthy_example_stride_events, get_ms_example_imu_data, ) -from tests._regression_utils import PyTestSnapshotTest try: from pomegranate import GeneralMixtureModel, State @@ -33,19 +32,6 @@ def reset_random_seed() -> None: random.seed(10) -@pytest.fixture() -def snapshot(request): - with PyTestSnapshotTest(request) as snapshot_test: - yield snapshot_test - - -def pytest_addoption(parser) -> None: - group = parser.getgroup("snapshottest") - group.addoption( - "--snapshot-update", action="store_true", default=False, dest="snapshot_update", help="Update the snapshots." - ) - - healthy_example_imu_data = pytest.fixture()(get_healthy_example_imu_data) ms_example_imu_data = pytest.fixture()(get_ms_example_imu_data) healthy_example_stride_borders = pytest.fixture()(get_healthy_example_stride_borders) diff --git a/tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_SimpleGyroIntegration_rotated_data.json b/tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_SimpleGyroIntegration_rotated_data.json new file mode 100644 index 00000000..d50f09cd --- /dev/null +++ b/tests/test_trajectory_reconstruction/test_orientation_methods/snapshot/TestSimpleRotations.test_single_stride_regression_SimpleGyroIntegration_rotated_data.json @@ -0,0 +1,1975 @@ +{ + "schema":{ + "fields":[ + { + "name":"index", + "type":"number" + }, + { + "name":"acc_x", + "type":"number" + }, + { + "name":"acc_y", + "type":"number" + }, + { + "name":"acc_z", + "type":"number" + }, + { + "name":"gyr_x", + "type":"number" + }, + { + "name":"gyr_y", + "type":"number" + }, + { + "name":"gyr_z", + "type":"number" + } + ], + "primaryKey":[ + "index" + ], + "pandas_version":"1.4.0" + }, + "data":[ + { + "index":2.412109375, + "acc_x":0.8376466171, + "acc_y":2.2936597178, + "acc_z":9.6547557343, + "gyr_x":1.5850653072, + "gyr_y":4.5470933517, + "gyr_z":-7.7914702018 + }, + { + "index":2.4169921875, + "acc_x":0.804183408, + "acc_y":2.3134205538, + "acc_z":9.7311057124, + "gyr_x":3.0488204612, + "gyr_y":4.8394406819, + "gyr_z":-7.7184129709 + }, + { + "index":2.421875, + "acc_x":0.823007102, + "acc_y":2.2468632356, + "acc_z":9.8071438517, + "gyr_x":4.9366157478, + "gyr_y":4.698465151, + "gyr_z":-7.4697679278 + }, + { + "index":2.4267578125, + "acc_x":0.7997524704, + "acc_y":2.2289674465, + "acc_z":9.7832694547, + "gyr_x":7.197877666, + "gyr_y":5.129402651, + "gyr_z":-8.6523427462 + }, + { + "index":2.431640625, + "acc_x":0.865614533, + "acc_y":2.3915739633, + "acc_z":9.6250490816, + "gyr_x":9.147809915, + "gyr_y":4.8796325576, + "gyr_z":-9.1872325017 + }, + { + "index":2.4365234375, + "acc_x":0.8741765812, + "acc_y":2.5939654583, + "acc_z":9.5200857281, + "gyr_x":9.9396289303, + "gyr_y":4.6415108135, + "gyr_z":-9.7880115437 + }, + { + "index":2.44140625, + "acc_x":0.8133471544, + "acc_y":2.5507788324, + "acc_z":9.4344373726, + "gyr_x":10.1212933316, + "gyr_y":4.4612851053, + "gyr_z":-9.9706309368 + }, + { + "index":2.4462890625, + "acc_x":0.7202516841, + "acc_y":2.6397666239, + "acc_z":9.355715891, + "gyr_x":9.5729250025, + "gyr_y":4.6317008312, + "gyr_z":-9.1889753282 + }, + { + "index":2.451171875, + "acc_x":0.7822842893, + "acc_y":2.6290893368, + "acc_z":9.5591029289, + "gyr_x":8.9622126057, + "gyr_y":4.5624699663, + "gyr_z":-8.5316846634 + }, + { + "index":2.4560546875, + "acc_x":0.8264387106, + "acc_y":2.5748665253, + "acc_z":9.3832070347, + "gyr_x":8.290513583, + "gyr_y":4.4512742055, + "gyr_z":-8.5959291842 + }, + { + "index":2.4609375, + "acc_x":0.8422733645, + "acc_y":2.6459475805, + "acc_z":9.4342976287, + "gyr_x":7.8672313713, + "gyr_y":4.7563957385, + "gyr_z":-8.4105306015 + }, + { + "index":2.4658203125, + "acc_x":0.828408808, + "acc_y":2.6147221487, + "acc_z":9.4637312538, + "gyr_x":7.1329762016, + "gyr_y":4.5113189888, + "gyr_z":-7.9369115812 + }, + { + "index":2.470703125, + "acc_x":0.7775440639, + "acc_y":2.6554822105, + "acc_z":9.3554684397, + "gyr_x":6.5242456111, + "gyr_y":4.5749115116, + "gyr_z":-7.6964435996 + }, + { + "index":2.4755859375, + "acc_x":0.7456419014, + "acc_y":2.6914382314, + "acc_z":9.2615525711, + "gyr_x":6.0289049603, + "gyr_y":3.9551075172, + "gyr_z":-6.9903993422 + }, + { + "index":2.48046875, + "acc_x":0.7893046388, + "acc_y":2.7557784166, + "acc_z":9.4081830683, + "gyr_x":5.6658440367, + "gyr_y":4.146754879, + "gyr_z":-7.106384669 + }, + { + "index":2.4853515625, + "acc_x":0.835578297, + "acc_y":2.6501207549, + "acc_z":9.435894734, + "gyr_x":5.2347083909, + "gyr_y":3.8571063907, + "gyr_z":-7.3536705702 + }, + { + "index":2.490234375, + "acc_x":0.7913580605, + "acc_y":2.5833409924, + "acc_z":9.4779975509, + "gyr_x":5.8412983112, + "gyr_y":3.5858705484, + "gyr_z":-6.7594252404 + }, + { + "index":2.4951171875, + "acc_x":0.8028420554, + "acc_y":2.7059566737, + "acc_z":9.4630937235, + "gyr_x":6.3304104968, + "gyr_y":3.7019248533, + "gyr_z":-6.8765798578 + }, + { + "index":2.5, + "acc_x":0.8555706678, + "acc_y":2.715319297, + "acc_z":9.4219450438, + "gyr_x":5.8343895351, + "gyr_y":3.2339942676, + "gyr_z":-7.1883155083 + }, + { + "index":2.5048828125, + "acc_x":0.8446821852, + "acc_y":2.6710240179, + "acc_z":9.4650231965, + "gyr_x":5.8966228647, + "gyr_y":3.3653359462, + "gyr_z":-7.5453569755 + }, + { + "index":2.509765625, + "acc_x":0.9397091554, + "acc_y":2.6512046822, + "acc_z":9.4328373606, + "gyr_x":5.2918587908, + "gyr_y":3.7645498225, + "gyr_z":-8.2552284127 + }, + { + "index":2.5146484375, + "acc_x":0.8418073498, + "acc_y":2.7626853968, + "acc_z":9.4026776318, + "gyr_x":4.7347878761, + "gyr_y":3.3600077829, + "gyr_z":-8.5655345575 + }, + { + "index":2.51953125, + "acc_x":0.8467445864, + "acc_y":2.7686545075, + "acc_z":9.3799020126, + "gyr_x":3.7578421112, + "gyr_y":3.4020867894, + "gyr_z":-9.2237478138 + }, + { + "index":2.5244140625, + "acc_x":0.81929879, + "acc_y":2.8160702119, + "acc_z":9.3003909228, + "gyr_x":3.9389218668, + "gyr_y":3.4134207469, + "gyr_z":-9.7041808069 + }, + { + "index":2.529296875, + "acc_x":0.8657179804, + "acc_y":2.7397237641, + "acc_z":9.3232485843, + "gyr_x":2.6143722126, + "gyr_y":3.9504400927, + "gyr_z":-8.4259349884 + }, + { + "index":2.5341796875, + "acc_x":0.783096492, + "acc_y":2.6650013116, + "acc_z":9.402469907, + "gyr_x":2.3785483813, + "gyr_y":4.3208561877, + "gyr_z":-8.4153019093 + }, + { + "index":2.5390625, + "acc_x":0.7487443235, + "acc_y":2.6000441778, + "acc_z":9.4534228594, + "gyr_x":1.5284119029, + "gyr_y":4.2495244452, + "gyr_z":-7.4542976444 + }, + { + "index":2.5439453125, + "acc_x":0.8406749683, + "acc_y":2.6276444466, + "acc_z":9.4929897043, + "gyr_x":1.7299964638, + "gyr_y":4.7460218128, + "gyr_z":-6.0596473312 + }, + { + "index":2.548828125, + "acc_x":0.782158428, + "acc_y":2.5679174088, + "acc_z":9.4480640368, + "gyr_x":0.2161123731, + "gyr_y":5.0044546334, + "gyr_z":-5.3269182135 + }, + { + "index":2.5537109375, + "acc_x":0.7123643573, + "acc_y":2.4327754091, + "acc_z":9.5537659911, + "gyr_x":-0.6243388572, + "gyr_y":5.5776963875, + "gyr_z":-5.4279911514 + }, + { + "index":2.55859375, + "acc_x":0.6280374235, + "acc_y":2.2925735976, + "acc_z":9.4676793549, + "gyr_x":-0.73819231, + "gyr_y":5.4665243452, + "gyr_z":-3.8092645405 + }, + { + "index":2.5634765625, + "acc_x":0.8458272512, + "acc_y":2.5493317257, + "acc_z":9.5229143979, + "gyr_x":-0.2358444365, + "gyr_y":5.7858565367, + "gyr_z":-2.8414153835 + }, + { + "index":2.568359375, + "acc_x":0.7890701512, + "acc_y":2.3701249321, + "acc_z":9.5603270994, + "gyr_x":-0.8187457158, + "gyr_y":6.2877607305, + "gyr_z":-0.9628287264 + }, + { + "index":2.5732421875, + "acc_x":0.6248856223, + "acc_y":2.3025318875, + "acc_z":9.4905263445, + "gyr_x":0.4715532562, + "gyr_y":6.6760993691, + "gyr_z":-0.899342039 + }, + { + "index":2.578125, + "acc_x":0.4384526208, + "acc_y":1.6341981798, + "acc_z":10.1364731921, + "gyr_x":0.4080936116, + "gyr_y":7.1772957989, + "gyr_z":4.279656677 + }, + { + "index":2.5830078125, + "acc_x":1.1992675658, + "acc_y":1.9836866782, + "acc_z":11.0175513694, + "gyr_x":7.4096060964, + "gyr_y":8.5695339853, + "gyr_z":6.0737917826 + }, + { + "index":2.587890625, + "acc_x":2.0610796361, + "acc_y":4.5112169423, + "acc_z":10.9280553672, + "gyr_x":11.8862352948, + "gyr_y":10.3876990022, + "gyr_z":4.4725199621 + }, + { + "index":2.5927734375, + "acc_x":1.8304865099, + "acc_y":3.6605063243, + "acc_z":9.9918289315, + "gyr_x":5.5492819541, + "gyr_y":13.7649645862, + "gyr_z":3.1831459568 + }, + { + "index":2.59765625, + "acc_x":2.4806076167, + "acc_y":4.3287484536, + "acc_z":10.6516741594, + "gyr_x":0.3716942592, + "gyr_y":18.8599878968, + "gyr_z":-1.1779618949 + }, + { + "index":2.6025390625, + "acc_x":1.8202975066, + "acc_y":4.05906264, + "acc_z":10.4987002248, + "gyr_x":-9.0973920136, + "gyr_y":24.1517082678, + "gyr_z":-2.6553551152 + }, + { + "index":2.607421875, + "acc_x":1.1432308535, + "acc_y":2.1926115523, + "acc_z":10.8329901698, + "gyr_x":-7.3692232049, + "gyr_y":27.1895422374, + "gyr_z":-2.0502608395 + }, + { + "index":2.6123046875, + "acc_x":1.0152241634, + "acc_y":2.291670034, + "acc_z":10.9668892909, + "gyr_x":-2.8997878986, + "gyr_y":28.9465798194, + "gyr_z":0.0327985135 + }, + { + "index":2.6171875, + "acc_x":1.6374118714, + "acc_y":2.4485871593, + "acc_z":11.4260899573, + "gyr_x":-0.6210796118, + "gyr_y":32.8798339667, + "gyr_z":2.5067309524 + }, + { + "index":2.6220703125, + "acc_x":1.8786704659, + "acc_y":2.8721786378, + "acc_z":11.5646123345, + "gyr_x":1.6248890002, + "gyr_y":38.0403203484, + "gyr_z":4.7091253395 + }, + { + "index":2.626953125, + "acc_x":1.9006311587, + "acc_y":2.9961039969, + "acc_z":11.6297053469, + "gyr_x":2.1180439173, + "gyr_y":43.800047813, + "gyr_z":6.7837800612 + }, + { + "index":2.6318359375, + "acc_x":1.837775001, + "acc_y":3.0800872515, + "acc_z":11.5702365807, + "gyr_x":2.4830422788, + "gyr_y":49.2631018714, + "gyr_z":8.732535825 + }, + { + "index":2.63671875, + "acc_x":2.0543709934, + "acc_y":3.1430275219, + "acc_z":12.077772262, + "gyr_x":2.9905505947, + "gyr_y":54.1475477107, + "gyr_z":11.5013739472 + }, + { + "index":2.6416015625, + "acc_x":2.4444223471, + "acc_y":2.9804683204, + "acc_z":12.2458278013, + "gyr_x":7.1381213049, + "gyr_y":59.5130641446, + "gyr_z":11.7455856807 + }, + { + "index":2.646484375, + "acc_x":2.1883144977, + "acc_y":3.21972303, + "acc_z":11.7600232174, + "gyr_x":9.2389862998, + "gyr_y":65.139087905, + "gyr_z":11.2231680297 + }, + { + "index":2.6513671875, + "acc_x":2.1621291814, + "acc_y":3.5056092989, + "acc_z":11.6717682006, + "gyr_x":6.1499071868, + "gyr_y":70.9844349868, + "gyr_z":13.1709236053 + }, + { + "index":2.65625, + "acc_x":2.5438939168, + "acc_y":3.3870747818, + "acc_z":11.8721672753, + "gyr_x":4.555216509, + "gyr_y":77.3233592244, + "gyr_z":12.1289247397 + }, + { + "index":2.6611328125, + "acc_x":2.6900951293, + "acc_y":3.5595925764, + "acc_z":12.0403330196, + "gyr_x":4.4315466834, + "gyr_y":83.556655871, + "gyr_z":11.4285945995 + }, + { + "index":2.666015625, + "acc_x":2.7788082027, + "acc_y":3.8510419756, + "acc_z":11.6763588615, + "gyr_x":3.7435971106, + "gyr_y":89.4372467128, + "gyr_z":7.265994198 + }, + { + "index":2.6708984375, + "acc_x":2.7878538916, + "acc_y":3.9201842302, + "acc_z":11.0399474282, + "gyr_x":0.4745197112, + "gyr_y":95.8275798259, + "gyr_z":5.6666380001 + }, + { + "index":2.67578125, + "acc_x":2.9300103096, + "acc_y":4.0422130077, + "acc_z":11.2423918995, + "gyr_x":-5.5274686928, + "gyr_y":101.5015055795, + "gyr_z":1.7667264431 + }, + { + "index":2.6806640625, + "acc_x":3.2219910311, + "acc_y":4.2539681496, + "acc_z":11.1614916195, + "gyr_x":-11.3556280584, + "gyr_y":107.9612164547, + "gyr_z":-3.8876081133 + }, + { + "index":2.685546875, + "acc_x":3.5444563056, + "acc_y":4.6015760503, + "acc_z":11.2854053297, + "gyr_x":-16.9402941667, + "gyr_y":116.0674015945, + "gyr_z":-8.0850881691 + }, + { + "index":2.6904296875, + "acc_x":3.9829127508, + "acc_y":4.6291302697, + "acc_z":11.4312417548, + "gyr_x":-20.949298182, + "gyr_y":123.6161475223, + "gyr_z":-13.0991356101 + }, + { + "index":2.6953125, + "acc_x":3.9157622399, + "acc_y":4.4471967117, + "acc_z":11.8544922516, + "gyr_x":-25.7554664548, + "gyr_y":131.7171150284, + "gyr_z":-17.7959875184 + }, + { + "index":2.7001953125, + "acc_x":4.4880743197, + "acc_y":3.8620736814, + "acc_z":12.3279562845, + "gyr_x":-27.9706161725, + "gyr_y":140.5954300172, + "gyr_z":-24.4832562432 + }, + { + "index":2.705078125, + "acc_x":4.5831391986, + "acc_y":3.1850776803, + "acc_z":13.2701934018, + "gyr_x":-28.676297684, + "gyr_y":150.0440782327, + "gyr_z":-29.3772909639 + }, + { + "index":2.7099609375, + "acc_x":5.0744911263, + "acc_y":3.1536414167, + "acc_z":13.5170539512, + "gyr_x":-28.8150505187, + "gyr_y":161.1280720604, + "gyr_z":-38.0537990702 + }, + { + "index":2.71484375, + "acc_x":5.4848279262, + "acc_y":2.9531169271, + "acc_z":13.6846231242, + "gyr_x":-28.126191339, + "gyr_y":174.434024905, + "gyr_z":-44.7357454777 + }, + { + "index":2.7197265625, + "acc_x":6.0815015539, + "acc_y":3.1865257112, + "acc_z":13.7531483539, + "gyr_x":-28.9274875467, + "gyr_y":189.1646978518, + "gyr_z":-50.7960751173 + }, + { + "index":2.724609375, + "acc_x":6.5420781609, + "acc_y":3.2984025704, + "acc_z":14.0171306525, + "gyr_x":-29.6512344459, + "gyr_y":204.8442581148, + "gyr_z":-56.2126301157 + }, + { + "index":2.7294921875, + "acc_x":6.6145597491, + "acc_y":3.7753190962, + "acc_z":13.5040945983, + "gyr_x":-36.1745212165, + "gyr_y":222.6583188962, + "gyr_z":-53.5720257666 + }, + { + "index":2.734375, + "acc_x":7.3999498563, + "acc_y":4.80278802, + "acc_z":13.6061107193, + "gyr_x":-48.658556479, + "gyr_y":243.226239276, + "gyr_z":-44.2228079356 + }, + { + "index":2.7392578125, + "acc_x":8.1493641957, + "acc_y":7.1835695586, + "acc_z":13.8953906745, + "gyr_x":-65.2706208112, + "gyr_y":264.2447084238, + "gyr_z":-25.7100749124 + }, + { + "index":2.744140625, + "acc_x":9.3712421704, + "acc_y":7.3342494348, + "acc_z":14.202151757, + "gyr_x":-78.1546366687, + "gyr_y":288.071887171, + "gyr_z":-7.6255539837 + }, + { + "index":2.7490234375, + "acc_x":9.4966121769, + "acc_y":5.7564325186, + "acc_z":14.1603040278, + "gyr_x":-84.6564542415, + "gyr_y":311.1328272321, + "gyr_z":2.1952224385 + }, + { + "index":2.75390625, + "acc_x":9.7097904675, + "acc_y":4.2536212128, + "acc_z":15.3977238707, + "gyr_x":-83.6384186556, + "gyr_y":333.6851926295, + "gyr_z":15.742123372 + }, + { + "index":2.7587890625, + "acc_x":10.1777828972, + "acc_y":4.4558824014, + "acc_z":15.986463106, + "gyr_x":-80.7551895951, + "gyr_y":356.2066833551, + "gyr_z":23.9710156109 + }, + { + "index":2.763671875, + "acc_x":12.131509464, + "acc_y":6.1724400253, + "acc_z":17.2828224701, + "gyr_x":-78.3796200317, + "gyr_y":373.8260659567, + "gyr_z":10.871696426 + }, + { + "index":2.7685546875, + "acc_x":15.4413581238, + "acc_y":8.1966258745, + "acc_z":19.2819492908, + "gyr_x":-61.2047789515, + "gyr_y":383.7380059085, + "gyr_z":-26.6086135276 + }, + { + "index":2.7734375, + "acc_x":14.1492326953, + "acc_y":8.184335344, + "acc_z":19.8363801439, + "gyr_x":-41.1102163462, + "gyr_y":390.3272583234, + "gyr_z":-64.9268276298 + }, + { + "index":2.7783203125, + "acc_x":15.837385588, + "acc_y":6.5165330917, + "acc_z":20.4416499657, + "gyr_x":-26.7887111078, + "gyr_y":399.4538993018, + "gyr_z":-92.4260887056 + }, + { + "index":2.783203125, + "acc_x":16.1662249481, + "acc_y":6.1524077104, + "acc_z":20.6015477423, + "gyr_x":-9.5758779046, + "gyr_y":405.7978109713, + "gyr_z":-127.9051194441 + }, + { + "index":2.7880859375, + "acc_x":14.6453190709, + "acc_y":6.1737120256, + "acc_z":22.4699898706, + "gyr_x":6.6301476253, + "gyr_y":412.0499771692, + "gyr_z":-154.93673698 + }, + { + "index":2.79296875, + "acc_x":16.4564022465, + "acc_y":7.7252328394, + "acc_z":25.0394779685, + "gyr_x":25.6018641563, + "gyr_y":398.2770127066, + "gyr_z":-177.8516676238 + }, + { + "index":2.7978515625, + "acc_x":18.3656001888, + "acc_y":3.842676542, + "acc_z":21.3634494276, + "gyr_x":40.1106675367, + "gyr_y":402.3031633843, + "gyr_z":-189.2406417038 + }, + { + "index":2.802734375, + "acc_x":19.0471595083, + "acc_y":2.6438967984, + "acc_z":18.4351344241, + "gyr_x":47.7492110371, + "gyr_y":403.1650424211, + "gyr_z":-198.3871594166 + }, + { + "index":2.8076171875, + "acc_x":21.0981401569, + "acc_y":3.2561022723, + "acc_z":18.1216727848, + "gyr_x":49.0530941974, + "gyr_y":400.7516048482, + "gyr_z":-207.5911667763 + }, + { + "index":2.8125, + "acc_x":25.7781479525, + "acc_y":1.4283635595, + "acc_z":17.6022369385, + "gyr_x":53.6438906977, + "gyr_y":393.3198890426, + "gyr_z":-229.5145637079 + }, + { + "index":2.8173828125, + "acc_x":27.1256976274, + "acc_y":-0.0232813494, + "acc_z":16.2455084363, + "gyr_x":60.2773780826, + "gyr_y":386.8758408359, + "gyr_z":-255.0122669752 + }, + { + "index":2.822265625, + "acc_x":30.4010451891, + "acc_y":-2.8064241749, + "acc_z":14.5243079783, + "gyr_x":64.7824854389, + "gyr_y":382.718332424, + "gyr_z":-270.7578556852 + }, + { + "index":2.8271484375, + "acc_x":32.4569633234, + "acc_y":-5.7924322654, + "acc_z":11.721899843, + "gyr_x":69.4950129386, + "gyr_y":386.1121218605, + "gyr_z":-275.4366638409 + }, + { + "index":2.83203125, + "acc_x":31.2045209601, + "acc_y":-8.3286467643, + "acc_z":8.9383906329, + "gyr_x":74.3473311552, + "gyr_y":390.4448528464, + "gyr_z":-273.4898185458 + }, + { + "index":2.8369140625, + "acc_x":31.2504854794, + "acc_y":-10.8808395104, + "acc_z":2.7049273184, + "gyr_x":69.8784053595, + "gyr_y":399.5589611701, + "gyr_z":-264.2072466889 + }, + { + "index":2.841796875, + "acc_x":30.6751797506, + "acc_y":-13.5551700602, + "acc_z":-0.7394885584, + "gyr_x":80.3496781826, + "gyr_y":418.7650423444, + "gyr_z":-249.8703959508 + }, + { + "index":2.8466796875, + "acc_x":30.0552280417, + "acc_y":-20.8220063712, + "acc_z":-2.2863519612, + "gyr_x":128.9705711548, + "gyr_y":446.5123269921, + "gyr_z":-250.5535220724 + }, + { + "index":2.8515625, + "acc_x":17.2715101768, + "acc_y":-16.4480589712, + "acc_z":-8.2930452667, + "gyr_x":168.8067408463, + "gyr_y":505.4374693155, + "gyr_z":-252.7699293634 + }, + { + "index":2.8564453125, + "acc_x":-4.2245439581, + "acc_y":-10.0480116486, + "acc_z":1.3162010985, + "gyr_x":197.2305213351, + "gyr_y":442.8505184176, + "gyr_z":-175.0369217095 + }, + { + "index":2.861328125, + "acc_x":-15.0640913304, + "acc_y":-4.7073564354, + "acc_z":-2.4589475344, + "gyr_x":164.9676530278, + "gyr_y":342.1865075708, + "gyr_z":-120.8253961366 + }, + { + "index":2.8662109375, + "acc_x":-20.981426562, + "acc_y":-4.5110658259, + "acc_z":-9.3015138606, + "gyr_x":148.3510830803, + "gyr_y":249.8733247819, + "gyr_z":9.5765393205 + }, + { + "index":2.87109375, + "acc_x":-7.7983436259, + "acc_y":-6.8238473448, + "acc_z":-8.7954677462, + "gyr_x":141.0551008501, + "gyr_y":134.1306271218, + "gyr_z":66.8191941833 + }, + { + "index":2.8759765625, + "acc_x":1.7322362548, + "acc_y":-6.7938136084, + "acc_z":-7.6023976873, + "gyr_x":137.436035382, + "gyr_y":30.2859039387, + "gyr_z":120.518493732 + }, + { + "index":2.880859375, + "acc_x":7.9623279732, + "acc_y":-7.3455837001, + "acc_z":-7.6632374292, + "gyr_x":129.8678275802, + "gyr_y":-56.5367606717, + "gyr_z":145.5493689643 + }, + { + "index":2.8857421875, + "acc_x":13.068577375, + "acc_y":-8.0852446909, + "acc_z":-7.8019344533, + "gyr_x":133.4800319185, + "gyr_y":-126.9815045668, + "gyr_z":145.6794359296 + }, + { + "index":2.890625, + "acc_x":15.6700882231, + "acc_y":-7.051233871, + "acc_z":-8.7036691924, + "gyr_x":136.8567670322, + "gyr_y":-186.4576967604, + "gyr_z":122.2428001642 + }, + { + "index":2.8955078125, + "acc_x":16.7663565226, + "acc_y":-6.0771147523, + "acc_z":-8.8691191732, + "gyr_x":140.0303645537, + "gyr_y":-228.4313576914, + "gyr_z":95.1450297751 + }, + { + "index":2.900390625, + "acc_x":17.7625557882, + "acc_y":-6.4527601614, + "acc_z":-7.9023459099, + "gyr_x":145.8201530745, + "gyr_y":-255.3069797106, + "gyr_z":59.6817174565 + }, + { + "index":2.9052734375, + "acc_x":17.0581503206, + "acc_y":-4.3640963534, + "acc_z":-8.0302564687, + "gyr_x":145.5893888158, + "gyr_y":-268.0574013505, + "gyr_z":27.2165482541 + }, + { + "index":2.91015625, + "acc_x":16.9291186601, + "acc_y":-3.5832928938, + "acc_z":-7.7804124946, + "gyr_x":142.3458044316, + "gyr_y":-271.1934548879, + "gyr_z":5.6931028682 + }, + { + "index":2.9150390625, + "acc_x":16.2116641387, + "acc_y":-3.66843991, + "acc_z":-6.941156339, + "gyr_x":140.757507311, + "gyr_y":-270.8319083226, + "gyr_z":-17.0698103895 + }, + { + "index":2.919921875, + "acc_x":13.7762343118, + "acc_y":-2.5484430934, + "acc_z":-6.2158658898, + "gyr_x":136.3125954631, + "gyr_y":-269.1436117238, + "gyr_z":-37.3433154834 + }, + { + "index":2.9248046875, + "acc_x":11.5103090377, + "acc_y":-2.1997600154, + "acc_z":-5.6308407476, + "gyr_x":130.2496260059, + "gyr_y":-267.3636030196, + "gyr_z":-47.712531344 + }, + { + "index":2.9296875, + "acc_x":9.8939788177, + "acc_y":-2.1576566313, + "acc_z":-5.2979056732, + "gyr_x":123.2909601524, + "gyr_y":-265.9845071414, + "gyr_z":-54.8591728572 + }, + { + "index":2.9345703125, + "acc_x":8.7733295277, + "acc_y":-2.2332055815, + "acc_z":-4.941329359, + "gyr_x":115.4765561446, + "gyr_y":-265.8214077161, + "gyr_z":-56.4987452061 + }, + { + "index":2.939453125, + "acc_x":7.9444073962, + "acc_y":-2.5884640212, + "acc_z":-4.1788932212, + "gyr_x":107.4803531231, + "gyr_y":-266.731978293, + "gyr_z":-53.4706719886 + }, + { + "index":2.9443359375, + "acc_x":6.9501750567, + "acc_y":-2.4455501682, + "acc_z":-3.7144029002, + "gyr_x":99.3406659807, + "gyr_y":-269.5506899499, + "gyr_z":-46.3494868518 + }, + { + "index":2.94921875, + "acc_x":5.9658862506, + "acc_y":-2.2233737598, + "acc_z":-3.4859274452, + "gyr_x":87.8979048569, + "gyr_y":-273.2770289221, + "gyr_z":-33.1534698121 + }, + { + "index":2.9541015625, + "acc_x":5.2292907299, + "acc_y":-1.9481632337, + "acc_z":-3.5146602887, + "gyr_x":74.7305276284, + "gyr_y":-278.7265928872, + "gyr_z":-16.7582171441 + }, + { + "index":2.958984375, + "acc_x":4.8768549784, + "acc_y":-1.8338401401, + "acc_z":-3.4978798686, + "gyr_x":58.5830498224, + "gyr_y":-285.1993814139, + "gyr_z":2.4524492387 + }, + { + "index":2.9638671875, + "acc_x":5.0918175185, + "acc_y":-1.9453038168, + "acc_z":-3.1302552101, + "gyr_x":41.3928653608, + "gyr_y":-293.207014061, + "gyr_z":22.591675797 + }, + { + "index":2.96875, + "acc_x":5.8396882096, + "acc_y":-2.0681324646, + "acc_z":-2.5578116137, + "gyr_x":26.1423434847, + "gyr_y":-302.1347300356, + "gyr_z":39.5244069914 + }, + { + "index":2.9736328125, + "acc_x":6.7515388228, + "acc_y":-2.1651411664, + "acc_z":-1.9698013081, + "gyr_x":12.7270899142, + "gyr_y":-310.506522966, + "gyr_z":52.6919452391 + }, + { + "index":2.978515625, + "acc_x":7.8082205322, + "acc_y":-2.3015510665, + "acc_z":-1.1567686118, + "gyr_x":0.4762056725, + "gyr_y":-319.3825680983, + "gyr_z":63.7634718351 + }, + { + "index":2.9833984375, + "acc_x":8.9523932299, + "acc_y":-2.47877884, + "acc_z":-0.2682186943, + "gyr_x":-8.4604698432, + "gyr_y":-326.4855633101, + "gyr_z":70.2144996634 + }, + { + "index":2.98828125, + "acc_x":9.7962647672, + "acc_y":-2.4106987554, + "acc_z":0.7733034178, + "gyr_x":-15.63307005, + "gyr_y":-333.0623180262, + "gyr_z":74.1601221306 + }, + { + "index":2.9931640625, + "acc_x":10.3044664843, + "acc_y":-2.4892818356, + "acc_z":1.7592697588, + "gyr_x":-21.2034921879, + "gyr_y":-339.0227404317, + "gyr_z":75.6448954949 + }, + { + "index":2.998046875, + "acc_x":10.6164396157, + "acc_y":-2.5604577893, + "acc_z":2.893928969, + "gyr_x":-24.1340048872, + "gyr_y":-343.4462078558, + "gyr_z":75.7717248223 + }, + { + "index":3.0029296875, + "acc_x":10.662918818, + "acc_y":-2.365740776, + "acc_z":3.8254406911, + "gyr_x":-27.0271785319, + "gyr_y":-347.4263387149, + "gyr_z":75.7573754403 + }, + { + "index":3.0078125, + "acc_x":10.6129770393, + "acc_y":-2.2943917632, + "acc_z":4.5614726182, + "gyr_x":-30.494664167, + "gyr_y":-351.228280306, + "gyr_z":77.3857931833 + }, + { + "index":3.0126953125, + "acc_x":12.696224094, + "acc_y":-2.0326879331, + "acc_z":1.098041619, + "gyr_x":-21.7069377886, + "gyr_y":-355.3225200783, + "gyr_z":72.3999467567 + }, + { + "index":3.017578125, + "acc_x":10.9021395443, + "acc_y":-3.7371386399, + "acc_z":6.7495294796, + "gyr_x":-41.5996677965, + "gyr_y":-362.4052091224, + "gyr_z":100.9733594733 + }, + { + "index":3.0224609375, + "acc_x":10.4185344856, + "acc_y":1.3523975404, + "acc_z":-2.3177485724, + "gyr_x":-67.035695228, + "gyr_y":-359.4598648076, + "gyr_z":63.8747106136 + }, + { + "index":3.02734375, + "acc_x":16.8414296403, + "acc_y":-5.2225829086, + "acc_z":2.2062727093, + "gyr_x":-30.6162207315, + "gyr_y":-364.8639726811, + "gyr_z":68.4114720637 + }, + { + "index":3.0322265625, + "acc_x":6.7600210389, + "acc_y":0.1156015532, + "acc_z":10.4579832968, + "gyr_x":-53.4120047907, + "gyr_y":-370.0936637335, + "gyr_z":111.8618821789 + }, + { + "index":3.037109375, + "acc_x":8.9843283442, + "acc_y":-2.452558712, + "acc_z":9.3340541019, + "gyr_x":-70.5863502105, + "gyr_y":-367.6681374239, + "gyr_z":102.0480995109 + }, + { + "index":3.0419921875, + "acc_x":8.6470730016, + "acc_y":-1.498666777, + "acc_z":10.1059944968, + "gyr_x":-89.3704116779, + "gyr_y":-367.3616881621, + "gyr_z":116.0108564318 + }, + { + "index":3.046875, + "acc_x":8.6430002236, + "acc_y":-1.8926816901, + "acc_z":11.5723516657, + "gyr_x":-100.2058750941, + "gyr_y":-363.8668848821, + "gyr_z":117.0524654978 + }, + { + "index":3.0517578125, + "acc_x":8.0707748223, + "acc_y":-0.8856236843, + "acc_z":13.0505145866, + "gyr_x":-114.4298980744, + "gyr_y":-360.6385128516, + "gyr_z":121.7598427392 + }, + { + "index":3.056640625, + "acc_x":7.6517195233, + "acc_y":-0.0222315889, + "acc_z":14.7291668152, + "gyr_x":-127.6290120889, + "gyr_y":-355.3206744982, + "gyr_z":123.6495512219 + }, + { + "index":3.0615234375, + "acc_x":7.1178172447, + "acc_y":0.8261905248, + "acc_z":16.4402857614, + "gyr_x":-139.4106658502, + "gyr_y":-350.4027118426, + "gyr_z":122.9145470008 + }, + { + "index":3.06640625, + "acc_x":6.5040192858, + "acc_y":1.6990334246, + "acc_z":18.1003935513, + "gyr_x":-151.1021242026, + "gyr_y":-342.6848307021, + "gyr_z":119.084885062 + }, + { + "index":3.0712890625, + "acc_x":5.8071112342, + "acc_y":2.6356338434, + "acc_z":19.9376140726, + "gyr_x":-160.2524556623, + "gyr_y":-334.0752518607, + "gyr_z":112.4166245298 + }, + { + "index":3.076171875, + "acc_x":5.1472638068, + "acc_y":3.7367597393, + "acc_z":21.4757700963, + "gyr_x":-167.543600097, + "gyr_y":-324.3094504015, + "gyr_z":103.8626630755 + }, + { + "index":3.0810546875, + "acc_x":4.1373676007, + "acc_y":4.7775848946, + "acc_z":22.5251212021, + "gyr_x":-172.846719104, + "gyr_y":-315.2123832703, + "gyr_z":92.1157292672 + }, + { + "index":3.0859375, + "acc_x":3.1733508899, + "acc_y":5.9279875209, + "acc_z":23.6588107291, + "gyr_x":-177.2262715339, + "gyr_y":-308.4997363946, + "gyr_z":78.502635834 + }, + { + "index":3.0908203125, + "acc_x":2.3247283262, + "acc_y":6.9601279529, + "acc_z":24.713127063, + "gyr_x":-176.6868693599, + "gyr_y":-304.5392277523, + "gyr_z":65.2960755364 + }, + { + "index":3.095703125, + "acc_x":1.1493983804, + "acc_y":7.6584562721, + "acc_z":25.8844203239, + "gyr_x":-168.3936503365, + "gyr_y":-304.298358717, + "gyr_z":52.5701345492 + }, + { + "index":3.1005859375, + "acc_x":-0.9008840643, + "acc_y":8.0689972503, + "acc_z":25.9263853079, + "gyr_x":-156.449612095, + "gyr_y":-309.206159278, + "gyr_z":45.6530502246 + }, + { + "index":3.10546875, + "acc_x":-3.6434385616, + "acc_y":8.1676233691, + "acc_z":24.6717527654, + "gyr_x":-148.1389744852, + "gyr_y":-320.6773828816, + "gyr_z":43.7623220771 + }, + { + "index":3.1103515625, + "acc_x":-6.431358617, + "acc_y":7.9479628066, + "acc_z":23.4498655057, + "gyr_x":-144.5142194809, + "gyr_y":-336.9405394258, + "gyr_z":47.7463438419 + }, + { + "index":3.115234375, + "acc_x":-8.9460154279, + "acc_y":8.1179778685, + "acc_z":22.8347228745, + "gyr_x":-145.3977539567, + "gyr_y":-355.74953579, + "gyr_z":54.8374970098 + }, + { + "index":3.1201171875, + "acc_x":-10.9076006252, + "acc_y":8.4776741829, + "acc_z":22.9468784383, + "gyr_x":-146.2035237504, + "gyr_y":-372.3661829816, + "gyr_z":62.7359955173 + }, + { + "index":3.125, + "acc_x":-13.0959967271, + "acc_y":8.9269866817, + "acc_z":23.3523766164, + "gyr_x":-146.4972867715, + "gyr_y":-386.1349060064, + "gyr_z":70.7775340388 + }, + { + "index":3.1298828125, + "acc_x":-15.8423313933, + "acc_y":9.6835450436, + "acc_z":23.750948444, + "gyr_x":-144.7314665296, + "gyr_y":-392.8093962436, + "gyr_z":76.5660895889 + }, + { + "index":3.134765625, + "acc_x":-18.9849523208, + "acc_y":10.3953494586, + "acc_z":23.6752788485, + "gyr_x":-143.2992785354, + "gyr_y":-393.8581161637, + "gyr_z":78.8960663203 + }, + { + "index":3.1396484375, + "acc_x":-22.1377000514, + "acc_y":10.8706878686, + "acc_z":23.2624093219, + "gyr_x":-138.8366968749, + "gyr_y":-384.9356732297, + "gyr_z":77.2217030975 + }, + { + "index":3.14453125, + "acc_x":-24.9832851553, + "acc_y":10.6515954195, + "acc_z":22.272721518, + "gyr_x":-131.1379250296, + "gyr_y":-368.2411627725, + "gyr_z":72.6355838522 + }, + { + "index":3.1494140625, + "acc_x":-27.3852465419, + "acc_y":10.3072315738, + "acc_z":20.7680648245, + "gyr_x":-119.0135402864, + "gyr_y":-346.6800502041, + "gyr_z":64.6282898186 + }, + { + "index":3.154296875, + "acc_x":-29.5142382222, + "acc_y":9.8684046493, + "acc_z":18.3028345956, + "gyr_x":-104.4881655086, + "gyr_y":-320.3085445003, + "gyr_z":56.5759618616 + }, + { + "index":3.1591796875, + "acc_x":-31.2465571939, + "acc_y":9.1572347707, + "acc_z":15.5675883711, + "gyr_x":-89.2504745482, + "gyr_y":-291.0918122018, + "gyr_z":48.5798579405 + }, + { + "index":3.1640625, + "acc_x":-32.8789509861, + "acc_y":8.2988566138, + "acc_z":12.6349829127, + "gyr_x":-74.6343065636, + "gyr_y":-262.3643502771, + "gyr_z":41.8730034456 + }, + { + "index":3.1689453125, + "acc_x":-34.6189713948, + "acc_y":7.2209215042, + "acc_z":9.9812940722, + "gyr_x":-62.3033418987, + "gyr_y":-233.7348997393, + "gyr_z":35.0646691237 + }, + { + "index":3.173828125, + "acc_x":-36.2267477338, + "acc_y":6.6493348696, + "acc_z":6.7975751749, + "gyr_x":-57.0498281772, + "gyr_y":-201.8118981775, + "gyr_z":27.7830395499 + }, + { + "index":3.1787109375, + "acc_x":-37.3177069634, + "acc_y":6.0446581126, + "acc_z":3.6955780505, + "gyr_x":-59.5870263077, + "gyr_y":-167.6816607409, + "gyr_z":19.1408417337 + }, + { + "index":3.18359375, + "acc_x":-37.9935246552, + "acc_y":5.9200143072, + "acc_z":0.5107588833, + "gyr_x":-69.9194840582, + "gyr_y":-131.7816213941, + "gyr_z":6.4751114909 + }, + { + "index":3.1884765625, + "acc_x":-38.1684703251, + "acc_y":6.35036428, + "acc_z":-2.1715241192, + "gyr_x":-83.1607671731, + "gyr_y":-99.2031421294, + "gyr_z":-6.2754348903 + }, + { + "index":3.193359375, + "acc_x":-37.7585921368, + "acc_y":7.2603137121, + "acc_z":-4.0334592981, + "gyr_x":-94.5954643876, + "gyr_y":-71.2387950835, + "gyr_z":-18.3737815301 + }, + { + "index":3.1982421875, + "acc_x":-37.2347425147, + "acc_y":8.7360228746, + "acc_z":-4.6349370751, + "gyr_x":-99.921286529, + "gyr_y":-48.8807134276, + "gyr_z":-29.6667066248 + }, + { + "index":3.203125, + "acc_x":-36.7917180913, + "acc_y":10.3026330658, + "acc_z":-4.3326367634, + "gyr_x":-96.9893021157, + "gyr_y":-30.158089261, + "gyr_z":-40.310749564 + }, + { + "index":3.2080078125, + "acc_x":-36.3827911167, + "acc_y":11.4348156619, + "acc_z":-3.2618944835, + "gyr_x":-84.6629200522, + "gyr_y":-14.7250861469, + "gyr_z":-47.5192280474 + }, + { + "index":3.212890625, + "acc_x":-35.7203077467, + "acc_y":12.3263947857, + "acc_z":-1.4778634047, + "gyr_x":-59.1934476172, + "gyr_y":-0.93337753, + "gyr_z":-51.5704444749 + }, + { + "index":3.2177734375, + "acc_x":-82.9227086917, + "acc_y":69.5165836621, + "acc_z":125.2518948823, + "gyr_x":172.7470884109, + "gyr_y":233.2584288702, + "gyr_z":15.707396062 + }, + { + "index":3.22265625, + "acc_x":-28.7825754175, + "acc_y":29.0754254928, + "acc_z":-33.6992224627, + "gyr_x":134.3674018804, + "gyr_y":155.224533969, + "gyr_z":-171.5073379157 + }, + { + "index":3.2275390625, + "acc_x":-46.6309368553, + "acc_y":-9.1353344978, + "acc_z":5.0065503876, + "gyr_x":161.9689897849, + "gyr_y":273.5447543352, + "gyr_z":-72.1056392696 + }, + { + "index":3.232421875, + "acc_x":-28.7759002421, + "acc_y":-4.7836665465, + "acc_z":17.202515591, + "gyr_x":159.2737224275, + "gyr_y":303.5206379628, + "gyr_z":28.7299989722 + }, + { + "index":3.2373046875, + "acc_x":9.0082132018, + "acc_y":-8.4419234712, + "acc_z":22.1767207637, + "gyr_x":202.5267852722, + "gyr_y":302.5212933801, + "gyr_z":11.5930185896 + }, + { + "index":3.2421875, + "acc_x":6.0674302023, + "acc_y":8.3124816375, + "acc_z":22.4488295145, + "gyr_x":283.3389903003, + "gyr_y":306.9228165304, + "gyr_z":12.6402729684 + }, + { + "index":3.2470703125, + "acc_x":46.1154545948, + "acc_y":30.679439822, + "acc_z":-29.846323554, + "gyr_x":365.2149782483, + "gyr_y":328.5942192124, + "gyr_z":-176.9922585292 + }, + { + "index":3.251953125, + "acc_x":10.4338054837, + "acc_y":21.3318018947, + "acc_z":-22.3507582203, + "gyr_x":137.8127776809, + "gyr_y":325.1983581482, + "gyr_z":-190.7305214898 + }, + { + "index":3.2568359375, + "acc_x":-1.1442982651, + "acc_y":9.7739452002, + "acc_z":4.1679266619, + "gyr_x":-44.0670692977, + "gyr_y":318.5682543054, + "gyr_z":-223.00149886 + }, + { + "index":3.26171875, + "acc_x":-14.1586977018, + "acc_y":10.2883341517, + "acc_z":10.5261198862, + "gyr_x":-80.771227929, + "gyr_y":307.7176515824, + "gyr_z":-168.2983413248 + }, + { + "index":3.2666015625, + "acc_x":-19.6649471114, + "acc_y":4.8752038065, + "acc_z":25.7350118423, + "gyr_x":-26.4169836486, + "gyr_y":267.8623870514, + "gyr_z":-165.6788843575 + }, + { + "index":3.271484375, + "acc_x":-14.4270388175, + "acc_y":4.1331444609, + "acc_z":21.8146091791, + "gyr_x":40.5562199767, + "gyr_y":216.1708148216, + "gyr_z":-169.2754458483 + }, + { + "index":3.2763671875, + "acc_x":-9.1751060297, + "acc_y":6.3509093173, + "acc_z":13.4062655916, + "gyr_x":55.8116567088, + "gyr_y":195.3484150757, + "gyr_z":-119.6300060925 + }, + { + "index":3.28125, + "acc_x":-6.8793983741, + "acc_y":6.9865064599, + "acc_z":9.2865711361, + "gyr_x":51.0938088053, + "gyr_y":188.4802796134, + "gyr_z":-65.8289712529 + }, + { + "index":3.2861328125, + "acc_x":-6.1016892957, + "acc_y":7.4297561154, + "acc_z":9.60350574, + "gyr_x":15.6389662756, + "gyr_y":168.2249851628, + "gyr_z":-35.0786114196 + }, + { + "index":3.291015625, + "acc_x":-4.0314877419, + "acc_y":3.8596449908, + "acc_z":13.0358470007, + "gyr_x":11.3864684, + "gyr_y":145.1648034609, + "gyr_z":-33.5460767576 + }, + { + "index":3.2958984375, + "acc_x":0.3057009882, + "acc_y":1.5285869523, + "acc_z":11.641442917, + "gyr_x":24.7800980582, + "gyr_y":134.0436860826, + "gyr_z":-48.310902233 + }, + { + "index":3.30078125, + "acc_x":-0.3806836274, + "acc_y":2.8754471777, + "acc_z":10.8859601858, + "gyr_x":31.0862378718, + "gyr_y":129.3154040045, + "gyr_z":-43.9955238955 + }, + { + "index":3.3056640625, + "acc_x":0.2686883088, + "acc_y":2.0772842179, + "acc_z":10.451363501, + "gyr_x":30.012142195, + "gyr_y":122.4741997042, + "gyr_z":-41.7321537682 + }, + { + "index":3.310546875, + "acc_x":-0.5420514056, + "acc_y":2.6538803591, + "acc_z":10.8887839306, + "gyr_x":31.2230526198, + "gyr_y":115.3658099919, + "gyr_z":-41.2351271296 + }, + { + "index":3.3154296875, + "acc_x":-0.3508964613, + "acc_y":2.943311345, + "acc_z":10.294818656, + "gyr_x":38.0778641549, + "gyr_y":112.050366901, + "gyr_z":-35.3056555015 + }, + { + "index":3.3203125, + "acc_x":-0.7544413136, + "acc_y":3.9019546815, + "acc_z":10.0721906737, + "gyr_x":37.0022737105, + "gyr_y":111.6005274531, + "gyr_z":-32.2605542306 + }, + { + "index":3.3251953125, + "acc_x":-16.1133929808, + "acc_y":5.5497904702, + "acc_z":20.0275605819, + "gyr_x":31.9516464528, + "gyr_y":38.2300907916, + "gyr_z":-26.0179205417 + }, + { + "index":3.330078125, + "acc_x":-4.2780892378, + "acc_y":1.2721308444, + "acc_z":11.1371959933, + "gyr_x":19.8179285368, + "gyr_y":23.1252439542, + "gyr_z":-10.5161387628 + }, + { + "index":3.3349609375, + "acc_x":-0.6223952805, + "acc_y":1.6580098667, + "acc_z":11.6700326138, + "gyr_x":21.6833061009, + "gyr_y":10.837335758, + "gyr_z":-18.5148995187 + }, + { + "index":3.33984375, + "acc_x":-0.0427232014, + "acc_y":3.3847652833, + "acc_z":8.7218009592, + "gyr_x":24.7184136661, + "gyr_y":5.6779138362, + "gyr_z":-16.4835924138 + }, + { + "index":3.3447265625, + "acc_x":1.8098472754, + "acc_y":1.1996022169, + "acc_z":10.5590398693, + "gyr_x":20.4993919299, + "gyr_y":3.9881414604, + "gyr_z":-23.4703341258 + }, + { + "index":3.349609375, + "acc_x":0.7632770817, + "acc_y":1.9933566034, + "acc_z":9.9570397443, + "gyr_x":27.9018117568, + "gyr_y":6.8024415261, + "gyr_z":-28.5906718212 + }, + { + "index":3.3544921875, + "acc_x":-0.0141410933, + "acc_y":2.951402437, + "acc_z":9.1966364868, + "gyr_x":27.5790762295, + "gyr_y":6.6980946277, + "gyr_z":-23.3096255794 + }, + { + "index":3.359375, + "acc_x":0.2653205493, + "acc_y":2.4786746054, + "acc_z":9.4443613448, + "gyr_x":21.9489400515, + "gyr_y":5.6419870459, + "gyr_z":-23.4025036172 + }, + { + "index":3.3642578125, + "acc_x":0.6647521095, + "acc_y":2.15465214, + "acc_z":9.9206520202, + "gyr_x":23.7099211257, + "gyr_y":4.1766386171, + "gyr_z":-22.8610113499 + }, + { + "index":3.369140625, + "acc_x":0.7437504501, + "acc_y":2.1972009981, + "acc_z":9.686851159, + "gyr_x":26.4337787413, + "gyr_y":3.1267991425, + "gyr_z":-24.2972282873 + }, + { + "index":3.3740234375, + "acc_x":0.4139479004, + "acc_y":2.4410620175, + "acc_z":9.4894407329, + "gyr_x":25.8220315513, + "gyr_y":3.1671320263, + "gyr_z":-24.179399333 + }, + { + "index":3.37890625, + "acc_x":0.4353873359, + "acc_y":2.6037776469, + "acc_z":9.4875052259, + "gyr_x":24.502783544, + "gyr_y":2.3859659615, + "gyr_z":-23.3528602616 + }, + { + "index":3.3837890625, + "acc_x":0.4671733765, + "acc_y":2.5663474929, + "acc_z":9.4402326843, + "gyr_x":23.3681400981, + "gyr_y":1.5655843465, + "gyr_z":-23.36822776 + }, + { + "index":3.388671875, + "acc_x":0.4952999481, + "acc_y":2.6372575464, + "acc_z":9.4278273858, + "gyr_x":21.7255971531, + "gyr_y":1.3940599242, + "gyr_z":-22.8964581641 + }, + { + "index":3.3935546875, + "acc_x":0.62544412, + "acc_y":2.6516690545, + "acc_z":9.3819312861, + "gyr_x":18.8161560775, + "gyr_y":0.6372443304, + "gyr_z":-21.8991180718 + }, + { + "index":3.3984375, + "acc_x":0.6068634407, + "acc_y":2.6818436154, + "acc_z":9.2480159771, + "gyr_x":17.6476693595, + "gyr_y":0.8609190075, + "gyr_z":-19.5572892494 + }, + { + "index":3.4033203125, + "acc_x":0.5650772099, + "acc_y":2.884481248, + "acc_z":9.2034403171, + "gyr_x":15.3160284172, + "gyr_y":1.2080535036, + "gyr_z":-15.8353350878 + }, + { + "index":3.408203125, + "acc_x":0.6310531437, + "acc_y":2.8874669219, + "acc_z":9.3277819482, + "gyr_x":11.6512332309, + "gyr_y":1.2295775859, + "gyr_z":-12.244398712 + }, + { + "index":3.4130859375, + "acc_x":0.7129494445, + "acc_y":2.8886659379, + "acc_z":9.0636722957, + "gyr_x":9.9347435245, + "gyr_y":1.33414789, + "gyr_z":-8.28453116 + }, + { + "index":3.41796875, + "acc_x":0.6496416653, + "acc_y":3.143510145, + "acc_z":9.0621329107, + "gyr_x":5.7716183577, + "gyr_y":2.036805526, + "gyr_z":-5.3988313984 + }, + { + "index":3.4228515625, + "acc_x":0.652690514, + "acc_y":2.7061653309, + "acc_z":9.4070225649, + "gyr_x":3.1557969838, + "gyr_y":1.4734758123, + "gyr_z":-2.0577463116 + }, + { + "index":3.427734375, + "acc_x":0.7296602823, + "acc_y":2.8750863396, + "acc_z":9.1447207653, + "gyr_x":0.650360844, + "gyr_y":1.7950357524, + "gyr_z":-1.8770693582 + }, + { + "index":3.4326171875, + "acc_x":0.8036540327, + "acc_y":2.7259329477, + "acc_z":9.4863520536, + "gyr_x":-1.1158132023, + "gyr_y":1.5922116326, + "gyr_z":-0.0253067643 + }, + { + "index":3.4375, + "acc_x":0.9687987579, + "acc_y":2.6608333066, + "acc_z":9.4333251015, + "gyr_x":-1.5966116856, + "gyr_y":1.2327786293, + "gyr_z":-0.455659563 + }, + { + "index":3.4423828125, + "acc_x":1.0045664497, + "acc_y":2.5983205295, + "acc_z":9.3885504751, + "gyr_x":-2.8226628347, + "gyr_y":1.6506467091, + "gyr_z":-0.8090281945 + }, + { + "index":3.447265625, + "acc_x":0.8827473733, + "acc_y":2.5267815869, + "acc_z":9.4134913799, + "gyr_x":-2.5742821416, + "gyr_y":1.4321790613, + "gyr_z":-1.5343779808 + }, + { + "index":3.4521484375, + "acc_x":0.9077202474, + "acc_y":2.5309600823, + "acc_z":9.4896784693, + "gyr_x":-2.6329463585, + "gyr_y":1.3166674198, + "gyr_z":-1.7774130649 + }, + { + "index":3.45703125, + "acc_x":0.7321656511, + "acc_y":2.5661359465, + "acc_z":9.4067760168, + "gyr_x":-2.3978178006, + "gyr_y":1.7891488254, + "gyr_z":-1.1653989011 + } + ] +} \ No newline at end of file diff --git a/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py b/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py index df760686..b3237189 100644 --- a/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py +++ b/tests/test_trajectory_reconstruction/test_orientation_methods/test_ori_method_mixin.py @@ -78,4 +78,7 @@ def test_single_stride_regression(self, healthy_example_imu_data, healthy_exampl test.estimate(data, sampling_rate_hz=fs) snapshot.assert_match(test.orientation_, test.__class__.__name__) - snapshot.assert_match(test.rotated_data_, f"{test.__class__.__name__}_rotated_data") + rotated_data = test.rotated_data_ + # Workaround as snapshot utils don't support named columns + rotated_data.columns.name = None + snapshot.assert_match(rotated_data, f"{test.__class__.__name__}_rotated_data") From d4058c25e66dc34884bb23a906f62bea956dffd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20K=C3=BCderle?= Date: Fri, 19 Apr 2024 10:47:03 +0200 Subject: [PATCH 3/4] Update to new linting rules --- .ruff.toml | 2 +- docs/conf.py | 4 +- examples/advanced_features/multi_process.py | 4 +- .../datasets_and_pipelines/custom_dataset.py | 4 +- .../_event_detection_mixin.py | 18 ++++----- gaitmap/base.py | 14 +++---- gaitmap/data_transform/_base.py | 19 +++++----- gaitmap/data_transform/_filter.py | 6 +-- gaitmap/data_transform/_scaler.py | 17 +++++---- gaitmap/evaluation_utils/event_detection.py | 4 +- gaitmap/evaluation_utils/parameter_errors.py | 16 ++++---- gaitmap/evaluation_utils/scores.py | 22 +++++------ .../evaluation_utils/stride_segmentation.py | 13 ++++--- .../_herzer_event_detection.py | 20 +++++----- gaitmap/parameters/_spatial_parameters.py | 9 +++-- gaitmap/parameters/_temporal_parameters.py | 10 ++--- .../sensor_alignment/_gravity_alignment.py | 4 +- .../sensor_alignment/_pca_alignment.py | 7 ++-- .../_roi_stride_segmentation.py | 12 +++--- gaitmap/stride_segmentation/_utils.py | 4 +- .../_region_level_trajectory.py | 10 ++--- .../_trajectory_wrapper.py | 13 ++++--- .../trajectory_methods/_kalman_numba_funcs.py | 8 ++-- gaitmap/utils/_datatype_validation_helper.py | 11 +++--- gaitmap/utils/_types.py | 3 +- gaitmap/utils/array_handling.py | 11 +++--- gaitmap/utils/coordinate_conversion.py | 6 +-- gaitmap/utils/datatype_helper.py | 25 ++++++------ gaitmap/utils/rotations.py | 10 ++--- gaitmap/utils/static_moment_detection.py | 11 +++--- gaitmap/utils/stride_list_conversion.py | 8 ++-- .../zupt_detection/_combo_zupt_detector.py | 6 +-- .../_moving_window_zupt_detector.py | 4 +- .../_filtered_rampp_event_detection.py | 8 ++-- .../event_detection/_rampp_event_detection.py | 20 +++++----- .../_ullrich_gait_sequence_detection.py | 16 ++++---- .../_forward_direction_alignment.py | 10 ++--- .../stride_segmentation/dtw/_barth_dtw.py | 8 ++-- .../stride_segmentation/dtw/_base_dtw.py | 38 +++++++++---------- .../dtw/_constrained_barth_dtw.py | 4 +- .../dtw/_dtw_templates/templates.py | 11 +++--- .../hmm/_hmm_feature_transform.py | 10 ++--- .../hmm/_hmm_stride_segmentation.py | 21 +++++----- .../hmm/_segmentation_model.py | 17 +++++---- .../stride_segmentation/hmm/_simple_model.py | 9 +++-- .../stride_segmentation/hmm/_utils.py | 28 +++++++------- tests/conftest.py | 4 +- tests/test_base.py | 6 +-- .../test_feature_transformer.py | 8 ++-- .../test_stride_segmentation/test_base_dtw.py | 4 +- .../test_roi_stride_segmentation.py | 4 +- .../test_trajectory_wrapper.py | 6 +-- .../test_moving_window_zupt_detector.py | 4 +- 53 files changed, 292 insertions(+), 279 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index 00c568a7..28123dae 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,5 +1,5 @@ line-length = 120 -target-version = "py38" +target-version = "py39" [lint] select = [ diff --git a/docs/conf.py b/docs/conf.py index d7f5919e..1d15d9c8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ from datetime import datetime from inspect import getsourcefile from pathlib import Path -from typing import List, Optional +from typing import Optional import toml from sphinx_gallery.sorting import ExplicitOrder @@ -245,7 +245,7 @@ def skip_properties(app, what, name, obj, skip, options) -> Optional[bool]: """ -def add_info_about_origin(app, what, name, obj, options, lines: List[str]) -> None: +def add_info_about_origin(app, what, name, obj, options, lines: list[str]) -> None: """Add a short info text to all algorithms that are only available via gaitmap_mad.""" if what != "class": return diff --git a/examples/advanced_features/multi_process.py b/examples/advanced_features/multi_process.py index d9de6ddc..b47a2d85 100644 --- a/examples/advanced_features/multi_process.py +++ b/examples/advanced_features/multi_process.py @@ -32,7 +32,7 @@ """ from pprint import pprint -from typing import Any, Dict +from typing import Any # %% # Load some example data @@ -70,7 +70,7 @@ # This could be further optimized by using a read-only shared memory object for the data. -def run(dtw: BarthDtw, parameter: Dict[str, Any]) -> BarthDtw: +def run(dtw: BarthDtw, parameter: dict[str, Any]) -> BarthDtw: # For this run, change the parameters on the dtw object dtw = dtw.set_params(**parameter) dtw = dtw.segment(data=bf_data, sampling_rate_hz=sampling_rate_hz) diff --git a/examples/datasets_and_pipelines/custom_dataset.py b/examples/datasets_and_pipelines/custom_dataset.py index c4dbe426..3a43c9a7 100644 --- a/examples/datasets_and_pipelines/custom_dataset.py +++ b/examples/datasets_and_pipelines/custom_dataset.py @@ -40,7 +40,7 @@ # Then you can filter the dataset first and load the data once you know which data-points you want to access. # We will discuss this later in the example. from itertools import product -from typing import List, Optional, Union +from typing import Optional, Union import pandas as pd @@ -329,7 +329,7 @@ def __init__( data_folder: str, custom_config_para: bool = False, *, - groupby_cols: Optional[Union[List[str], str]] = None, + groupby_cols: Optional[Union[list[str], str]] = None, subset_index: Optional[pd.DataFrame] = None, ) -> None: self.data_folder = data_folder diff --git a/gaitmap/_event_detection_common/_event_detection_mixin.py b/gaitmap/_event_detection_common/_event_detection_mixin.py index c96555a7..9f93043e 100644 --- a/gaitmap/_event_detection_common/_event_detection_mixin.py +++ b/gaitmap/_event_detection_common/_event_detection_mixin.py @@ -1,6 +1,6 @@ """Mixin for event detection algorithms that work similar to Rampp et al.""" -from typing import Any, Callable, Dict, Optional, Tuple, Union +from typing import Any, Callable, Optional, Union import numpy as np import pandas as pd @@ -30,10 +30,10 @@ class _EventDetectionMixin: memory: Optional[Memory] enforce_consistency: bool - detect_only: Optional[Tuple[str, ...]] + detect_only: Optional[tuple[str, ...]] - min_vel_event_list_: Optional[Union[pd.DataFrame, Dict[str, pd.DataFrame]]] - segmented_event_list_: Optional[Union[pd.DataFrame, Dict[str, pd.DataFrame]]] + min_vel_event_list_: Optional[Union[pd.DataFrame, dict[str, pd.DataFrame]]] + segmented_event_list_: Optional[Union[pd.DataFrame, dict[str, pd.DataFrame]]] data: SensorData sampling_rate_hz: float @@ -43,7 +43,7 @@ def __init__( self, memory: Optional[Memory] = None, enforce_consistency: bool = True, - detect_only: Optional[Tuple[str, ...]] = None, + detect_only: Optional[tuple[str, ...]] = None, ) -> None: self.memory = memory self.enforce_consistency = enforce_consistency @@ -85,7 +85,7 @@ def detect(self, data: SensorData, stride_list: StrideList, *, sampling_rate_hz: if dataset_type == "single": results = self._detect_single_dataset(data, stride_list, detect_kwargs=detect_kwargs, memory=self.memory) else: - results_dict: Dict[_Hashable, Dict[str, pd.DataFrame]] = {} + results_dict: dict[_Hashable, dict[str, pd.DataFrame]] = {} for sensor in get_multi_sensor_names(data): results_dict[sensor] = self._detect_single_dataset( data[sensor], @@ -104,9 +104,9 @@ def _detect_single_dataset( self, data: pd.DataFrame, stride_list: pd.DataFrame, - detect_kwargs: Dict[str, Any], + detect_kwargs: dict[str, Any], memory: Memory, - ) -> Dict[str, pd.DataFrame]: + ) -> dict[str, pd.DataFrame]: """Detect gait events for a single sensor data set and put into correct output stride list.""" if memory is None: memory = Memory(None) @@ -168,7 +168,7 @@ def _select_all_event_detection_method(self) -> Callable: """ raise NotImplementedError() - def _get_detect_kwargs(self) -> Dict[str, Any]: + def _get_detect_kwargs(self) -> dict[str, Any]: """Return a dictionary of keyword arguments that should be passed to the detect method. This is a separate method to make it easy to overwrite by a subclass. diff --git a/gaitmap/base.py b/gaitmap/base.py index 886b4acf..d1bab679 100644 --- a/gaitmap/base.py +++ b/gaitmap/base.py @@ -2,7 +2,7 @@ import json import warnings -from typing import Any, Dict, Optional, Type, TypeVar, Union +from typing import Any, Optional, TypeVar, Union import numpy as np import pandas as pd @@ -115,27 +115,27 @@ def _custom_deserialize(json_obj): # pylint: disable=too-many-return-statements class _BaseSerializable(tpcp.BaseTpcpObject): @classmethod - def _get_subclasses(cls: Type[Self]): + def _get_subclasses(cls: type[Self]): for subclass in cls.__subclasses__(): yield from subclass._get_subclasses() yield subclass @classmethod - def _find_subclass(cls: Type[Self], name: str) -> Type[Self]: + def _find_subclass(cls: type[Self], name: str) -> type[Self]: for subclass in _BaseSerializable._get_subclasses(): if subclass.__name__ == name: return subclass raise ValueError(f"No algorithm class with name {name} exists") @classmethod - def _from_json_dict(cls: Type[Self], json_dict: Dict) -> Self: + def _from_json_dict(cls: type[Self], json_dict: dict) -> Self: params = json_dict["params"] input_data = {k: params[k] for k in tpcp.get_param_names(cls) if k in params} instance = cls(**input_data) return instance - def _to_json_dict(self) -> Dict[str, Any]: - json_dict: Dict[str, Union[str, Dict[str, Any]]] = { + def _to_json_dict(self) -> dict[str, Any]: + json_dict: dict[str, Union[str, dict[str, Any]]] = { "_gaitmap_obj": self.__class__.__name__, "params": self.get_params(deep=False), } @@ -155,7 +155,7 @@ def to_json(self) -> str: return json.dumps(final_dict, indent=4, cls=_CustomEncoder) @classmethod - def from_json(cls: Type[Self], json_str: str) -> Self: + def from_json(cls: type[Self], json_str: str) -> Self: """Import an gaitmap object from its json representation. For details have a look at the this :ref:`example `. diff --git a/gaitmap/data_transform/_base.py b/gaitmap/data_transform/_base.py index aee0b18a..9511df1c 100644 --- a/gaitmap/data_transform/_base.py +++ b/gaitmap/data_transform/_base.py @@ -1,8 +1,9 @@ """Basic transformers for higher level functionality.""" +from collections.abc import Sequence from copy import copy from functools import reduce -from typing import List, Sequence, Set, Tuple, Union +from typing import Union import pandas as pd from tpcp import OptimizableParameter, PureParameter @@ -90,14 +91,14 @@ class GroupedTransformer(BaseTransformer, TrainableTransformerMixin): """ - transformer_mapping: OptimizableParameter[List[Tuple[Union[_Hashable, Tuple[_Hashable, ...]], BaseTransformer]]] + transformer_mapping: OptimizableParameter[list[tuple[Union[_Hashable, tuple[_Hashable, ...]], BaseTransformer]]] keep_all_cols: PureParameter[bool] data: SingleSensorData def __init__( self, - transformer_mapping: List[Tuple[Union[_Hashable, Tuple[_Hashable, ...]], BaseTransformer]], + transformer_mapping: list[tuple[Union[_Hashable, tuple[_Hashable, ...]], BaseTransformer]], keep_all_cols: bool = True, ) -> None: self.transformer_mapping = transformer_mapping @@ -181,7 +182,7 @@ def transform(self, data: SingleSensorData, **kwargs) -> Self: self.transformed_data_ = pd.concat(results, axis=1)[sorted(mapped_cols, key=list(data.columns).index)] return self - def _validate_mapping(self) -> Set[_Hashable]: + def _validate_mapping(self) -> set[_Hashable]: # Check that each column is only mentioned once: unique_k = [] for k, _ in self.transformer_mapping: @@ -196,7 +197,7 @@ def _validate_mapping(self) -> Set[_Hashable]: unique_k.append(i) return set(unique_k) - def _validate(self, data: SingleSensorData, selected_cols: Set[_Hashable]) -> None: + def _validate(self, data: SingleSensorData, selected_cols: set[_Hashable]) -> None: if not set(data.columns).issuperset(selected_cols): raise ValueError("You specified transformations for columns that do not exist. This is not supported!") @@ -237,9 +238,9 @@ class ChainedTransformer(BaseTransformer, TrainableTransformerMixin): _composite_params = ("chain",) - chain: OptimizableParameter[List[Tuple[_Hashable, BaseTransformer]]] + chain: OptimizableParameter[list[tuple[_Hashable, BaseTransformer]]] - def __init__(self, chain: List[Tuple[_Hashable, BaseTransformer]]) -> None: + def __init__(self, chain: list[tuple[_Hashable, BaseTransformer]]) -> None: self.chain = chain def self_optimize(self, data: Sequence[SingleSensorData], **kwargs) -> Self: @@ -332,9 +333,9 @@ class ParallelTransformer(BaseTransformer, TrainableTransformerMixin): _composite_params = ("transformers",) - transformers: OptimizableParameter[List[Tuple[_Hashable, BaseTransformer]]] + transformers: OptimizableParameter[list[tuple[_Hashable, BaseTransformer]]] - def __init__(self, transformers: List[Tuple[_Hashable, BaseTransformer]]) -> None: + def __init__(self, transformers: list[tuple[_Hashable, BaseTransformer]]) -> None: self.transformers = transformers def self_optimize(self, data: Sequence[SingleSensorData], **kwargs) -> Self: diff --git a/gaitmap/data_transform/_filter.py b/gaitmap/data_transform/_filter.py index b8761712..6861d4d3 100644 --- a/gaitmap/data_transform/_filter.py +++ b/gaitmap/data_transform/_filter.py @@ -1,6 +1,6 @@ """A set of filters that can be applied to data.""" -from typing import Literal, Optional, Tuple, Union +from typing import Literal, Optional, Union import pandas as pd from scipy.signal import butter, sosfiltfilt @@ -73,7 +73,7 @@ class ButterworthFilter(BaseFilter): """ order: int - cutoff_freq_hz: Union[float, Tuple[float, float]] + cutoff_freq_hz: Union[float, tuple[float, float]] filter_type: Literal["lowpass", "highpass", "bandpass", "bandstop"] sampling_rate_hz: float @@ -81,7 +81,7 @@ class ButterworthFilter(BaseFilter): def __init__( self, order: int, - cutoff_freq_hz: Union[float, Tuple[float, float]], + cutoff_freq_hz: Union[float, tuple[float, float]], filter_type: Literal["lowpass", "highpass", "bandpass", "bandstop"] = "lowpass", ) -> None: self.order = order diff --git a/gaitmap/data_transform/_scaler.py b/gaitmap/data_transform/_scaler.py index 6c5570ec..fb13513c 100644 --- a/gaitmap/data_transform/_scaler.py +++ b/gaitmap/data_transform/_scaler.py @@ -1,6 +1,7 @@ """Transformers that scale data to certain data ranges.""" -from typing import Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import Optional import numpy as np from tpcp import OptimizableParameter, Parameter @@ -390,11 +391,11 @@ class MinMaxScaler(BaseTransformer): """ - out_range: Parameter[Tuple[float, float]] + out_range: Parameter[tuple[float, float]] def __init__( self, - out_range: Tuple[float, float] = (0, 1.0), + out_range: tuple[float, float] = (0, 1.0), ) -> None: self.out_range = out_range @@ -420,13 +421,13 @@ def transform(self, data: SingleSensorData, **_) -> Self: self.transformed_data_ = self._transform(data, data_range) return self - def _calc_data_range(self, data: SensorData) -> Tuple[float, float]: + def _calc_data_range(self, data: SensorData) -> tuple[float, float]: is_single_sensor_data(data, check_gyr=False, check_acc=False, raise_exception=True) # We calculate the global min and max over all rows and columns! data = data.to_numpy() return float(np.nanmin(data)), float(np.nanmax(data)) - def _transform(self, data: SingleSensorData, data_range: Tuple[float, float]) -> SingleSensorData: + def _transform(self, data: SingleSensorData, data_range: tuple[float, float]) -> SingleSensorData: data = data.copy() feature_range = self.out_range data_min, data_max = data_range @@ -483,12 +484,12 @@ class TrainableMinMaxScaler(MinMaxScaler, TrainableTransformerMixin): """ - data_range: OptimizableParameter[Optional[Tuple[float, float]]] + data_range: OptimizableParameter[Optional[tuple[float, float]]] def __init__( self, - out_range: Tuple[float, float] = (0, 1.0), - data_range: Optional[Tuple[float, float]] = None, + out_range: tuple[float, float] = (0, 1.0), + data_range: Optional[tuple[float, float]] = None, ) -> None: self.data_range = data_range super().__init__(out_range=out_range) diff --git a/gaitmap/evaluation_utils/event_detection.py b/gaitmap/evaluation_utils/event_detection.py index da5cb919..47e35408 100644 --- a/gaitmap/evaluation_utils/event_detection.py +++ b/gaitmap/evaluation_utils/event_detection.py @@ -1,6 +1,6 @@ """A set of helper functions to evaluate the output of an event stride segmentation against ground truth.""" -from typing import Dict, Union +from typing import Union from pandas import DataFrame from typing_extensions import Literal @@ -19,7 +19,7 @@ def evaluate_stride_event_list( one_to_one: bool = True, stride_list_postfix: str = "", ground_truth_postfix: str = "_ground_truth", -) -> Union[DataFrame, Dict[_Hashable, DataFrame]]: +) -> Union[DataFrame, dict[_Hashable, DataFrame]]: """Find True Positives, False Positives and True Negatives by comparing an stride event list with ground truth. This compares a stride event list with a ground truth stride event list and returns True Positives, diff --git a/gaitmap/evaluation_utils/parameter_errors.py b/gaitmap/evaluation_utils/parameter_errors.py index 049b987b..06195f7c 100644 --- a/gaitmap/evaluation_utils/parameter_errors.py +++ b/gaitmap/evaluation_utils/parameter_errors.py @@ -1,7 +1,7 @@ """A helper function to evaluate the output of the temporal or spatial parameter calculation against a ground truth.""" import warnings -from typing import Dict, Literal, Tuple, Union +from typing import Literal, Union import numpy as np import pandas as pd @@ -15,10 +15,10 @@ def calculate_parameter_errors( *, - reference_parameter: Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]], - predicted_parameter: Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]], + reference_parameter: Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]], + predicted_parameter: Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]], id_column: str = "s_id", -) -> Tuple[Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]], Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]]]: +) -> tuple[Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]], Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]]]: """Calculate the error per row between a parameter predicted and a given ground truth. We calculate four different groups of errors: @@ -96,8 +96,8 @@ def calculate_parameter_errors( def calculate_aggregated_parameter_errors( *, - reference_parameter: Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]], - predicted_parameter: Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]], + reference_parameter: Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]], + predicted_parameter: Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]], calculate_per_sensor: bool = True, scoring_errors: Literal["ignore", "warn", "raise"] = "warn", id_column: str = "s_id", @@ -496,7 +496,7 @@ def _align_parameters(reference_parameter, predicted_parameter, id_column): return aligned_dict, meta_error_dict -def _calculate_error(aligned_parameters: Dict[_Hashable, pd.DataFrame]) -> Dict[_Hashable, pd.DataFrame]: +def _calculate_error(aligned_parameters: dict[_Hashable, pd.DataFrame]) -> dict[_Hashable, pd.DataFrame]: """Calculate the error between a reference and a predicted parameter.""" final_error_dict = {} for k, v in aligned_parameters.items(): @@ -568,7 +568,7 @@ def _icc(data: pd.DataFrame, scoring_errors: Literal["ignore", "warn", "raise"]) paras = data.columns.get_level_values("parameter").unique() data = data.stack("error_type").reset_index() - coefs: Dict[str, pd.Series] = {} + coefs: dict[str, pd.Series] = {} for para in paras: try: # If handle error is ignore, we also ignore all warnings here. diff --git a/gaitmap/evaluation_utils/scores.py b/gaitmap/evaluation_utils/scores.py index 7f89a575..c73cd2fe 100644 --- a/gaitmap/evaluation_utils/scores.py +++ b/gaitmap/evaluation_utils/scores.py @@ -1,7 +1,7 @@ """A set of helper functions to score the output of the evaluation of a stride segmentation against ground truth.""" import warnings -from typing import Dict, Union, overload +from typing import Union, overload import pandas as pd from typing_extensions import Literal, TypedDict @@ -18,8 +18,8 @@ class _ScoresDict(TypedDict): @overload def recall_score( - matches_df: Dict[_Hashable, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" -) -> Dict[_Hashable, float]: ... + matches_df: dict[_Hashable, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" +) -> dict[_Hashable, float]: ... @overload @@ -78,8 +78,8 @@ def recall_score(matches_df, *, zero_division: Literal["warn", 0, 1] = "warn"): @overload def precision_score( - matches_df: Dict[_Hashable, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" -) -> Dict[_Hashable, float]: ... + matches_df: dict[_Hashable, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" +) -> dict[_Hashable, float]: ... @overload @@ -140,8 +140,8 @@ def precision_score(matches_df, *, zero_division: Literal["warn", 0, 1] = "warn" @overload def f1_score( - matches_df: Dict[_Hashable, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" -) -> Dict[_Hashable, float]: ... + matches_df: dict[_Hashable, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" +) -> dict[_Hashable, float]: ... @overload @@ -203,8 +203,8 @@ def f1_score(matches_df, *, zero_division: Literal["warn", 0, 1] = "warn"): @overload def precision_recall_f1_score( - matches_df: Dict[str, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" -) -> Dict[str, _ScoresDict]: ... + matches_df: dict[str, pd.DataFrame], *, zero_division: Literal["warn", 0, 1] = "warn" +) -> dict[str, _ScoresDict]: ... @overload @@ -273,8 +273,8 @@ def precision_recall_f1_score(matches_df, *, zero_division: Literal["warn", 0, 1 def _get_match_type_dfs( - match_results: Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]], -) -> Union[Dict[_Hashable, Dict[str, pd.DataFrame]], Dict[str, pd.DataFrame]]: + match_results: Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]], +) -> Union[dict[_Hashable, dict[str, pd.DataFrame]], dict[str, pd.DataFrame]]: is_not_dict = not isinstance(match_results, dict) if is_not_dict: match_results = {"__dummy__": match_results} diff --git a/gaitmap/evaluation_utils/stride_segmentation.py b/gaitmap/evaluation_utils/stride_segmentation.py index 31568003..5f4de4f6 100644 --- a/gaitmap/evaluation_utils/stride_segmentation.py +++ b/gaitmap/evaluation_utils/stride_segmentation.py @@ -1,6 +1,7 @@ """A set of helper functions to evaluate the output of a stride segmentation against ground truth.""" -from typing import Dict, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Union import numpy as np import pandas as pd @@ -26,7 +27,7 @@ def evaluate_segmented_stride_list( one_to_one: bool = True, stride_list_postfix: str = "", ground_truth_postfix: str = "_ground_truth", -) -> Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]]: +) -> Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]]: """Find True Positives, False Positives and True Negatives by comparing a segmented stride list with ground truth. This compares a segmented stride list with a ground truth segmented stride list and returns True Positives, @@ -148,7 +149,7 @@ def _evaluate_stride_list( one_to_one: bool = True, stride_list_postfix: str = "", ground_truth_postfix: str = "_ground_truth", -) -> Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]]: +) -> Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]]: segmented_stride_list_type = is_stride_list(segmented_stride_list) ground_truth_type = is_stride_list(ground_truth) @@ -195,7 +196,7 @@ def match_stride_lists( one_to_one: bool = True, postfix_a: str = "_a", postfix_b: str = "_b", -) -> Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]]: +) -> Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]]: """Find matching strides in two stride lists with a certain tolerance. This function will find matching strides in two stride lists as long as all selected columns/event of a stride @@ -319,7 +320,7 @@ def _match_stride_lists( one_to_one: bool = True, postfix_a: str = "_a", postfix_b: str = "_b", -) -> Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]]: +) -> Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]]: if postfix_a == postfix_b: raise ValueError("The postfix for the left and the right stride list must be different.") @@ -420,7 +421,7 @@ def _match_single_stride_lists( def _match_label_lists( list_left: np.ndarray, list_right: np.ndarray, tolerance: Union[int, float], one_to_one: bool -) -> Tuple[np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray]: """Find matches in two lists based on the distance between their vectors. Parameters diff --git a/gaitmap/event_detection/_herzer_event_detection.py b/gaitmap/event_detection/_herzer_event_detection.py index 28d06810..ab084ac5 100644 --- a/gaitmap/event_detection/_herzer_event_detection.py +++ b/gaitmap/event_detection/_herzer_event_detection.py @@ -1,6 +1,6 @@ """An event detection algorithm optimized for stair ambulation developed by Liv Herzer in her Bachelor Thesis .""" -from typing import Callable, Dict, Optional, Tuple, Union +from typing import Callable, Optional, Union import numpy as np import pandas as pd @@ -183,7 +183,7 @@ class HerzerEventDetection(_EventDetectionMixin, BaseEventDetection): """ min_vel_search_win_size_ms: float - mid_swing_peak_prominence: Union[Tuple[float, float], float] + mid_swing_peak_prominence: Union[tuple[float, float], float] mid_swing_n_considered_peaks: int ic_lowpass_filter: BaseFilter memory: Optional[Memory] @@ -192,12 +192,12 @@ class HerzerEventDetection(_EventDetectionMixin, BaseEventDetection): def __init__( self, min_vel_search_win_size_ms: float = 100, - mid_swing_peak_prominence: Union[Tuple[float, float], float] = 20, + mid_swing_peak_prominence: Union[tuple[float, float], float] = 20, mid_swing_n_considered_peaks: int = 3, ic_lowpass_filter: BaseFilter = cf(ButterworthFilter(order=1, cutoff_freq_hz=4)), memory: Optional[Memory] = None, enforce_consistency: bool = True, - detect_only: Optional[Tuple[str, ...]] = None, + detect_only: Optional[tuple[str, ...]] = None, ) -> None: self.min_vel_search_win_size_ms = min_vel_search_win_size_ms self.mid_swing_peak_prominence = mid_swing_peak_prominence @@ -205,7 +205,7 @@ def __init__( self.ic_lowpass_filter = ic_lowpass_filter super().__init__(memory=memory, enforce_consistency=enforce_consistency, detect_only=detect_only) - def _get_detect_kwargs(self) -> Dict[str, int]: + def _get_detect_kwargs(self) -> dict[str, int]: min_vel_search_win_size = int(self.min_vel_search_win_size_ms / 1000 * self.sampling_rate_hz) return { "min_vel_search_win_size": min_vel_search_win_size, @@ -228,13 +228,13 @@ def _find_all_events( acc: pd.DataFrame, stride_list: pd.DataFrame, *, - events: Tuple[str, ...] = ("ic", "tc", "min_vel"), + events: tuple[str, ...] = ("ic", "tc", "min_vel"), min_vel_search_win_size: int, - mid_swing_peak_prominence: Union[Tuple[float, float], float], + mid_swing_peak_prominence: Union[tuple[float, float], float], mid_swing_n_considered_peaks: int, ic_lowpass_filter: BaseFilter, sampling_rate_hz: float, -) -> Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]: +) -> tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]: """Find events in provided data by looping over single strides.""" gyr_ml = gyr["gyr_ml"].to_numpy() gyr = gyr.to_numpy() @@ -280,7 +280,7 @@ def _find_all_events( ) -def _get_midswing_max(gyr_ml, peak_prominence_thresholds: Union[Tuple[float, float], float], n_considered_peaks: int): +def _get_midswing_max(gyr_ml, peak_prominence_thresholds: Union[tuple[float, float], float], n_considered_peaks: int): """Return the first prominent maximum within the given stride. This maximum should correspond to the mid swing gait event. @@ -299,7 +299,7 @@ def _detect_ic( gyr_ml: np.ndarray, acc_pa_inv: np.ndarray, gyr_ml_grad: np.ndarray, - peak_prominence_thresholds: Union[Tuple[float, float], float], + peak_prominence_thresholds: Union[tuple[float, float], float], n_considered_peaks: int, lowpass_filter: BaseFilter, sampling_rate_hz: float, diff --git a/gaitmap/parameters/_spatial_parameters.py b/gaitmap/parameters/_spatial_parameters.py index d6752a46..f5f90c52 100644 --- a/gaitmap/parameters/_spatial_parameters.py +++ b/gaitmap/parameters/_spatial_parameters.py @@ -1,7 +1,8 @@ """Calculate spatial parameters algorithm by Kanzler et al. 2015 and Rampp et al. 2014.""" import warnings -from typing import Dict, Literal, Optional, Sequence, Union +from collections.abc import Sequence +from typing import Literal, Optional, Union import numpy as np import pandas as pd @@ -153,8 +154,8 @@ class SpatialParameterCalculation(BaseSpatialParameterCalculation): calculate_only: Optional[Sequence[ParamterNames]] expected_stride_type: Literal["min_vel", "ic"] - parameters_: Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]] - sole_angle_course_: Optional[Union[pd.Series, Dict[_Hashable, pd.Series]]] + parameters_: Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]] + sole_angle_course_: Optional[Union[pd.Series, dict[_Hashable, pd.Series]]] stride_event_list: StrideList positions: Optional[PositionList] @@ -170,7 +171,7 @@ def __init__( self.expected_stride_type = expected_stride_type @property - def parameters_pretty_(self) -> Union[pd.DataFrame, Dict[_Hashable, pd.DataFrame]]: + def parameters_pretty_(self) -> Union[pd.DataFrame, dict[_Hashable, pd.DataFrame]]: """Return parameters with column names indicating units.""" if isinstance(self.parameters_, dict): parameters_ = {} diff --git a/gaitmap/parameters/_temporal_parameters.py b/gaitmap/parameters/_temporal_parameters.py index 6dddecf5..47faeee0 100644 --- a/gaitmap/parameters/_temporal_parameters.py +++ b/gaitmap/parameters/_temporal_parameters.py @@ -1,6 +1,6 @@ """Calculate temporal parameters algorithm.""" -from typing import Dict, Literal, Tuple, TypeVar, Union +from typing import Literal, TypeVar, Union import pandas as pd @@ -87,7 +87,7 @@ class TemporalParameterCalculation(BaseTemporalParameterCalculation): expected_stride_type: Literal["min_vel", "ic"] - parameters_: Union[pd.DataFrame, Dict[str, pd.DataFrame]] + parameters_: Union[pd.DataFrame, dict[str, pd.DataFrame]] sampling_rate_hz: float stride_event_list: StrideList @@ -96,7 +96,7 @@ def __init__(self, expected_stride_type: Literal["min_vel", "ic"] = "min_vel") - self.expected_stride_type = expected_stride_type @property - def parameters_pretty_(self) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: + def parameters_pretty_(self) -> Union[pd.DataFrame, dict[str, pd.DataFrame]]: """Return parameters with column names indicating units.""" if isinstance(self.parameters_, dict): parameters_ = {} @@ -183,7 +183,7 @@ def _calculate_single_sensor( return parameters_ -def _get_stride_time_cols(stride_type: Literal["min_vel", "ic"]) -> Tuple[str, str]: +def _get_stride_time_cols(stride_type: Literal["min_vel", "ic"]) -> tuple[str, str]: if stride_type == "min_vel": return "pre_ic", "ic" if stride_type == "ic": @@ -191,7 +191,7 @@ def _get_stride_time_cols(stride_type: Literal["min_vel", "ic"]) -> Tuple[str, s raise ValueError("stride_type should be either 'min_vel' or 'ic'") -def _get_swing_time_cols(stride_type: Literal["min_vel", "ic"]) -> Tuple[str, str]: +def _get_swing_time_cols(stride_type: Literal["min_vel", "ic"]) -> tuple[str, str]: if stride_type == "min_vel": return "tc", "ic" if stride_type == "ic": diff --git a/gaitmap/preprocessing/sensor_alignment/_gravity_alignment.py b/gaitmap/preprocessing/sensor_alignment/_gravity_alignment.py index 4f071fce..40bb79f0 100644 --- a/gaitmap/preprocessing/sensor_alignment/_gravity_alignment.py +++ b/gaitmap/preprocessing/sensor_alignment/_gravity_alignment.py @@ -1,6 +1,6 @@ """Helpers to rotate the sensor in the predefined gaitmap sensor frame.""" -from typing import Dict, Union +from typing import Union import numpy as np import pandas as pd @@ -83,7 +83,7 @@ def align_dataset_to_gravity( dataset_type = is_sensor_data(dataset) window_length = int(round(window_length_s * sampling_rate_hz)) - acc_vector: Union[np.ndarray, Dict[_Hashable, np.ndarray]] + acc_vector: Union[np.ndarray, dict[_Hashable, np.ndarray]] if dataset_type == "single": # get static acc vector acc_vector = _get_static_acc_vector(dataset, window_length, static_signal_th, metric) diff --git a/gaitmap/preprocessing/sensor_alignment/_pca_alignment.py b/gaitmap/preprocessing/sensor_alignment/_pca_alignment.py index d5dba50c..30ddc4f7 100644 --- a/gaitmap/preprocessing/sensor_alignment/_pca_alignment.py +++ b/gaitmap/preprocessing/sensor_alignment/_pca_alignment.py @@ -1,6 +1,7 @@ """A implementation of a PCA based sensor alignment to perform coordinate system rotations.""" -from typing import Dict, Optional, Sequence, Union +from collections.abc import Sequence +from typing import Optional, Union import numpy as np from scipy.spatial.transform import Rotation @@ -129,8 +130,8 @@ class PcaAlignment(BaseSensorAlignment): """ - rotation_: Union[Rotation, Dict[_Hashable, Rotation]] - pca_: Union[PCA, Dict[_Hashable, PCA]] + rotation_: Union[Rotation, dict[_Hashable, Rotation]] + pca_: Union[PCA, dict[_Hashable, PCA]] target_axis: str pca_plane_axis: Sequence[str] diff --git a/gaitmap/stride_segmentation/_roi_stride_segmentation.py b/gaitmap/stride_segmentation/_roi_stride_segmentation.py index aaf9cd84..c031d8f8 100644 --- a/gaitmap/stride_segmentation/_roi_stride_segmentation.py +++ b/gaitmap/stride_segmentation/_roi_stride_segmentation.py @@ -1,7 +1,7 @@ """Wrapper class to apply a stride segmentation to multiple regions of interest in a dataset.""" from copy import deepcopy -from typing import Dict, Generic, Optional, TypeVar, Union +from typing import Generic, Optional, TypeVar, Union import pandas as pd from tpcp import get_action_method @@ -127,7 +127,7 @@ class RoiStrideSegmentation(BaseStrideSegmentation, Generic[StrideSegmentationAl action_method: Optional[str] instances_per_roi_: Union[ - Dict[_Hashable, StrideSegmentationAlgorithm], Dict[_Hashable, Dict[_Hashable, StrideSegmentationAlgorithm]] + dict[_Hashable, StrideSegmentationAlgorithm], dict[_Hashable, dict[_Hashable, StrideSegmentationAlgorithm]] ] stride_list_: StrideList @@ -184,8 +184,8 @@ def segment( if self._multi_roi: # Apply the segmentation to a single dataset in case a multi - sensor roi list is provided - results_dict: Dict[ - _Hashable, Dict[str, Union[pd.DataFrame, Dict[_Hashable, StrideSegmentationAlgorithm]]] + results_dict: dict[ + _Hashable, dict[str, Union[pd.DataFrame, dict[_Hashable, StrideSegmentationAlgorithm]]] ] = {} for sensor, roi in self.regions_of_interest.items(): sensor_data = self.data[sensor] @@ -209,7 +209,7 @@ def _segment_single_sensor( rois: SingleSensorRegionsOfInterestList, sensor_name: Optional[_Hashable] = None, **kwargs, - ) -> Dict[str, Union[pd.DataFrame, Dict[_Hashable, StrideSegmentationAlgorithm]]]: + ) -> dict[str, Union[pd.DataFrame, dict[_Hashable, StrideSegmentationAlgorithm]]]: """Call the the segmentation algorithm for each region of interest and store the instance.""" rois = rois.reset_index() index_col = ROI_ID_COLS[get_single_sensor_regions_of_interest_types(rois)] @@ -246,7 +246,7 @@ def _segment_single_sensor( combined_stride_list = self._merge_stride_lists(combined_stride_list, index_col) return {"stride_list": combined_stride_list, "instances_per_roi": instances_per_roi} - def _merge_stride_lists(self, stride_lists: Dict[_Hashable, StrideList], index_name: str) -> StrideList: + def _merge_stride_lists(self, stride_lists: dict[_Hashable, StrideList], index_name: str) -> StrideList: """Merge either single or multisensor stride lists. The roi id (the dict key in the input), will be a column in the final output dataframe. diff --git a/gaitmap/stride_segmentation/_utils.py b/gaitmap/stride_segmentation/_utils.py index ea18e784..51dee137 100644 --- a/gaitmap/stride_segmentation/_utils.py +++ b/gaitmap/stride_segmentation/_utils.py @@ -1,6 +1,6 @@ """Some general utils for the stride segmentation algorithms.""" -from typing import Tuple, Union +from typing import Union import numpy as np @@ -10,7 +10,7 @@ def snap_to_min( data: np.ndarray, matches_start_end: np.ndarray, - snap_to_min_win_samples: Union[int, Tuple[int, int]], + snap_to_min_win_samples: Union[int, tuple[int, int]], ): """Post process a set of matches by "snapping" their start and end values to the closest minima of the data. diff --git a/gaitmap/trajectory_reconstruction/_region_level_trajectory.py b/gaitmap/trajectory_reconstruction/_region_level_trajectory.py index 84a24b67..77c0f13a 100644 --- a/gaitmap/trajectory_reconstruction/_region_level_trajectory.py +++ b/gaitmap/trajectory_reconstruction/_region_level_trajectory.py @@ -1,6 +1,6 @@ """Wrapper to apply position and orientation estimation to multiple regions in a dataset.""" -from typing import Dict, List, Optional, Tuple, Union +from typing import Optional, Union import pandas as pd from scipy.spatial.transform import Rotation @@ -326,12 +326,12 @@ def estimate_intersect( def intersect( self, stride_event_list: StrideList, - return_data: Tuple[Literal["orientation", "position", "velocity"], ...] = ( + return_data: tuple[Literal["orientation", "position", "velocity"], ...] = ( "orientation", "position", "velocity", ), - ) -> Tuple[Union[PositionList, OrientationList, VelocityList], ...]: + ) -> tuple[Union[PositionList, OrientationList, VelocityList], ...]: """Cut out the trajectory of individual strides from the region trajectories. This method can only be used after `estimate` was called. @@ -436,8 +436,8 @@ def _estimate_single_sensor( self, data: SingleSensorData, integration_regions: SingleSensorRegionsOfInterestList, - stride_list_list: Optional[List[SingleSensorStrideList]], - ) -> Dict[str, pd.DataFrame]: + stride_list_list: Optional[list[SingleSensorStrideList]], + ) -> dict[str, pd.DataFrame]: # Set the class variable to determine the correct index values per dataset. self._expected_integration_region_index = [ ROI_ID_COLS[get_single_sensor_regions_of_interest_types(integration_regions)] diff --git a/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py b/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py index 782cb2fd..4a827cc6 100644 --- a/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py +++ b/gaitmap/trajectory_reconstruction/_trajectory_wrapper.py @@ -1,7 +1,8 @@ """A helper class for common utilities TrajectoryReconstructionWrapper classes.""" import warnings -from typing import Dict, List, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Optional, Union import numpy as np import pandas as pd @@ -87,7 +88,7 @@ def _estimate( self, data, integration_regions, - stride_list_list: Optional[Union[Dict[str, List[SingleSensorStrideList]], List[SingleSensorStrideList]]], + stride_list_list: Optional[Union[dict[str, list[SingleSensorStrideList]], list[SingleSensorStrideList]]], dataset_type: Literal["single", "multi"], ) -> Self: """Actual estimation method. @@ -98,7 +99,7 @@ def _estimate( if dataset_type == "single": results = self._estimate_single_sensor(data, integration_regions, stride_list_list) else: - results_dict: Dict[_Hashable, Dict[str, pd.DataFrame]] = {} + results_dict: dict[_Hashable, dict[str, pd.DataFrame]] = {} for sensor in get_multi_sensor_names(data): results_dict[sensor] = self._estimate_single_sensor( data[sensor], integration_regions[sensor], stride_list_list[sensor] if stride_list_list else None @@ -111,8 +112,8 @@ def _estimate_single_sensor( self, data: SingleSensorData, integration_regions: SingleSensorRegionsOfInterestList, - stride_list_list: Optional[List[SingleSensorStrideList]], - ) -> Dict[str, pd.DataFrame]: + stride_list_list: Optional[list[SingleSensorStrideList]], + ) -> dict[str, pd.DataFrame]: integration_regions = set_correct_index(integration_regions, self._expected_integration_region_index) full_index = (*self._expected_integration_region_index, "sample") orientation = {} @@ -147,7 +148,7 @@ def _estimate_single_sensor( def _estimate_region( self, data: SingleSensorData, start: int, end: int, stride_event_list: SingleSensorStrideList - ) -> Tuple[Rotation, pd.DataFrame, pd.DataFrame]: + ) -> tuple[Rotation, pd.DataFrame, pd.DataFrame]: stride_data = data.iloc[start:end].copy() initial_orientation = self._calculate_initial_orientation(data, start) diff --git a/gaitmap/trajectory_reconstruction/trajectory_methods/_kalman_numba_funcs.py b/gaitmap/trajectory_reconstruction/trajectory_methods/_kalman_numba_funcs.py index 88243f1c..88f1da08 100644 --- a/gaitmap/trajectory_reconstruction/trajectory_methods/_kalman_numba_funcs.py +++ b/gaitmap/trajectory_reconstruction/trajectory_methods/_kalman_numba_funcs.py @@ -1,6 +1,6 @@ """Helper functions for the RTS Kalman filter.""" -from typing import Any, Callable, NamedTuple, Tuple +from typing import Any, Callable, NamedTuple import numpy as np from numba import njit @@ -21,7 +21,7 @@ class ForwardPassDependencies(NamedTuple): motion_update_func: Callable # This needs to be a tuple and not a dict, as numba can not process dicts - motion_update_func_parameters: Tuple + motion_update_func_parameters: tuple def rts_kalman_update_series( @@ -32,7 +32,7 @@ def rts_kalman_update_series( meas_noise, process_noise, zupts, - parameters: Tuple[Any, ...], + parameters: tuple[Any, ...], forward_pass_func: Callable, forward_pass_dependencies: ForwardPassDependencies, ): @@ -292,7 +292,7 @@ def _rts_kalman_update_series( meas_noise, process_noise, zupts, - parameters: Tuple[Any, ...], + parameters: tuple[Any, ...], forward_pass_func: Callable, forward_pass_dependencies: ForwardPassDependencies, ): diff --git a/gaitmap/utils/_datatype_validation_helper.py b/gaitmap/utils/_datatype_validation_helper.py index 04064728..6e04551f 100644 --- a/gaitmap/utils/_datatype_validation_helper.py +++ b/gaitmap/utils/_datatype_validation_helper.py @@ -1,6 +1,7 @@ """Internal helpers for dataset validation.""" -from typing import Dict, Iterable, List, Sequence, Tuple, Union +from collections.abc import Iterable, Sequence +from typing import Union import pandas as pd from typing_extensions import Literal @@ -17,7 +18,7 @@ def _get_expected_dataset_cols( frame: Literal["sensor", "body"], check_acc: bool = True, check_gyr: bool = True -) -> List: +) -> list: expected_cols = [] if frame == "sensor": acc = SF_ACC @@ -34,7 +35,7 @@ def _get_expected_dataset_cols( return expected_cols -def _assert_is_dtype(obj, dtype: Union[type, Tuple[type, ...]]) -> None: +def _assert_is_dtype(obj, dtype: Union[type, tuple[type, ...]]) -> None: """Check if an object has a specific dtype.""" if not isinstance(obj, dtype): raise ValidationError(f"The dataobject is expected to be one of ({dtype},). But it is a {type(obj)}") @@ -71,7 +72,7 @@ def _assert_has_multindex_cols(df: pd.DataFrame, nlevels: int = 2, expected: boo ) -def _assert_has_columns(df: pd.DataFrame, columns_sets: Sequence[Union[List[_Hashable], List[str]]]) -> None: +def _assert_has_columns(df: pd.DataFrame, columns_sets: Sequence[Union[list[_Hashable], list[str]]]) -> None: """Check if the dataframe has at least all columns sets. Examples @@ -114,7 +115,7 @@ def _get_multi_sensor_data_names(dataset: Union[dict, pd.DataFrame]) -> Sequence return keys -def _assert_multisensor_is_not_empty(obj: Union[pd.DataFrame, Dict]) -> None: +def _assert_multisensor_is_not_empty(obj: Union[pd.DataFrame, dict]) -> None: sensors = _get_multi_sensor_data_names(obj) if len(sensors) == 0: raise ValidationError("The provided multi-sensor object does not contain any data/contains no sensors.") diff --git a/gaitmap/utils/_types.py b/gaitmap/utils/_types.py index 199f3877..f62e0b6d 100644 --- a/gaitmap/utils/_types.py +++ b/gaitmap/utils/_types.py @@ -3,7 +3,8 @@ For user facing type declarations, please see `gaitmap.utils.datatype_helper`. """ -from typing import TYPE_CHECKING, Any, Hashable, TypeVar, Union +from collections.abc import Hashable +from typing import TYPE_CHECKING, Any, TypeVar, Union import pandas as pd diff --git a/gaitmap/utils/array_handling.py b/gaitmap/utils/array_handling.py index 3a80afb9..5875a930 100644 --- a/gaitmap/utils/array_handling.py +++ b/gaitmap/utils/array_handling.py @@ -1,6 +1,7 @@ """A set of util functions that help to manipulate arrays in any imaginable way.""" -from typing import Iterable, Iterator, List, Optional, Tuple, Union +from collections.abc import Iterable, Iterator +from typing import Optional, Union import numba.typed import numpy as np @@ -187,7 +188,7 @@ def start_end_array_to_bool_array(start_end_array: np.ndarray, pad_to_length: Op return bool_array.astype(bool) -def split_array_at_nan(a: np.ndarray) -> List[Tuple[int, np.ndarray]]: +def split_array_at_nan(a: np.ndarray) -> list[tuple[int, np.ndarray]]: """Split an array into sections at nan values. Examples @@ -242,7 +243,7 @@ def find_local_minima_with_distance(data: np.ndarray, threshold: Optional[float] def find_extrema_in_radius( data: np.ndarray, indices: np.ndarray, - radius: Union[int, Tuple[int, int]], + radius: Union[int, tuple[int, int]], extrema_type: Literal["min", "max"] = "min", ): """Return the index of the global extrema of data in the given radius around each index in indices. @@ -318,7 +319,7 @@ def _bool_fill(indices: np.ndarray, bool_values: np.ndarray, array: np.ndarray) return array -def multi_array_interpolation(arrays: List[np.ndarray], n_samples, kind: str = "linear") -> np.ndarray: +def multi_array_interpolation(arrays: list[np.ndarray], n_samples, kind: str = "linear") -> np.ndarray: """Interpolate multiple 2D-arrays to the same length along axis 0. Parameters @@ -421,7 +422,7 @@ def _solve_overlap(input_array: np.ndarray, gap_size: int) -> numba.typed.List: def iterate_region_data( signal_sequence: Iterable[SingleSensorData], label_sequences: Iterable[Union[SingleSensorStrideList, SingleSensorRegionsOfInterestList]], - expected_col_order: Optional[List[str]] = None, + expected_col_order: Optional[list[str]] = None, ) -> Iterator[SingleSensorData]: """Iterate over individual strides/ROIs in multiple sensor data sequences. diff --git a/gaitmap/utils/coordinate_conversion.py b/gaitmap/utils/coordinate_conversion.py index fd98e30d..e682ea0f 100644 --- a/gaitmap/utils/coordinate_conversion.py +++ b/gaitmap/utils/coordinate_conversion.py @@ -4,7 +4,7 @@ """ import warnings -from typing import List, Optional +from typing import Optional import pandas as pd @@ -82,8 +82,8 @@ def convert_right_foot_to_fbf(data: SingleSensorData): def convert_to_fbf( data: MultiSensorData, - left: Optional[List[str]] = None, - right: Optional[List[str]] = None, + left: Optional[list[str]] = None, + right: Optional[list[str]] = None, right_like: Optional[str] = None, left_like: Optional[str] = None, ): diff --git a/gaitmap/utils/datatype_helper.py b/gaitmap/utils/datatype_helper.py index 6ad5edf3..fa6e88a1 100644 --- a/gaitmap/utils/datatype_helper.py +++ b/gaitmap/utils/datatype_helper.py @@ -1,6 +1,7 @@ """A couple of helper functions that easy the use of the typical gaitmap data formats.""" -from typing import Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union, cast +from collections.abc import Iterable, Sequence +from typing import Callable, Optional, Union, cast import numpy as np import pandas as pd @@ -40,26 +41,26 @@ SingleSensorVelocityList = _DataFrame SingleSensorOrientationList = _DataFrame -MultiSensorData = Union[pd.DataFrame, Dict[_Hashable, SingleSensorData]] +MultiSensorData = Union[pd.DataFrame, dict[_Hashable, SingleSensorData]] SensorData = Union[SingleSensorData, MultiSensorData] -MultiSensorStrideList = Dict[_Hashable, pd.DataFrame] +MultiSensorStrideList = dict[_Hashable, pd.DataFrame] StrideList = Union[SingleSensorStrideList, MultiSensorStrideList] -MultiSensorRegionsOfInterestList = Dict[_Hashable, pd.DataFrame] +MultiSensorRegionsOfInterestList = dict[_Hashable, pd.DataFrame] RegionsOfInterestList = Union[SingleSensorRegionsOfInterestList, MultiSensorRegionsOfInterestList] -MultiSensorPositionList = Dict[_Hashable, pd.DataFrame] +MultiSensorPositionList = dict[_Hashable, pd.DataFrame] PositionList = Union[SingleSensorPositionList, MultiSensorPositionList] -MultiSensorVelocityList = Dict[_Hashable, pd.DataFrame] +MultiSensorVelocityList = dict[_Hashable, pd.DataFrame] VelocityList = Union[SingleSensorVelocityList, MultiSensorVelocityList] -MultiSensorOrientationList = Dict[_Hashable, pd.DataFrame] +MultiSensorOrientationList = dict[_Hashable, pd.DataFrame] OrientationList = Union[SingleSensorOrientationList, MultiSensorOrientationList] -def to_dict_multi_sensor_data(sensordata: MultiSensorData) -> Dict[_Hashable, SingleSensorData]: +def to_dict_multi_sensor_data(sensordata: MultiSensorData) -> dict[_Hashable, SingleSensorData]: """Convert a multi-sensor data to a dictionary of single sensor datas. If it is already in this format, the function will not do anything. @@ -278,7 +279,7 @@ def is_sensor_data( def is_single_sensor_stride_list( stride_list: SingleSensorStrideList, stride_type: _ALLOWED_STRIDE_TYPE = "any", - check_additional_cols: Union[bool, Tuple[str, ...]] = True, + check_additional_cols: Union[bool, tuple[str, ...]] = True, raise_exception: bool = False, ) -> bool: """Check if an input is a single-sensor stride list. @@ -384,7 +385,7 @@ def is_single_sensor_stride_list( def is_multi_sensor_stride_list( stride_list: MultiSensorStrideList, stride_type: _ALLOWED_STRIDE_TYPE = "any", - check_additional_cols: Union[bool, Tuple[str, ...]] = True, + check_additional_cols: Union[bool, tuple[str, ...]] = True, raise_exception: bool = False, ) -> bool: """Check if an input is a multi-sensor stride list. @@ -448,7 +449,7 @@ def is_multi_sensor_stride_list( def is_stride_list( stride_list: StrideList, stride_type: _ALLOWED_STRIDE_TYPE = "any", - check_additional_cols: Union[bool, Tuple[str, ...]] = True, + check_additional_cols: Union[bool, tuple[str, ...]] = True, ) -> Literal["single", "multi"]: """Check if an object is a valid multi-sensor or single-sensor stride list. @@ -740,7 +741,7 @@ def get_single_sensor_trajectory_list_types( def _is_single_sensor_trajectory_list( input_prefix: str, input_datatype: str, - expected_cols: List[str], + expected_cols: list[str], traj_list: Union[SingleSensorOrientationList, SingleSensorVelocityList, SingleSensorOrientationList], traj_list_type: Optional[_ALLOWED_TRAJ_LIST_TYPES] = None, raise_exception: bool = False, diff --git a/gaitmap/utils/rotations.py b/gaitmap/utils/rotations.py index 2b393d7b..ed39ffe2 100644 --- a/gaitmap/utils/rotations.py +++ b/gaitmap/utils/rotations.py @@ -3,7 +3,7 @@ All util functions use :class:`scipy.spatial.transform.Rotation` to represent rotations. """ -from typing import Callable, Dict, List, Optional, Union +from typing import Callable, Optional, Union import numpy as np import pandas as pd @@ -118,7 +118,7 @@ def _rotate_sensor(data: SingleSensorData, rotation: Optional[Rotation]) -> Sing def _rotate_or_flip_dataset( - dataset: SensorData, rotation: Union[Rotation, Dict[str, Rotation]], single_rot_method: Callable + dataset: SensorData, rotation: Union[Rotation, dict[str, Rotation]], single_rot_method: Callable ): dataset_type = is_sensor_data(dataset, frame="sensor") if dataset_type == "single": @@ -148,7 +148,7 @@ def _rotate_or_flip_dataset( return rotated_dataset -def flip_dataset(dataset: SensorData, rotation: Union[Rotation, Dict[str, Rotation]]) -> SensorData: +def flip_dataset(dataset: SensorData, rotation: Union[Rotation, dict[str, Rotation]]) -> SensorData: """Flip datasets around axis data of a dataset. This is equivalent to rotating the data, but only 90/180 deg rotations are allowed. @@ -183,7 +183,7 @@ def flip_dataset(dataset: SensorData, rotation: Union[Rotation, Dict[str, Rotati return _rotate_or_flip_dataset(dataset, rotation, _flip_sensor) -def rotate_dataset(dataset: SensorData, rotation: Union[Rotation, Dict[str, Rotation]]) -> SensorData: +def rotate_dataset(dataset: SensorData, rotation: Union[Rotation, dict[str, Rotation]]) -> SensorData: """Apply a rotation to acc and gyro data of a dataset. Parameters @@ -320,7 +320,7 @@ def get_gravity_rotation(gravity_vector: np.ndarray, expected_gravity: np.ndarra return find_shortest_rotation(gravity_vector, expected_gravity) -def find_rotation_around_axis(rot: Rotation, rotation_axis: Union[np.ndarray, List]) -> Rotation: +def find_rotation_around_axis(rot: Rotation, rotation_axis: Union[np.ndarray, list]) -> Rotation: """Calculate the rotation component of rot around the given rotation axis. This performs a swing-twist decomposition of the rotation quaternion [1]_. diff --git a/gaitmap/utils/static_moment_detection.py b/gaitmap/utils/static_moment_detection.py index 805fa63e..b2e96392 100644 --- a/gaitmap/utils/static_moment_detection.py +++ b/gaitmap/utils/static_moment_detection.py @@ -1,7 +1,8 @@ """A set of util functions to detect static regions in a IMU signal given certain constrains.""" +from collections.abc import Sequence from functools import partial -from typing import Callable, Optional, Sequence, Tuple, get_args +from typing import Callable, Optional, get_args import numpy as np from numpy.linalg import norm @@ -18,7 +19,7 @@ def _window_apply_threshold( data, window_length: int, overlap: int, func: Callable[[np.ndarray], np.ndarray], threshold: float -) -> Tuple[np.ndarray, int, float]: +) -> tuple[np.ndarray, int, float]: # allocate output array inactive_signal_bool_array = np.zeros(len(data)) @@ -45,7 +46,7 @@ def find_static_samples( inactive_signal_th: float, metric: METRIC_FUNCTION_NAMES = "mean", overlap: Optional[int] = None, -) -> Tuple[np.ndarray, int, float]: +) -> tuple[np.ndarray, int, float]: """Search for static samples within given input signal, based on windowed L2-norm thresholding. .. warning:: @@ -147,7 +148,7 @@ def find_static_samples_shoe( window_length: int, inactive_signal_th: float, overlap: Optional[int] = None, -) -> Tuple[np.ndarray, int, float]: +) -> tuple[np.ndarray, int, float]: """Use the SHOE algorithm for static moment detection. This is based on the papers [1]_ and [2]_ and uses as weighted sum of the gravity corrected acc and the gyro norm to @@ -283,7 +284,7 @@ def find_static_sequences( def find_first_static_window_multi_sensor( signals: Sequence[np.ndarray], window_length: int, inactive_signal_th: float, metric: METRIC_FUNCTION_NAMES -) -> Tuple[int, int]: +) -> tuple[int, int]: """Find the first time window in the signal where all provided sensors are static. Parameters diff --git a/gaitmap/utils/stride_list_conversion.py b/gaitmap/utils/stride_list_conversion.py index 8625425e..6983ffb5 100644 --- a/gaitmap/utils/stride_list_conversion.py +++ b/gaitmap/utils/stride_list_conversion.py @@ -1,7 +1,5 @@ """A couple of utils to convert stride lists into different formats.""" -from typing import List, Tuple - import numpy as np import pandas as pd from typing_extensions import Literal @@ -48,7 +46,7 @@ def convert_segmented_stride_list(stride_list: StrideList, target_stride_type: L def _segmented_stride_list_to_min_vel_single_sensor( stride_list: SingleSensorStrideList, target_stride_type: Literal["min_vel", "ic"] -) -> Tuple[SingleSensorStrideList, SingleSensorStrideList]: +) -> tuple[SingleSensorStrideList, SingleSensorStrideList]: """Convert a segmented stride list with detected events into other types of stride lists. During the conversion some strides might be removed. @@ -115,7 +113,7 @@ def enforce_stride_list_consistency( stride_list: SingleSensorStrideList, stride_type=Literal["segmented", "min_vel", "ic"], check_stride_list: bool = True, -) -> Tuple[SingleSensorStrideList, SingleSensorStrideList]: +) -> tuple[SingleSensorStrideList, SingleSensorStrideList]: """Exclude those strides where the gait events do not match the expected order or contain NaN. Correct order in depends on the stride type: @@ -169,7 +167,7 @@ def enforce_stride_list_consistency( def intersect_stride_list( stride_event_list: SingleSensorStrideList, regions_of_interest: SingleSensorRegionsOfInterestList, -) -> List[SingleSensorStrideList]: +) -> list[SingleSensorStrideList]: """Split the stride list into multiple stride lists based on the regions of interest. All events in the returned stride lists are made relative to the start of the region of interest. diff --git a/gaitmap/zupt_detection/_combo_zupt_detector.py b/gaitmap/zupt_detection/_combo_zupt_detector.py index c3cfadda..84c27c9b 100644 --- a/gaitmap/zupt_detection/_combo_zupt_detector.py +++ b/gaitmap/zupt_detection/_combo_zupt_detector.py @@ -1,4 +1,4 @@ -from typing import List, Literal, Optional, Tuple +from typing import Literal, Optional import numpy as np from typing_extensions import Self @@ -42,11 +42,11 @@ class ComboZuptDetector(BaseZuptDetector, PerSampleZuptDetectorMixin): _composite_params = ("detectors",) - detectors: Optional[List[Tuple[str, BaseZuptDetector]]] + detectors: Optional[list[tuple[str, BaseZuptDetector]]] operation: Literal["and", "or"] def __init__( - self, detectors: Optional[List[Tuple[str, BaseZuptDetector]]] = None, operation: Literal["and", "or"] = "or" + self, detectors: Optional[list[tuple[str, BaseZuptDetector]]] = None, operation: Literal["and", "or"] = "or" ) -> None: self.detectors = detectors self.operation = operation diff --git a/gaitmap/zupt_detection/_moving_window_zupt_detector.py b/gaitmap/zupt_detection/_moving_window_zupt_detector.py index dc4b62d4..b980aa69 100644 --- a/gaitmap/zupt_detection/_moving_window_zupt_detector.py +++ b/gaitmap/zupt_detection/_moving_window_zupt_detector.py @@ -1,6 +1,6 @@ """A Basic ZUPT detector based on moving windows on the norm.""" -from typing import Optional, Tuple +from typing import Optional import numpy as np from typing_extensions import Literal, Self @@ -19,7 +19,7 @@ def _validate_window( window_overlap: Optional[float], window_overlap_samples: Optional[int], sampling_rate_hz: float, -) -> Tuple[int, Optional[int]]: +) -> tuple[int, Optional[int]]: """Validate window_length and overlap.""" window_length = round(sampling_rate_hz * window_length_s) if window_length < 3: diff --git a/gaitmap_mad/gaitmap_mad/event_detection/_filtered_rampp_event_detection.py b/gaitmap_mad/gaitmap_mad/event_detection/_filtered_rampp_event_detection.py index f5310a86..b9c61cdc 100644 --- a/gaitmap_mad/gaitmap_mad/event_detection/_filtered_rampp_event_detection.py +++ b/gaitmap_mad/gaitmap_mad/event_detection/_filtered_rampp_event_detection.py @@ -1,6 +1,6 @@ """The event detection algorithm by Rampp et al. 2014.""" -from typing import Dict, Optional, Tuple +from typing import Optional from joblib import Memory from tpcp import cf @@ -91,12 +91,12 @@ class FilteredRamppEventDetection(RamppEventDetection): def __init__( self, - ic_search_region_ms: Tuple[float, float] = (80, 50), + ic_search_region_ms: tuple[float, float] = (80, 50), min_vel_search_win_size_ms: float = 100, ic_lowpass_filter: BaseFilter = cf(ButterworthFilter(order=2, cutoff_freq_hz=15)), memory: Optional[Memory] = None, enforce_consistency: bool = True, - detect_only: Optional[Tuple[str, ...]] = None, + detect_only: Optional[tuple[str, ...]] = None, ) -> None: self.ic_lowpass_filter = ic_lowpass_filter super().__init__( @@ -107,6 +107,6 @@ def __init__( detect_only=detect_only, ) - def _get_detect_kwargs(self) -> Dict: + def _get_detect_kwargs(self) -> dict: parent_kwargs = super()._get_detect_kwargs() return {**parent_kwargs, "gyr_ic_lowpass_filter": self.ic_lowpass_filter} diff --git a/gaitmap_mad/gaitmap_mad/event_detection/_rampp_event_detection.py b/gaitmap_mad/gaitmap_mad/event_detection/_rampp_event_detection.py index 63055915..55a1c83a 100644 --- a/gaitmap_mad/gaitmap_mad/event_detection/_rampp_event_detection.py +++ b/gaitmap_mad/gaitmap_mad/event_detection/_rampp_event_detection.py @@ -1,6 +1,6 @@ """The event detection algorithm by Rampp et al. 2014.""" -from typing import Callable, Dict, Optional, Tuple, Union, cast +from typing import Callable, Optional, Union, cast import numpy as np import pandas as pd @@ -152,16 +152,16 @@ class RamppEventDetection(_EventDetectionMixin, BaseEventDetection): """ - ic_search_region_ms: Tuple[float, float] + ic_search_region_ms: tuple[float, float] min_vel_search_win_size_ms: float def __init__( self, - ic_search_region_ms: Tuple[float, float] = (80, 50), + ic_search_region_ms: tuple[float, float] = (80, 50), min_vel_search_win_size_ms: float = 100, memory: Optional[Memory] = None, enforce_consistency: bool = True, - detect_only: Optional[Tuple[str, ...]] = None, + detect_only: Optional[tuple[str, ...]] = None, ) -> None: self.ic_search_region_ms = ic_search_region_ms self.min_vel_search_win_size_ms = min_vel_search_win_size_ms @@ -174,9 +174,9 @@ def _select_all_event_detection_method(self) -> Callable: """ return _find_all_events - def _get_detect_kwargs(self) -> Dict[str, Union[Tuple[int, int], int]]: + def _get_detect_kwargs(self) -> dict[str, Union[tuple[int, int], int]]: ic_search_region = cast( - Tuple[int, int], tuple(int(v / 1000 * self.sampling_rate_hz) for v in self.ic_search_region_ms) + tuple[int, int], tuple(int(v / 1000 * self.sampling_rate_hz) for v in self.ic_search_region_ms) ) if all(v == 0 for v in ic_search_region): raise ValueError( @@ -195,12 +195,12 @@ def _find_all_events( gyr: pd.DataFrame, acc: pd.DataFrame, stride_list: pd.DataFrame, - events: Tuple[str, ...], - ic_search_region: Tuple[float, float], + events: tuple[str, ...], + ic_search_region: tuple[float, float], min_vel_search_win_size: int, sampling_rate_hz: float, gyr_ic_lowpass_filter: Optional[BaseFilter], -) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Find events in provided data by looping over single strides.""" gyr_ml = gyr["gyr_ml"] @@ -240,7 +240,7 @@ def _find_all_events( def _detect_ic( - gyr_ml: np.ndarray, acc_pa_inv: np.ndarray, gyr_ml_grad: np.ndarray, ic_search_region: Tuple[float, float] + gyr_ml: np.ndarray, acc_pa_inv: np.ndarray, gyr_ml_grad: np.ndarray, ic_search_region: tuple[float, float] ) -> float: """Find the ic. diff --git a/gaitmap_mad/gaitmap_mad/gait_detection/_ullrich_gait_sequence_detection.py b/gaitmap_mad/gaitmap_mad/gait_detection/_ullrich_gait_sequence_detection.py index 0f42ef24..df849b07 100644 --- a/gaitmap_mad/gaitmap_mad/gait_detection/_ullrich_gait_sequence_detection.py +++ b/gaitmap_mad/gaitmap_mad/gait_detection/_ullrich_gait_sequence_detection.py @@ -2,7 +2,7 @@ import copy import itertools -from typing import Dict, Optional, Tuple, TypeVar, Union +from typing import Optional, TypeVar, Union import numpy as np import pandas as pd @@ -148,7 +148,7 @@ def __init__( peak_prominence: float = 17.0, window_size_s: float = 10, active_signal_threshold: Optional[float] = None, - locomotion_band: Tuple[float, float] = (0.5, 3), + locomotion_band: tuple[float, float] = (0.5, 3), harmonic_tolerance_hz: float = 0.3, merge_gait_sequences_from_sensors: bool = False, additional_margin_s: Optional[float] = None, @@ -189,7 +189,7 @@ def detect(self: Self, data: SensorData, sampling_rate_hz: float) -> Self: if dataset_type == "single": results = self._detect_single_dataset(data, window_size) else: # Multisensor - results_dict: Dict[_Hashable, Dict[str, pd.DataFrame]] = {} + results_dict: dict[_Hashable, dict[str, pd.DataFrame]] = {} for sensor in get_multi_sensor_names(data): results_dict[sensor] = self._detect_single_dataset(data[sensor], window_size) results = invert_result_dictionary(results_dict) @@ -201,20 +201,20 @@ def detect(self: Self, data: SensorData, sampling_rate_hz: float) -> Self: return self @property - def start_(self) -> Union[np.ndarray, Dict[_Hashable, np.ndarray]]: + def start_(self) -> Union[np.ndarray, dict[_Hashable, np.ndarray]]: """Just the start values of all gait sequences.""" if isinstance(self.gait_sequences_, dict): return {k: np.array(v["start"]) for k, v in self.gait_sequences_.items()} return np.array(self.gait_sequences_["start"]) @property - def end_(self) -> Union[np.ndarray, Dict[_Hashable, np.ndarray]]: + def end_(self) -> Union[np.ndarray, dict[_Hashable, np.ndarray]]: """Just the end values of all gait sequences.""" if isinstance(self.gait_sequences_, dict): return {k: np.array(v["end"]) for k, v in self.gait_sequences_.items()} return np.array(self.gait_sequences_["end"]) - def _detect_single_dataset(self, data: pd.DataFrame, window_size: int) -> Dict[str, pd.DataFrame]: + def _detect_single_dataset(self, data: pd.DataFrame, window_size: int) -> dict[str, pd.DataFrame]: """Detect gait sequences for a single sensor data set.""" s_3d, s_1d, active_signal_th, fft_factor = self._signal_extraction(data) @@ -438,8 +438,8 @@ def _assert_input_data(self, data) -> None: ) def _merge_gait_sequences_multi_sensor_data( - self, gait_sequences: Dict[_Hashable, pd.DataFrame] - ) -> Dict[_Hashable, pd.DataFrame]: + self, gait_sequences: dict[_Hashable, pd.DataFrame] + ) -> dict[_Hashable, pd.DataFrame]: """Merge gait sequences from different sensor positions for synced data. Gait sequences from individual sensors are merged using gaitmap.utils.array_handling.merge_intervals. diff --git a/gaitmap_mad/gaitmap_mad/preprocessing/sensor_alignment/_forward_direction_alignment.py b/gaitmap_mad/gaitmap_mad/preprocessing/sensor_alignment/_forward_direction_alignment.py index 54f6a3b0..9641fc24 100644 --- a/gaitmap_mad/gaitmap_mad/preprocessing/sensor_alignment/_forward_direction_alignment.py +++ b/gaitmap_mad/gaitmap_mad/preprocessing/sensor_alignment/_forward_direction_alignment.py @@ -1,6 +1,6 @@ """Correct for 180 degree misalignments between sensor and foot coordinate frame based on forward direction.""" -from typing import Dict, Union +from typing import Union import numpy as np import pandas as pd @@ -111,10 +111,10 @@ class ForwardDirectionSignAlignment(BaseSensorAlignment): data: SensorData sampling_rate_hz: float - rotation_: Union[Rotation, Dict[_Hashable, Rotation]] - is_flipped_: Union[bool, Dict[_Hashable, bool]] - pos_method_: Union[BasePositionMethod, Dict[_Hashable, BasePositionMethod]] - ori_method_: Union[BaseOrientationMethod, Dict[_Hashable, BaseOrientationMethod]] + rotation_: Union[Rotation, dict[_Hashable, Rotation]] + is_flipped_: Union[bool, dict[_Hashable, bool]] + pos_method_: Union[BasePositionMethod, dict[_Hashable, BasePositionMethod]] + ori_method_: Union[BaseOrientationMethod, dict[_Hashable, BaseOrientationMethod]] def __init__( self, diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_barth_dtw.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_barth_dtw.py index 0eddbbf1..6a311486 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_barth_dtw.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_barth_dtw.py @@ -1,7 +1,7 @@ """The msDTW based stride segmentation algorithm by Barth et al 2013.""" import warnings -from typing import Dict, List, NoReturn, Optional, Tuple, Union +from typing import NoReturn, Optional, Union import numpy as np import pandas as pd @@ -170,7 +170,7 @@ class BarthDtw(BaseDtw, BaseStrideSegmentation): def __init__( self, - template: Optional[Union[BaseDtwTemplate, Dict[_Hashable, BaseDtwTemplate]]] = CloneFactory( + template: Optional[Union[BaseDtwTemplate, dict[_Hashable, BaseDtwTemplate]]] = CloneFactory( BarthOriginalTemplate() ), resample_template: bool = True, @@ -229,13 +229,13 @@ def _format_stride_list(array: np.ndarray) -> pd.DataFrame: def _postprocess_matches( self, data, - paths: List, + paths: list, cost: np.ndarray, matches_start_end: np.ndarray, acc_cost_mat: np.ndarray, to_keep: np.ndarray, memory: Memory, - ) -> Tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: # Apply snap to minimum if self.snap_to_min_win_ms: # Late import to avoid circular import diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_base_dtw.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_base_dtw.py index b86927e6..c43beffc 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_base_dtw.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_base_dtw.py @@ -1,7 +1,7 @@ """A implementation of a sDTW that can be used independent of the context of Stride Segmentation.""" import warnings -from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple, TypeVar, Union +from typing import Any, Callable, ClassVar, Optional, TypeVar, Union import numpy as np import pandas as pd @@ -240,7 +240,7 @@ class BaseDtw(BaseAlgorithm): _action_methods = ("segment",) - template: Optional[Union[BaseDtwTemplate, Dict[_Hashable, BaseDtwTemplate]]] + template: Optional[Union[BaseDtwTemplate, dict[_Hashable, BaseDtwTemplate]]] max_cost: Optional[float] resample_template: bool min_match_length_s: Optional[float] @@ -250,10 +250,10 @@ class BaseDtw(BaseAlgorithm): find_matches_method: Literal["min_under_thres", "find_peaks"] memory: Optional[Memory] - matches_start_end_: Union[np.ndarray, Dict[_Hashable, np.ndarray]] - acc_cost_mat_: Union[np.ndarray, Dict[_Hashable, np.ndarray]] - paths_: Union[List[np.ndarray], Dict[_Hashable, List[np.ndarray]]] - costs_: Union[np.ndarray, Dict[_Hashable, np.ndarray]] + matches_start_end_: Union[np.ndarray, dict[_Hashable, np.ndarray]] + acc_cost_mat_: Union[np.ndarray, dict[_Hashable, np.ndarray]] + paths_: Union[list[np.ndarray], dict[_Hashable, list[np.ndarray]]] + costs_: Union[np.ndarray, dict[_Hashable, np.ndarray]] data: Union[np.ndarray, SensorData] sampling_rate_hz: float @@ -277,7 +277,7 @@ def cost_function_(self): @property def matches_start_end_original_( self, - ) -> Union[np.ndarray, Dict[_Hashable, np.ndarray]]: + ) -> Union[np.ndarray, dict[_Hashable, np.ndarray]]: """Return the starts and end directly from the paths. This will not be effected by potential changes of the postprocessing. @@ -289,7 +289,7 @@ def matches_start_end_original_( def __init__( self, - template: Optional[Union[BaseDtwTemplate, Dict[_Hashable, BaseDtwTemplate]]] = None, + template: Optional[Union[BaseDtwTemplate, dict[_Hashable, BaseDtwTemplate]]] = None, resample_template: bool = True, find_matches_method: Literal["min_under_thres", "find_peaks"] = "find_peaks", max_cost: Optional[float] = None, @@ -341,7 +341,7 @@ def _segment( data: Union[np.ndarray, SensorData], sampling_rate_hz: float, memory: Optional[Memory] = None, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: if not memory: memory = Memory(None) @@ -358,8 +358,8 @@ def _segment( assert self.template is not None results: Union[ - Dict[str, Union[np.ndarray, List[np.ndarray]]], - Dict[str, Dict[Union[_Hashable, str], Union[np.ndarray, List[np.ndarray]]]], + dict[str, Union[np.ndarray, list[np.ndarray]]], + dict[str, dict[Union[_Hashable, str], Union[np.ndarray, list[np.ndarray]]]], ] if isinstance(data, np.ndarray): dataset_type = "array" @@ -370,7 +370,7 @@ def _segment( # Single template single sensor: easy results = self._segment_single_dataset(data, template, memory=memory) else: # Multisensor - result_dict: Dict[_Hashable, Dict[str, Union[np.ndarray, List[np.ndarray]]]] = {} + result_dict: dict[_Hashable, dict[str, Union[np.ndarray, list[np.ndarray]]]] = {} if isinstance(template, dict): # multiple templates, multiple sensors: Apply the correct template to the correct sensor. # Ignore the rest @@ -390,8 +390,8 @@ def _segment( return results def _segment_single_dataset( - self, dataset, template: Union[BaseDtwTemplate, Dict[_Hashable, BaseDtwTemplate]], memory: Memory - ) -> Dict[str, Union[np.ndarray, List[np.ndarray]]]: + self, dataset, template: Union[BaseDtwTemplate, dict[_Hashable, BaseDtwTemplate]], memory: Memory + ) -> dict[str, Union[np.ndarray, list[np.ndarray]]]: template_sampling_rate = getattr(template, "sampling_rate_hz", None) if self.resample_template and not template_sampling_rate: raise ValueError( @@ -477,7 +477,7 @@ def _segment_single_dataset( def _select_cost_matrix_method( self, max_template_stretch: float, max_signal_stretch: float - ) -> Tuple[Callable, Dict[str, Any]]: + ) -> tuple[Callable, dict[str, Any]]: """Select the correct function to calculate the cost matrix. This is separate method to make it easy to overwrite by a subclass. @@ -509,13 +509,13 @@ def _find_matches(self, acc_cost_mat, max_cost, min_sequence_length, find_matche def _postprocess_matches( self, data, # noqa: ARG002 - paths: List, # noqa: ARG002 + paths: list, # noqa: ARG002 cost: np.ndarray, # noqa: ARG002 matches_start_end: np.ndarray, acc_cost_mat: np.ndarray, # noqa: ARG002 to_keep: np.ndarray, memory: Memory, # noqa: ARG002 - ) -> Tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: """Apply postprocessing. This can be overwritten by subclasses to filter and modify the matches further. @@ -606,7 +606,7 @@ def _extract_relevant_data_and_template( template: BaseDtwTemplate, data: Union[np.ndarray, pd.DataFrame], sampling_rate_hz: float, - ) -> Tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: """Get the relevant parts of the data based on the provided template and return template and data as array.""" template_array = template.get_data() data_is_numpy = isinstance(data, np.ndarray) @@ -645,7 +645,7 @@ def _extract_relevant_data_and_template( raise ValueError("Invalid combination of data and template") @staticmethod - def _find_multiple_paths(acc_cost_mat: np.ndarray, start_points: np.ndarray) -> List[np.ndarray]: + def _find_multiple_paths(acc_cost_mat: np.ndarray, start_points: np.ndarray) -> list[np.ndarray]: paths = [] for start in start_points: path = subsequence_path(acc_cost_mat, start) diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_constrained_barth_dtw.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_constrained_barth_dtw.py index 9dd1e9fa..6a655dd3 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_constrained_barth_dtw.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_constrained_barth_dtw.py @@ -1,6 +1,6 @@ """A version of BarthDTW that used local warping constrains by default.""" -from typing import Dict, Optional, Union +from typing import Optional, Union from joblib import Memory from tpcp import CloneFactory @@ -135,7 +135,7 @@ class ConstrainedBarthDtw(BarthDtw): def __init__( self, - template: Optional[Union[BaseDtwTemplate, Dict[_Hashable, BaseDtwTemplate]]] = CloneFactory( + template: Optional[Union[BaseDtwTemplate, dict[_Hashable, BaseDtwTemplate]]] = CloneFactory( BarthOriginalTemplate() ), resample_template: bool = True, diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_dtw_templates/templates.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_dtw_templates/templates.py index 8bdda9a0..5bcca1dc 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_dtw_templates/templates.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/dtw/_dtw_templates/templates.py @@ -1,7 +1,8 @@ """Dtw template base classes and helper.""" +from collections.abc import Iterable, Sequence from importlib.resources import open_text -from typing import Iterable, List, Optional, Sequence, Tuple, Union, cast +from typing import Optional, Union, cast import numpy as np import pandas as pd @@ -134,7 +135,7 @@ def self_optimize( data_sequences: Iterable[SingleSensorData], sampling_rate_hz: Optional[float] = None, *, - columns: Optional[List[_Hashable]] = None, + columns: Optional[list[_Hashable]] = None, **_, ) -> Self: """Optimize or recreate the template from data sequences.""" @@ -296,7 +297,7 @@ def self_optimize( data_sequences: Iterable[SingleSensorData], sampling_rate_hz: Optional[float] = None, *, - columns: Optional[List[_Hashable]] = None, + columns: Optional[list[_Hashable]] = None, **_, ): """Create a template from multiple data sequences. @@ -347,8 +348,8 @@ def _create_interpolated_dtw_template( sampling_rate_hz: float, kind: str = "linear", n_samples: Optional[int] = None, - columns: Optional[List[_Hashable]] = None, -) -> Tuple[pd.DataFrame, float]: + columns: Optional[list[_Hashable]] = None, +) -> tuple[pd.DataFrame, float]: expected_col_order = columns arrays = [] for df in signal_sequences: diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_feature_transform.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_feature_transform.py index 312ef74f..5e1c4b42 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_feature_transform.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_feature_transform.py @@ -1,6 +1,6 @@ """Feature transformation class for HMM.""" -from typing import List, NoReturn, Optional +from typing import NoReturn, Optional import numpy as np import pandas as pd @@ -159,8 +159,8 @@ class RothHmmFeatureTransformer(BaseHmmFeatureTransformer): # TODO: Find a way to expose the internal objects instead of exposing just parameters. sampling_rate_feature_space_hz: float low_pass_filter: Optional[BaseFilter] - axes: List[str] - features: List[str] + axes: list[str] + features: list[str] window_size_s: float standardization: bool @@ -168,8 +168,8 @@ def __init__( self, sampling_rate_feature_space_hz: float = 51.2, low_pass_filter: Optional[BaseFilter] = cf(ButterworthFilter(cutoff_freq_hz=10.0, order=4)), - axes: List[str] = cf(["gyr_ml"]), - features: List[str] = cf(["raw", "gradient"]), + axes: list[str] = cf(["gyr_ml"]), + features: list[str] = cf(["raw", "gradient"]), window_size_s: float = 0.2, standardization: bool = True, ) -> None: diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_stride_segmentation.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_stride_segmentation.py index 8e928915..a451f7a9 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_stride_segmentation.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_hmm_stride_segmentation.py @@ -3,7 +3,7 @@ from contextlib import suppress from importlib.resources import open_text from pathlib import Path -from typing import Dict, Generic, Optional, TypeVar, Union +from typing import Generic, Optional, TypeVar, Union import numpy as np import pandas as pd @@ -42,9 +42,12 @@ class PreTrainedRothSegmentationModel(RothSegmentationHmm): def __new__(cls): # try to load models - with open_text( - "gaitmap_mad.stride_segmentation.hmm._pre_trained_models", "fallriskpd_at_lab_model.json" - ) as test_data, Path(test_data.name).open(encoding="utf8") as f: + with ( + open_text( + "gaitmap_mad.stride_segmentation.hmm._pre_trained_models", "fallriskpd_at_lab_model.json" + ) as test_data, + Path(test_data.name).open(encoding="utf8") as f, + ): model_json = f.read() return RothSegmentationHmm.from_json(model_json) @@ -126,9 +129,9 @@ class HmmStrideSegmentation(BaseStrideSegmentation, Generic[BaseSegmentationHmmT data: Union[np.ndarray, SensorData] sampling_rate_hz: float - matches_start_end_: Union[np.ndarray, Dict[str, np.ndarray]] - hidden_state_sequence_: Union[np.ndarray, Dict[str, np.ndarray]] - result_model_: Union[BaseSegmentationHmmT, Dict[str, BaseSegmentationHmmT]] + matches_start_end_: Union[np.ndarray, dict[str, np.ndarray]] + hidden_state_sequence_: Union[np.ndarray, dict[str, np.ndarray]] + result_model_: Union[BaseSegmentationHmmT, dict[str, BaseSegmentationHmmT]] def __init__( self, @@ -142,7 +145,7 @@ def __init__( self.model = model @property - def stride_list_(self) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: + def stride_list_(self) -> Union[pd.DataFrame, dict[str, pd.DataFrame]]: """Return start and end of each match as pd.DataFrame.""" start_ends = self.matches_start_end_ if isinstance(start_ends, dict): @@ -150,7 +153,7 @@ def stride_list_(self) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: return self._format_stride_list(start_ends) @property - def matches_start_end_original_(self) -> Union[np.ndarray, Dict[_Hashable, np.ndarray]]: + def matches_start_end_original_(self) -> Union[np.ndarray, dict[_Hashable, np.ndarray]]: """Return the starts and end directly from the hidden state sequence. This will not be effected by potential changes of the postprocessing. diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_segmentation_model.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_segmentation_model.py index 15b8a6e5..247f6602 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_segmentation_model.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_segmentation_model.py @@ -1,7 +1,8 @@ """Segmentation _model base classes and helper.""" import copy -from typing import Any, Dict, List, Literal, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import Any, Literal, Optional import numpy as np import pandas as pd @@ -102,7 +103,7 @@ class BaseSegmentationHmm(_BaseSerializable): sampling_rate_hz: float @property - def stride_states(self) -> List[int]: + def stride_states(self) -> list[int]: """Get the indexes of the states in the hidden state sequence corresponding to strides.""" raise NotImplementedError @@ -155,7 +156,7 @@ def self_optimize_with_info( data_sequence: Sequence[SingleSensorData], stride_list_sequence: Sequence[SingleSensorStrideList], sampling_rate_hz: float, - ) -> Tuple[Self, Any]: + ) -> tuple[Self, Any]: """Create and train the HMM model based on the given data and labels. This is identical to `self_optimize`, but can return additional information about the training process. @@ -297,7 +298,7 @@ class RothSegmentationHmm(BaseSegmentationHmm, _HackyClonableHMMFix, ShortenedHM n_jobs: int name: Optional[str] model: OptiPara[Optional[pgHMM]] - data_columns: OptiPara[Optional[Tuple[str, ...]]] + data_columns: OptiPara[Optional[tuple[str, ...]]] feature_space_data_: pd.DataFrame hidden_state_sequence_feature_space_: np.ndarray @@ -337,7 +338,7 @@ def __init__( n_jobs: int = 1, name: str = "segmentation_model", model: Optional[pgHMM] = None, - data_columns: Optional[Tuple[str, ...]] = None, + data_columns: Optional[tuple[str, ...]] = None, ) -> None: self.stride_model = stride_model self.transition_model = transition_model @@ -359,12 +360,12 @@ def n_states(self) -> int: return self.transition_model.n_states + self.stride_model.n_states @property - def stride_states(self) -> List[int]: + def stride_states(self) -> list[int]: """Return the ids of the stride states.""" return (np.arange(self.stride_model.n_states) + self.transition_model.n_states).tolist() @property - def transition_states(self) -> List[int]: + def transition_states(self) -> list[int]: """Return the ids of the transition states.""" return np.arange(self.transition_model.n_states).tolist() @@ -477,7 +478,7 @@ def self_optimize_with_info( data_sequence: Sequence[SingleSensorData], stride_list_sequence: Sequence[SingleSensorStrideList], sampling_rate_hz: float, - ) -> Tuple[Self, Dict[Literal["self", "transition_model", "stride_model"], History]]: + ) -> tuple[Self, dict[Literal["self", "transition_model", "stride_model"], History]]: """Create and train the HMM model based on the given data and labels. This is identical to `self_optimize`, but returns additional information about the training process. diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_simple_model.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_simple_model.py index 666869cd..ce3e7371 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_simple_model.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_simple_model.py @@ -2,7 +2,8 @@ import copy import warnings -from typing import Literal, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Literal, Optional, Union import numpy as np import pandas as pd @@ -256,7 +257,7 @@ class SimpleHmm(_BaseSerializable, _HackyClonableHMMFix, ShortenedHMMPrint): n_jobs: int name: Optional[str] model: OptiPara[Optional[pgHMM]] - data_columns: OptiPara[Optional[Tuple[str, ...]]] + data_columns: OptiPara[Optional[tuple[str, ...]]] def __init__( self, @@ -271,7 +272,7 @@ def __init__( n_jobs: int = 1, name: str = "my_model", model: Optional[pgHMM] = None, - data_columns: Optional[Tuple[str, ...]] = None, + data_columns: Optional[tuple[str, ...]] = None, ) -> None: self.n_states = n_states self.n_gmm_components = n_gmm_components @@ -346,7 +347,7 @@ def self_optimize_with_info( self, data_sequence: Sequence[SingleSensorData], labels_sequence: Sequence[Union[np.ndarray, pd.Series, pd.DataFrame]], - ) -> Tuple[Self, History]: + ) -> tuple[Self, History]: """Create and train the HMM model based on the given data and labels. This is identical to `self_optimize`, but returns additional information about the training process. diff --git a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_utils.py b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_utils.py index dd290363..c3ee014f 100644 --- a/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_utils.py +++ b/gaitmap_mad/gaitmap_mad/stride_segmentation/hmm/_utils.py @@ -2,7 +2,7 @@ import json import warnings -from typing import Any, List, Literal, Optional, Set, Tuple +from typing import Any, Literal, Optional import numpy as np import pandas as pd @@ -153,7 +153,7 @@ def __repr_parameter__(self, name: str, value: Any) -> str: return super().__repr_parameter__(name, value) -def create_transition_matrix_fully_connected(n_states: int) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: +def create_transition_matrix_fully_connected(n_states: int) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Create nxn transition matrix with only 1 entries.""" transition_matrix = np.ones((n_states, n_states)) / n_states start_probs = np.ones(n_states) @@ -164,7 +164,7 @@ def create_transition_matrix_fully_connected(n_states: int) -> Tuple[np.ndarray, def create_transition_matrix_left_right( n_states: int, self_transition: bool = True -) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Create nxn transition for left to right model.""" transition_matrix = np.zeros((n_states, n_states)) transition_matrix[range(n_states - 1), range(1, n_states)] = 1 @@ -192,7 +192,7 @@ def print_transition_matrix(model: pg.HiddenMarkovModel, precision: int = 3) -> print(model) -def cluster_data_by_labels(data_list: List[np.ndarray], label_list: List[np.ndarray]): +def cluster_data_by_labels(data_list: list[np.ndarray], label_list: list[np.ndarray]): """Cluster data by labels.""" assert isinstance(label_list, list), "label_list must be list!" assert isinstance(data_list, list), "data_list must be list!" @@ -349,7 +349,7 @@ def get_state_by_name(model: pg.HiddenMarkovModel, state_name: str) -> str: raise ValueError(f"State {state_name} not found within given _model!") -def add_transition(model: pg.HiddenMarkovModel, transition: Tuple[str, str], transition_probability: float) -> None: +def add_transition(model: pg.HiddenMarkovModel, transition: tuple[str, str], transition_probability: float) -> None: """Add a transition to an existing model by state-names. add_transition(model, transition = ("s0","s1"), transition_probability = 0.5) @@ -362,7 +362,7 @@ def add_transition(model: pg.HiddenMarkovModel, transition: Tuple[str, str], tra ) -def get_model_distributions(model: pg.HiddenMarkovModel) -> List[pg.Distribution]: +def get_model_distributions(model: pg.HiddenMarkovModel) -> list[pg.Distribution]: """Return all not None distributions as list from given model.""" distributions = [] for state in model.states: @@ -371,7 +371,7 @@ def get_model_distributions(model: pg.HiddenMarkovModel) -> List[pg.Distribution return distributions -def labels_to_strings(labelsequence: List[Optional[np.ndarray]]) -> List[Optional[List[str]]]: +def labels_to_strings(labelsequence: list[Optional[np.ndarray]]) -> list[Optional[list[str]]]: """Convert label sequence of ints to strings. Pomegranated messes up sorting of states: it will sort like this: s0, s1, s10, s2.... which can lead to unexpected @@ -389,8 +389,8 @@ def labels_to_strings(labelsequence: List[Optional[np.ndarray]]) -> List[Optiona def extract_transitions_starts_stops_from_hidden_state_sequence( - hidden_state_sequence: List[np.ndarray], -) -> Tuple[Set[Tuple[str, str]], np.ndarray, np.ndarray]: + hidden_state_sequence: list[np.ndarray], +) -> tuple[set[tuple[str, str]], np.ndarray, np.ndarray]: """Extract transitions from hidden state sequence. This function will return a list of transitions as well as start and stop labels that can be found within the @@ -500,8 +500,8 @@ def convert_stride_list_to_transition_list( def get_train_data_sequences_transitions( - data_train_sequence: List[SingleSensorData], stride_list_sequence: List[SingleSensorStrideList], n_states: int -) -> Tuple[List[np.ndarray], List[np.ndarray]]: + data_train_sequence: list[SingleSensorData], stride_list_sequence: list[SingleSensorStrideList], n_states: int +) -> tuple[list[np.ndarray], list[np.ndarray]]: """Extract Transition Training set. - data_train_sequence: list of datasets in feature space @@ -540,8 +540,8 @@ def get_train_data_sequences_transitions( def get_train_data_sequences_strides( - data_train_sequence: List[SingleSensorData], stride_list_sequence: List[SingleSensorStrideList], n_states: int -) -> Tuple[List[np.ndarray], List[np.ndarray]]: + data_train_sequence: list[SingleSensorData], stride_list_sequence: list[SingleSensorStrideList], n_states: int +) -> tuple[list[np.ndarray], list[np.ndarray]]: """Extract Transition Training set. - data_train_sequence: list of datasets in feature space @@ -583,7 +583,7 @@ def predict( model: Optional[pg.HiddenMarkovModel], data: pd.DataFrame, *, - expected_columns: Tuple[str, ...], + expected_columns: tuple[str, ...], algorithm: Literal["viterbi", "map"], ) -> np.ndarray: """Predict the hidden state sequence for the given data. diff --git a/tests/conftest.py b/tests/conftest.py index 37f8e5cf..e62e6b5b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ import random -from typing import Any, Dict +from typing import Any import numpy as np import pandas as pd @@ -41,7 +41,7 @@ def reset_random_seed() -> None: healthy_example_position = pytest.fixture()(get_healthy_example_position) -def _get_params_without_nested_class(instance: BaseTpcpObject) -> Dict[str, Any]: +def _get_params_without_nested_class(instance: BaseTpcpObject) -> dict[str, Any]: return {k: v for k, v in instance.get_params().items() if not hasattr(v, "get_params")} diff --git a/tests/test_base.py b/tests/test_base.py index 608ce59c..8d5498d0 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,7 +1,7 @@ """This tests the BaseAlgorithm and fundamental functionality.""" from inspect import Parameter, signature -from typing import Any, Dict, Tuple +from typing import Any import pytest from tpcp import clone, get_action_method, get_action_methods_names, get_action_params, get_results, is_action_applied @@ -59,13 +59,13 @@ def create_test_class(action_method_name, params=None, private_params=None, acti }, ] ) -def example_test_class_initialised(request) -> Tuple[BaseAlgorithm, Dict[str, Any]]: +def example_test_class_initialised(request) -> tuple[BaseAlgorithm, dict[str, Any]]: test_instance = create_test_class(**request.param) return test_instance, request.param @pytest.fixture() -def example_test_class_after_action(example_test_class_initialised) -> Tuple[BaseAlgorithm, Dict[str, Any]]: +def example_test_class_after_action(example_test_class_initialised) -> tuple[BaseAlgorithm, dict[str, Any]]: test_instance, params = example_test_class_initialised action_params = { **params["attributes"], diff --git a/tests/test_data_transforms/test_feature_transformer.py b/tests/test_data_transforms/test_feature_transformer.py index e59bfcd6..11fd3ac3 100644 --- a/tests/test_data_transforms/test_feature_transformer.py +++ b/tests/test_data_transforms/test_feature_transformer.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Dict, Type +from typing import Any, Callable import numpy as np import pandas as pd @@ -136,7 +136,7 @@ def test_require_target_sampling_rate(self) -> None: class _TestSlidingWindowTransformer: __test__ = False - algorithm_class: Type[BaseSlidingWindowFeatureTransform] + algorithm_class: type[BaseSlidingWindowFeatureTransform] @pytest.mark.parametrize( ("window_size_s", "effective_win_size"), [(1, 101), (0.5, 51), (0.1, 11), (0.23, 23), (0.111, 11)] @@ -162,8 +162,8 @@ def test_sampling_rate_required(self) -> None: class TestSlidingWindowTransformers(_TestSlidingWindowTransformer): __test__ = True - algorithm_class: Type[_PandasRollingFeatureTransform] - algo_params: Dict[str, Any] + algorithm_class: type[_PandasRollingFeatureTransform] + algo_params: dict[str, Any] equivalent_method: Callable @pytest.fixture(params=all_rolling_transformer, autouse=True) diff --git a/tests/test_stride_segmentation/test_base_dtw.py b/tests/test_stride_segmentation/test_base_dtw.py index 5c9909e1..42885cad 100644 --- a/tests/test_stride_segmentation/test_base_dtw.py +++ b/tests/test_stride_segmentation/test_base_dtw.py @@ -9,7 +9,7 @@ """ -from typing import Dict, Union +from typing import Union from unittest.mock import patch import numpy as np @@ -382,7 +382,7 @@ def test_sampling_rate_mismatch_warning(self) -> None: class TestMultiSensorInputs(DtwTestBase): - data: Union[pd.DataFrame, Dict[str, pd.DataFrame]] + data: Union[pd.DataFrame, dict[str, pd.DataFrame]] @pytest.fixture(params=("dict", "frame"), autouse=True) def multi_sensor_dataset(self, request) -> None: diff --git a/tests/test_stride_segmentation/test_roi_stride_segmentation.py b/tests/test_stride_segmentation/test_roi_stride_segmentation.py index 9b55ae5e..6809e8bd 100644 --- a/tests/test_stride_segmentation/test_roi_stride_segmentation.py +++ b/tests/test_stride_segmentation/test_roi_stride_segmentation.py @@ -1,5 +1,5 @@ from copy import deepcopy -from typing import Dict, Union +from typing import Union import numpy as np import pandas as pd @@ -168,7 +168,7 @@ def secondary_segment(self: BaseType, data: SensorData, sampling_rate_hz: float, return out @property - def stride_list_(self) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: + def stride_list_(self) -> Union[pd.DataFrame, dict[str, pd.DataFrame]]: return deepcopy(self._stride_list_) diff --git a/tests/test_trajectory_reconstruction/test_trajectory_wrapper.py b/tests/test_trajectory_reconstruction/test_trajectory_wrapper.py index f1b740f6..e97d6ac6 100644 --- a/tests/test_trajectory_reconstruction/test_trajectory_wrapper.py +++ b/tests/test_trajectory_reconstruction/test_trajectory_wrapper.py @@ -1,5 +1,3 @@ -from typing import Dict, Type - import numpy as np import pandas as pd import pytest @@ -22,8 +20,8 @@ class TestIODataStructures: - wrapper_class: Type[BaseTrajectoryReconstructionWrapper] - example_region: Dict[str, pd.DataFrame] + wrapper_class: type[BaseTrajectoryReconstructionWrapper] + example_region: dict[str, pd.DataFrame] key: Literal["s_id", "roi_id"] output_list_type: Literal["roi", "stride"] diff --git a/tests/test_zupt_detection/test_moving_window_zupt_detector.py b/tests/test_zupt_detection/test_moving_window_zupt_detector.py index 79d397a6..3b4f5e48 100644 --- a/tests/test_zupt_detection/test_moving_window_zupt_detector.py +++ b/tests/test_zupt_detection/test_moving_window_zupt_detector.py @@ -1,5 +1,5 @@ from contextlib import nullcontext -from typing import Type, Union +from typing import Union import numpy as np import pandas as pd @@ -46,7 +46,7 @@ def after_action_instance(self, healthy_example_imu_data): class TestNormZuptDetector: """Test the function `sliding_window_view`.""" - algorithm_class: Union[Type[NormZuptDetector], Type[AredZuptDetector]] + algorithm_class: Union[type[NormZuptDetector], type[AredZuptDetector]] @pytest.fixture(params=(NormZuptDetector, AredZuptDetector), autouse=True) def get_algorithm_class(self, request) -> None: From 2d3eaa0d25726090471dea8abf47c31389148808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20K=C3=BCderle?= Date: Fri, 19 Apr 2024 11:20:12 +0200 Subject: [PATCH 4/4] Updated Changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3c11253..aec0e5f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ project. ## [Unreleased] +### Added + +- All orientation and trajectory methods now have a new parameter `rotated_data_` that provides the input data rotated + to the world frame based on the calculated orientation. + (https://github.com/mad-lab-fau/gaitmap/pull/64) + ### Fixed - Fixed a bug that when using `merge_interval` with empty input of shape (0, 2), the output was not empty. @@ -22,7 +28,7 @@ project. - Changed resampling function in inverse feature transform of HMM. Resampling of state sequence is now also possible if the `target_sample_rate` is not a multiple of the HMM sampling rate, e.g. `target_sample_rate=200`, `sample_rate=52.1` (https://github.com/mad-lab-fau/gaitmap/pull/62) -- Dropped Python 3.8 support! +- Dropped Python 3.8 support! (https://github.com/mad-lab-fau/gaitmap/pull/64) ## [2.3.0] - 2023-08-03