biomechzoo 0.5.0__py3-none-any.whl → 0.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of biomechzoo might be problematic. Click here for more details.

@@ -0,0 +1,51 @@
1
+ def movement_onset(yd, constant, etype=etype):
2
+ """
3
+ Extracts movement onset based on the average and standard deviation of a sliding window
4
+ Standard thresholds for running are mean_thresh=1.2, std_thresh=0.2. For walking mean_thresh=0.6, std_thresh=0.2.
5
+
6
+ yd: 1d array of the vector
7
+ constants: [sample_frequency, mean_thresh, std_thresh]
8
+ """
9
+ acc_mag = yd.copy()
10
+
11
+ # ----extract the constants----
12
+ fs = constant[0]
13
+ mean_thresh = constant[1]
14
+ std_thresh = constant[2]
15
+
16
+ # ----sliding window features----
17
+ features = []
18
+ timestamps = []
19
+ window_size = 2 * fs # windows van 2 seconds
20
+ step_size = 1 * fs # with an overlap of 1 seconds
21
+
22
+ for start in range(0, len(acc_mag) - window_size, step_size):
23
+ segment = acc_mag[start:start + window_size]
24
+ mean_val = segment.mean()
25
+ std_val = segment.std()
26
+ # entropy = -np.sum((segment / np.sum(segment)) * np.log2(segment / np.sum(segment) + 1e-12))
27
+ timestamps.append(start)
28
+ features.append((mean_val, std_val))
29
+
30
+ features = np.array(features)
31
+ timestamps = np.array(timestamps)
32
+
33
+ # ----Check already moving else find start----
34
+ initial_window = features[:5] # First few seconds
35
+ if np.all(initial_window[:, 0] > mean_thresh) and np.all(initial_window[:, 1] > std_thresh):
36
+ print("already moving")
37
+ if etype == 'movement_offset':
38
+ index = 0
39
+ else:
40
+ # features, timestamps = self.sliding_window_features(acc_mag)
41
+ movement_flags = (features[:, 0] > mean_thresh) & (features[:, 1] > std_thresh)
42
+ index = None
43
+ for i in range(len(movement_flags) - int(min_duration * self.fs / 50)):
44
+ if np.all(movement_flags[i:i + int(min_duration * self.fs / 50)]):
45
+ index = i
46
+ break
47
+
48
+ if etype == 'movement_offset':
49
+ index = len(yd) - end_time
50
+
51
+ return timestamps[index] if index is not None else timestamps[0]
biomechzoo/biomechzoo.py CHANGED
@@ -1,6 +1,8 @@
1
1
  import os
2
2
  import inspect
3
3
  import time
4
+
5
+ from biomechzoo.imu.tilt_algorithm import tilt_algorithm_data
4
6
  from biomechzoo.utils.engine import engine # assumes this returns .zoo files in folder
5
7
  from biomechzoo.utils.zload import zload
6
8
  from biomechzoo.utils.zsave import zsave
@@ -134,6 +136,28 @@ class BiomechZoo:
134
136
  def parquet2zoo(self, out_folder=None, inplace=None):
135
137
  raise NotImplementedError('Use table2zoo instead')
136
138
 
139
+ def rectify(self):
140
+ raise NotImplementedError
141
+ def tilt_algorithm(self, chname_avert, chname_medlat, chname_antpost, out_folder=None, inplace=False):
142
+ """ tilt correction for acceleration data """
143
+ start_time = time.time()
144
+ verbose = self.verbose
145
+ in_folder = self.in_folder
146
+ if inplace is None:
147
+ inplace = self.inplace
148
+ fl = engine(in_folder, name_contains=self.name_contains, subfolders=self.subfolders)
149
+ for f in fl:
150
+ batchdisp('tilt correction of acceleration channels for {}'.format(f), level=2, verbose=verbose)
151
+ data = zload(f)
152
+ data = tilt_algorithm_data(data, chname_avert, chname_medlat, chname_antpost)
153
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
154
+ method_name = inspect.currentframe().f_code.co_name
155
+ batchdisp(
156
+ '{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time),
157
+ level=1, verbose=verbose)
158
+ # Update self.folder after processing
159
+ self._update_folder(out_folder, inplace, in_folder)
160
+
137
161
  def phase_angle(self, ch, out_folder=None, inplace=None):
138
162
  """ computes phase angles"""
139
163
  start_time = time.time()
@@ -0,0 +1,112 @@
1
+ import numpy as np
2
+ import math
3
+ import pandas as pd
4
+ from biomechzoo.processing.add_channel_data import add_channel_data
5
+
6
+ def tilt_algorithm_data(data,ch_vert, ch_medlat, ch_antpost, plot_or_not=None):
7
+
8
+ # extract channels from data
9
+ avert = data[ch_vert]['line']
10
+ amedlat = data[ch_medlat]['line']
11
+ aantpost = data[ch_antpost]['line']
12
+
13
+ _, avert_corr, amedlat_corr, aantpost_corr = tilt_algorithm_line(avert, amedlat, aantpost)
14
+
15
+ data = add_channel_data(data, ch_vert + '_tilt_corr', avert_corr)
16
+ data = add_channel_data(data, ch_medlat + '_tilt_corr', amedlat_corr)
17
+ data = add_channel_data(data, ch_antpost + '_tilt_corr', aantpost_corr)
18
+
19
+ return data
20
+
21
+
22
+ def tilt_algorithm_line(avert, amedlat, aantpost, plot_or_not=None):
23
+ """
24
+ TiltAlgorithm - to account for gravity and improper tilt alignment of a tri-axial trunk accelerometer.
25
+ Step 1: Extract raw measured (mean) accelerations
26
+ Step 2: Calculate tilt angles
27
+ Step 3: Calculate horizontal dynamic accelerations vectors
28
+ Step 4: Calculate estimated provisional vertical vector
29
+ Step 5: Calculate vertical dynamic vector
30
+ step 6.1: Calculate the contribution of static components
31
+ step 6.2 Transpose static component matrices
32
+ step 7: Remove the static components from the templates of pre and post
33
+
34
+ :param avert: signal predominantly in vertical direction
35
+ :param amedlat: signal predominantly in medio-lateral direction
36
+ :param aantpost: signal predominantly in anterior-posterior direction
37
+ :param plot_or_not: whether to plot the results
38
+ :return: dataframe of the tilt corrected and gravity subtracted vertical, medio-lateral and anterior-posterior
39
+ acceleration signals
40
+ """
41
+ #
42
+
43
+ a_vt = avert.mean()
44
+ a_ml = amedlat.mean()
45
+ a_ap = aantpost.mean()
46
+
47
+ # if avert is negative than turn the sensor around.
48
+ if a_vt < 0.5:
49
+ avert *= -1
50
+ amedlat *= -1
51
+ a_vt = avert.mean()
52
+ a_ml = amedlat.mean()
53
+
54
+ # Anterior tilt
55
+ TiltAngle_ap_rad = np.arcsin(a_ap)
56
+ TiltAngle_ap_deg = math.degrees(TiltAngle_ap_rad)
57
+
58
+ # mediolateral tilt
59
+ TiltAngle_ml_rad = np.arcsin(a_ml)
60
+ TiltAngle_ml_deg = math.degrees(TiltAngle_ml_rad)
61
+
62
+ # Anterior posterior
63
+ a_AP = (a_ap * np.cos(TiltAngle_ap_rad)) - (a_vt * np.sin(TiltAngle_ap_rad))
64
+ # AMediolateral
65
+ a_ML = (a_ml * np.cos(TiltAngle_ml_rad)) - (a_vt * np.sin(TiltAngle_ml_rad))
66
+
67
+ # a_vt_prov = a_ap*Sin(theta_ap) + a_vt*Cos(theta_ap)
68
+ a_vt_prov = (a_ap * np.sin(TiltAngle_ap_rad)) + (a_vt * np.cos(TiltAngle_ap_rad))
69
+
70
+ # a_VT = a_ml*sin(theta_ml) + a_vt_prov*cos(theta_ml) - 1
71
+ a_VT = (a_ml * np.sin(TiltAngle_ml_rad)) + (a_vt_prov * np.cos(TiltAngle_ml_rad)) - 1
72
+
73
+ a_AP_static = a_ap - a_AP
74
+ a_ML_static = a_ml - a_ML
75
+ a_VT_static = a_vt - a_VT
76
+
77
+ a_AP_static = np.transpose(a_AP_static)
78
+ a_ML_static = np.transpose(a_ML_static)
79
+ a_VT_static = np.transpose(a_VT_static)
80
+
81
+ amedlat2 = amedlat - a_ML_static
82
+ avert2 = avert - a_VT_static
83
+ aantpost2 = aantpost - a_AP_static
84
+
85
+ data = {'avert': avert2,
86
+ 'amedlat': amedlat2,
87
+ 'aantpost': aantpost2}
88
+ df_corrected = pd.DataFrame(data)
89
+
90
+ # if plot_or_not:
91
+ # f, ax = plt.subplots(nrows=3, ncols=1, sharex=True, dpi=300)
92
+ # sns.despine(offset=10)
93
+ # f.tight_layout()
94
+ # offset = 0.1
95
+ # f.subplots_adjust(left=0.15, top=0.95)
96
+ #
97
+ # sns.lineplot(avert, ax=ax[0], label='Raw')
98
+ # sns.lineplot(avert2, ax=ax[0], label='tilt corrected')
99
+ # ax[0].set_ylabel('vert acc (g)')
100
+ # ax[0].set_title('Vertical acceleration corrected with {}'.format(np.round(a_VT_static, 2)))
101
+ #
102
+ # sns.lineplot(amedlat, ax=ax[1], label='Raw')
103
+ # sns.lineplot(amedlat2, ax=ax[1], label='Tilt corrected')
104
+ # ax[1].set_ylabel('ml acc (g)')
105
+ # ax[1].set_title('Medio-lateral tilt angle corrected with {} degrees'.format(np.round(TiltAngle_ml_deg, 2)))
106
+ #
107
+ # sns.lineplot(aantpost, ax=ax[2], label='Raw')
108
+ # sns.lineplot(aantpost2, ax=ax[2], label='Tilt corrected')
109
+ # ax[2].set_ylabel('ap acc (g)')
110
+ # ax[2].set_title('Anterior-posterior tilt angle corrected with {} degrees'.format(np.round(TiltAngle_ap_deg, 2)))
111
+
112
+ return df_corrected, avert2, amedlat2, aantpost2
@@ -1,7 +1,9 @@
1
1
  import numpy as np
2
2
  import copy
3
3
  import warnings
4
+ from scipy.signal import find_peaks
4
5
  from biomechzoo.utils.peak_sign import peak_sign
6
+ from biomechzoo.biomech_ops.movement_onset import movement_onset
5
7
 
6
8
  def addevent_data(data, channels, ename, etype, constant=None):
7
9
 
@@ -41,11 +43,17 @@ def addevent_data(data, channels, ename, etype, constant=None):
41
43
  elif etype == 'rom':
42
44
  eyd = float(np.max(yd) - np.min(yd))
43
45
  exd = 0 # dummy index (like MATLAB version)
44
- elif etype == 'max_stance':
46
+ elif etype == 'first peak':
45
47
  # special event for gait and running
46
- exd = max_stance(yd)
47
- eyd = float(yd[exd])
48
+ exd = find_first_peak(yd, constant)
48
49
  eyd = float(yd[exd])
50
+ elif etype == 'movement_onset':
51
+ exd = movement_onset(yd, constant, etype=etype)
52
+ eyd = yd[exd]
53
+ elif etype == 'movement_offset':
54
+ yd2 = yd[::-1].copy() # Reverse the time series.
55
+ exd = movement_onset(yd2, constant, etype=etype)
56
+ eyd = yd[exd]
49
57
  elif etype in ['fs_fp', 'fo_fp']:
50
58
  # --- Handle constant ---
51
59
  if constant is None:
@@ -92,7 +100,17 @@ def addevent_data(data, channels, ename, etype, constant=None):
92
100
 
93
101
  return data_new
94
102
 
95
- def max_stance(yd):
96
- """ extracts max from first 40% of the gait cycle"""
97
- raise NotImplementedError
103
+ def find_first_peak(yd, constant):
104
+ """ extracts first peak from a series of 2 peaks """
105
+ # Find peaks above threshold
106
+ peaks, _ = find_peaks(yd, height=constant)
107
+
108
+ if len(peaks) == 0:
109
+ raise ValueError('No peaks found')
110
+ elif len(peaks) == 1:
111
+ raise ValueError('Only 1 peak found')
112
+ else:
113
+ # Take the first valid peak
114
+ exd = peaks[0]
115
+
98
116
  return exd
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biomechzoo
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Python implementation of the biomechZoo toolbox
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/mcgillmotionlab/biomechzoo
@@ -1,12 +1,13 @@
1
1
  __init__.py,sha256=Uy3ykqw4l_lZKiUWSUFPRZpkZajYUfZLBaQLVhKzxdI,772
2
2
  biomechzoo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  biomechzoo/__main__.py,sha256=hSMHN1Rxn2367fSGTLHoOQ4_pocZw0IWI7HFxl-74oY,88
4
- biomechzoo/biomechzoo.py,sha256=EBh92xNnkYtQan1Q2vS2lnleoMUVEwoDJxw0YPD4FYA,21353
4
+ biomechzoo/biomechzoo.py,sha256=Dma-eZNCud0pSD-OjNkAVlMrynOCysCQwUQXMS3GcQk,22572
5
5
  biomechzoo/biomech_ops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  biomechzoo/biomech_ops/continuous_relative_phase_data.py,sha256=RePbt6zyOI1iv74CWhxSrunIokTFYVfFmFnoW51781E,1300
7
7
  biomechzoo/biomech_ops/continuous_relative_phase_line.py,sha256=Fa1LFRuPlmGPLQLvln6HVnJy3zMSm9z5YeooHmTu0lc,1365
8
8
  biomechzoo/biomech_ops/filter_data.py,sha256=Q9knW23Ft_WeWVCBjaIQ5GkKU0NYV4SdGiDZV8Fm-hM,1805
9
9
  biomechzoo/biomech_ops/filter_line.py,sha256=XKUdRsxU5AO1gSldnwp3qNzsUUL8qpOpAMyQbEMo5uI,2600
10
+ biomechzoo/biomech_ops/movement_onset.py,sha256=qKeNiVTDra_0wjjPTLfP_Gtoyz5cbHD2F_EDxOxo1oQ,1961
10
11
  biomechzoo/biomech_ops/normalize_data.py,sha256=ESdXeUkKdd0WpjP6FW2EYrOAd7NKWkX1JWUNn1SzDB4,1335
11
12
  biomechzoo/biomech_ops/normalize_line.py,sha256=KUE8gEkIolA-VDFCdUuaskk-waO8jjJ20ZMZaS8Qha8,854
12
13
  biomechzoo/biomech_ops/phase_angle_data.py,sha256=_ekUBW2v3iC4UawcDL38ZZLYJmQsAmyqA61Q6_AMtmQ,1435
@@ -16,6 +17,7 @@ biomechzoo/conversion/c3d2zoo_data.py,sha256=28JCj1Jpn7zsv2HjQdzH2F30jnGMwzmbBwY
16
17
  biomechzoo/conversion/mvnx2zoo_data.py,sha256=uMAZ4pNSSZ7NToW1WnawrXeVP8D-xE3dNDnooPvALE4,3545
17
18
  biomechzoo/conversion/opencap2zoo_data.py,sha256=n4bLsrJI0wTSzG5bgJcmxj1dp2plnUKFNRzcTIbmV1o,608
18
19
  biomechzoo/conversion/table2zoo_data.py,sha256=rAP7OrKbVrb50NHJ6h8fgoMntV87-t9wT2xQ30AD96I,3330
20
+ biomechzoo/imu/tilt_algorithm.py,sha256=PYVErXHeY-Wgi0aXCQxDodohTDzirRXo8BEW3_Pskqo,4312
19
21
  biomechzoo/mvn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
22
  biomechzoo/mvn/load_mvnx.py,sha256=B4VGuidQ-5G_5E1t5vpU51Nb_Lu85_EOS_FmGZYjfX8,22499
21
23
  biomechzoo/mvn/main_mvnx.py,sha256=e1LasJQ9icyzjWnRCEcAWQq_-L13-mIzf7PzYA3QYDg,2394
@@ -24,7 +26,7 @@ biomechzoo/mvn/mvnx_file_accessor.py,sha256=Gk2vKq9v_gPbnOS_12zgeJehjFz7v3ClTnN2
24
26
  biomechzoo/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
27
  biomechzoo/processing/add_channel_data.py,sha256=U1xLLBSnyJeeDWzgmHSxOz1hyguzuuDXxQCQ8IFoZkw,1955
26
28
  biomechzoo/processing/addchannel_data.py,sha256=rmnnlMRVkoMlQCR-nRg1jjh-hzMDt37Sx9fAmocrpqA,1953
27
- biomechzoo/processing/addevent_data.py,sha256=T1l7u7cOuILO0mAk3Oac5FjOxH6VBKHHgXUgr8cnCTk,3282
29
+ biomechzoo/processing/addevent_data.py,sha256=GGzXSGNNpZFTEMh0l3awqeBf5jHaCdV84K2Nwjuhqkk,3955
28
30
  biomechzoo/processing/explodechannel_data.py,sha256=ceyXfcCmeNqj4p0zCksVdrnmsYR4t-JHLIyv3JlfNpU,2484
29
31
  biomechzoo/processing/partition_data.py,sha256=4wuKrnMmRMNobymYwZ0WuRvNGsvkhThZ2ZhoSkkhntg,1741
30
32
  biomechzoo/processing/removechannel_data.py,sha256=uO7jjuHapRr2CGLsrvYQ1eJLvb1y_8KR6Ct4M6TPALA,1740
@@ -45,9 +47,9 @@ biomechzoo/utils/version.py,sha256=JIXDUuOcaJiZv9ruMP6PtWvJBh4sP0D5kAvlqPiZK_I,1
45
47
  biomechzoo/utils/zload.py,sha256=_qmbQpiEwUNRcB86aS6dHiytOrz1ZXJVjYkk8h5fg8s,2039
46
48
  biomechzoo/utils/zplot.py,sha256=WVA8aCy1Pqy_bo_HXab9AmW-cBd8J8MPX2LAOd6dznU,1512
47
49
  biomechzoo/utils/zsave.py,sha256=wnRNuDxQc8bwCji4UrfoGjYrSZmka4eaDxQ5rMa78pE,1759
48
- biomechzoo-0.5.0.dist-info/licenses/LICENSE,sha256=Fsz62nrgRORre3A1wNXUDISaHoostodMvocRPDdXc9w,1076
49
- biomechzoo-0.5.0.dist-info/METADATA,sha256=GuXAmEs9sQ2uVq0SzUirvtKWzmWTGC-t6DMNKxWgkiI,1579
50
- biomechzoo-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- biomechzoo-0.5.0.dist-info/entry_points.txt,sha256=VdryUUiwwvx0WZxrgmMrsyfe5Z1jtyaxdXOi0zWHOqk,41
52
- biomechzoo-0.5.0.dist-info/top_level.txt,sha256=nJEtuEZ9UPoN3EOR-BJ6myevEu7B5quWsWhaM_YeQpw,20
53
- biomechzoo-0.5.0.dist-info/RECORD,,
50
+ biomechzoo-0.5.2.dist-info/licenses/LICENSE,sha256=Fsz62nrgRORre3A1wNXUDISaHoostodMvocRPDdXc9w,1076
51
+ biomechzoo-0.5.2.dist-info/METADATA,sha256=_2N7nddOIeDuoVCCXsD8aEggf84O21RBstPq4h3k7pM,1579
52
+ biomechzoo-0.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ biomechzoo-0.5.2.dist-info/entry_points.txt,sha256=VdryUUiwwvx0WZxrgmMrsyfe5Z1jtyaxdXOi0zWHOqk,41
54
+ biomechzoo-0.5.2.dist-info/top_level.txt,sha256=nJEtuEZ9UPoN3EOR-BJ6myevEu7B5quWsWhaM_YeQpw,20
55
+ biomechzoo-0.5.2.dist-info/RECORD,,