biomechzoo 0.5.9__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.
- __init__.py +33 -0
- biomechzoo/__init__.py +0 -0
- biomechzoo/__main__.py +6 -0
- biomechzoo/biomech_ops/__init__.py +0 -0
- biomechzoo/biomech_ops/continuous_relative_phase_data.py +31 -0
- biomechzoo/biomech_ops/continuous_relative_phase_line.py +36 -0
- biomechzoo/biomech_ops/filter_data.py +58 -0
- biomechzoo/biomech_ops/filter_line.py +85 -0
- biomechzoo/biomech_ops/movement_onset.py +53 -0
- biomechzoo/biomech_ops/normalize_data.py +36 -0
- biomechzoo/biomech_ops/normalize_line.py +51 -0
- biomechzoo/biomech_ops/phase_angle_data.py +39 -0
- biomechzoo/biomech_ops/phase_angle_line.py +48 -0
- biomechzoo/biomechzoo.py +447 -0
- biomechzoo/conversion/__init__.py +0 -0
- biomechzoo/conversion/c3d2zoo_data.py +95 -0
- biomechzoo/conversion/mvnx2zoo_data.py +113 -0
- biomechzoo/conversion/opencap2zoo_data.py +23 -0
- biomechzoo/conversion/table2zoo_data.py +114 -0
- biomechzoo/imu/__init__.py +0 -0
- biomechzoo/imu/kinematics.py +0 -0
- biomechzoo/imu/tilt_algorithm.py +112 -0
- biomechzoo/linear_algebra_ops/__init__.py +0 -0
- biomechzoo/linear_algebra_ops/compute_magnitude_data.py +43 -0
- biomechzoo/mvn/__init__.py +0 -0
- biomechzoo/mvn/load_mvnx.py +514 -0
- biomechzoo/mvn/main_mvnx.py +75 -0
- biomechzoo/mvn/mvn.py +232 -0
- biomechzoo/mvn/mvnx_file_accessor.py +464 -0
- biomechzoo/processing/__init__.py +0 -0
- biomechzoo/processing/addchannel_data.py +71 -0
- biomechzoo/processing/addevent_data.py +116 -0
- biomechzoo/processing/explodechannel_data.py +69 -0
- biomechzoo/processing/partition_data.py +46 -0
- biomechzoo/processing/removechannel_data.py +46 -0
- biomechzoo/processing/removeevent_data.py +57 -0
- biomechzoo/processing/renamechannel_data.py +79 -0
- biomechzoo/processing/renameevent_data.py +62 -0
- biomechzoo/processing/split_trial_data.py +40 -0
- biomechzoo/statistics/eventval.py +118 -0
- biomechzoo/utils/__init__.py +0 -0
- biomechzoo/utils/batchdisp.py +21 -0
- biomechzoo/utils/compute_sampling_rate_from_time.py +25 -0
- biomechzoo/utils/engine.py +88 -0
- biomechzoo/utils/findfield.py +11 -0
- biomechzoo/utils/get_split_events.py +33 -0
- biomechzoo/utils/peak_sign.py +24 -0
- biomechzoo/utils/set_zoosystem.py +66 -0
- biomechzoo/utils/version.py +5 -0
- biomechzoo/utils/zload.py +57 -0
- biomechzoo/utils/zplot.py +61 -0
- biomechzoo/utils/zsave.py +54 -0
- biomechzoo-0.5.9.dist-info/METADATA +46 -0
- biomechzoo-0.5.9.dist-info/RECORD +58 -0
- biomechzoo-0.5.9.dist-info/WHEEL +5 -0
- biomechzoo-0.5.9.dist-info/entry_points.txt +2 -0
- biomechzoo-0.5.9.dist-info/licenses/LICENSE +21 -0
- biomechzoo-0.5.9.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from biomechzoo.utils.set_zoosystem import set_zoosystem
|
|
6
|
+
from biomechzoo.utils.compute_sampling_rate_from_time import compute_sampling_rate_from_time
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def table2zoo_data(fl, extension, skip_rows=0, freq=None):
|
|
10
|
+
|
|
11
|
+
if extension == 'csv':
|
|
12
|
+
df, metadata, freq = _csv2zoo(fl, skip_rows=skip_rows, freq=freq)
|
|
13
|
+
|
|
14
|
+
elif extension == 'parquet':
|
|
15
|
+
df, metadata, freq = _parquet2zoo(fl, skip_rows=skip_rows, freq=freq)
|
|
16
|
+
else:
|
|
17
|
+
raise ValueError('extension {} not implemented'.format(extension))
|
|
18
|
+
|
|
19
|
+
# assemble zoo data
|
|
20
|
+
data = {'zoosystem': set_zoosystem()}
|
|
21
|
+
for ch in df.columns:
|
|
22
|
+
data[ch] = {
|
|
23
|
+
'line': df[ch].values,
|
|
24
|
+
'event': []
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# now try to calculate freq from a time column
|
|
29
|
+
if freq is None:
|
|
30
|
+
time_col = [col for col in df.columns if 'time' in col.lower()]
|
|
31
|
+
if time_col is not None and len(time_col) > 0:
|
|
32
|
+
time_data = df[time_col].to_numpy()[:, 0]
|
|
33
|
+
freq = compute_sampling_rate_from_time(time_data)
|
|
34
|
+
else:
|
|
35
|
+
raise ValueError('Unable to compute sampling rate for time column, please specify a sampling frequency'
|
|
36
|
+
)
|
|
37
|
+
# add metadata
|
|
38
|
+
data['zoosystem']['Video']['Freq'] = freq
|
|
39
|
+
data['zoosystem']['Analog']['Freq'] = 'None'
|
|
40
|
+
|
|
41
|
+
if metadata is not None:
|
|
42
|
+
data['zoosystem']['Other'] = metadata
|
|
43
|
+
|
|
44
|
+
return data
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _parquet2zoo(fl, skip_rows=0, freq=None):
|
|
48
|
+
df = pd.read_parquet(fl)
|
|
49
|
+
metadata = None
|
|
50
|
+
return df, metadata, freq
|
|
51
|
+
|
|
52
|
+
def _csv2zoo(fl, skip_rows=0, freq=None):
|
|
53
|
+
header_lines = []
|
|
54
|
+
with open(fl, 'r') as f:
|
|
55
|
+
for line in f:
|
|
56
|
+
header_lines.append(line.strip())
|
|
57
|
+
if line.strip().lower() == 'endheader':
|
|
58
|
+
break
|
|
59
|
+
# Parse metadata
|
|
60
|
+
metadata = _parse_metadata(header_lines)
|
|
61
|
+
|
|
62
|
+
# try to find frequency in metadata
|
|
63
|
+
if freq is None:
|
|
64
|
+
if 'freq' in metadata:
|
|
65
|
+
freq = metadata['freq']
|
|
66
|
+
elif 'sampling_rate' in metadata:
|
|
67
|
+
freq = metadata['sampling_rate']
|
|
68
|
+
else:
|
|
69
|
+
freq = None # or raise an error
|
|
70
|
+
|
|
71
|
+
# read csv
|
|
72
|
+
df = pd.read_csv(fl, skiprows=skip_rows)
|
|
73
|
+
|
|
74
|
+
return df, metadata, freq
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _parse_metadata(header_lines):
|
|
80
|
+
metadata = {}
|
|
81
|
+
for line in header_lines:
|
|
82
|
+
if '=' in line:
|
|
83
|
+
key, val = line.split('=', 1)
|
|
84
|
+
key = key.strip()
|
|
85
|
+
val = val.strip()
|
|
86
|
+
|
|
87
|
+
# Strip trailing commas and whitespace explicitly
|
|
88
|
+
val = val.rstrip(',').strip()
|
|
89
|
+
|
|
90
|
+
# Extract first numeric token if any
|
|
91
|
+
match = re.search(r'[-+]?\d*\.?\d+', val)
|
|
92
|
+
if match:
|
|
93
|
+
num_str = match.group(0)
|
|
94
|
+
try:
|
|
95
|
+
val_num = int(num_str)
|
|
96
|
+
except ValueError:
|
|
97
|
+
val_num = float(num_str)
|
|
98
|
+
else:
|
|
99
|
+
# Now val should be clean of trailing commas, so just lower case it
|
|
100
|
+
val_num = val.lower()
|
|
101
|
+
|
|
102
|
+
metadata[key] = val_num
|
|
103
|
+
return metadata
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == '__main__':
|
|
109
|
+
""" for unit testing"""
|
|
110
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
111
|
+
project_root = os.path.dirname(current_dir)
|
|
112
|
+
csv_file = os.path.join(project_root, 'data', 'other', 'opencap_walking1.csv')
|
|
113
|
+
|
|
114
|
+
data = table2zoo_data(csv_file)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import math
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from biomechzoo.processing.addchannel_data import addchannel_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 = addchannel_data(data, ch_vert + '_tilt_corr', avert_corr)
|
|
16
|
+
data = addchannel_data(data, ch_medlat + '_tilt_corr', amedlat_corr)
|
|
17
|
+
data = addchannel_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
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from biomechzoo.processing.addchannel_data import addchannel_data
|
|
3
|
+
def compute_magnitude_line(x,y,z):
|
|
4
|
+
magnitude = np.sqrt((x**2) + (y**2) + (z **2))
|
|
5
|
+
return magnitude
|
|
6
|
+
|
|
7
|
+
def compute_magnitude_data(data, ch_x, ch_y, ch_z, ch_new_name=None):
|
|
8
|
+
"""
|
|
9
|
+
Compute the magnitude of acceleration data from IMU channels (BiomechZoo format).
|
|
10
|
+
|
|
11
|
+
Returns the magnitude
|
|
12
|
+
"""
|
|
13
|
+
# extract channels from data
|
|
14
|
+
x = data[ch_x]['line']
|
|
15
|
+
y = data[ch_y]['line']
|
|
16
|
+
z = data[ch_z]['line']
|
|
17
|
+
|
|
18
|
+
#calculate the magnitude of the data
|
|
19
|
+
magnitude_data = compute_magnitude_line(x,y,z)
|
|
20
|
+
|
|
21
|
+
# get name of new channel:
|
|
22
|
+
if ch_new_name is None:
|
|
23
|
+
ch_new_name = common_substring_or_concat(ch_x, ch_y, ch_z)
|
|
24
|
+
|
|
25
|
+
#add channels
|
|
26
|
+
data = addchannel_data(data, ch_new_name=ch_new_name + '_mag', ch_new_data=magnitude_data )
|
|
27
|
+
|
|
28
|
+
return data
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def common_substring_or_concat(str1, str2, str3):
|
|
32
|
+
common = ""
|
|
33
|
+
for i in range(len(str1)):
|
|
34
|
+
for j in range(i + 1, len(str1) + 1):
|
|
35
|
+
sub = str1[i:j]
|
|
36
|
+
if sub in str2 and sub in str3 and len(sub) > len(common):
|
|
37
|
+
common = sub
|
|
38
|
+
|
|
39
|
+
# If no common substring found, concatenate all three
|
|
40
|
+
if not common:
|
|
41
|
+
return str1 + str2 + str3
|
|
42
|
+
return common
|
|
43
|
+
|
|
File without changes
|