biomechzoo 0.4.1__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of biomechzoo might be problematic. Click here for more details.
- {biomechzoo-0.4.1/src/biomechzoo.egg-info → biomechzoo-0.4.4}/PKG-INFO +3 -2
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/pyproject.toml +5 -4
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomechzoo.py +104 -61
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/conversion/csv2zoo_data.py +40 -20
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/partition_data.py +4 -4
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/engine.py +6 -1
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/zsave.py +7 -3
- {biomechzoo-0.4.1 → biomechzoo-0.4.4/src/biomechzoo.egg-info}/PKG-INFO +3 -2
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo.egg-info/requires.txt +1 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/LICENSE +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/README.md +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/setup.cfg +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/__init__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/__main__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/__init__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/continuous_relative_phase_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/continuous_relative_phase_line.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/filter_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/filter_line.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/normalize_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/normalize_line.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/phase_angle_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/phase_angle_line.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/conversion/__init__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/conversion/c3d2zoo_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/conversion/mvnx2zoo_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/conversion/opencap2zoo_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/mvn/__init__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/mvn/load_mvnx.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/mvn/main_mvnx.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/mvn/mvn.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/mvn/mvnx_file_accessor.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/__init__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/add_channel_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/addchannel_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/addevent_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/explodechannel_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/removechannel_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/renamechannel_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/renameevent_data.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/split_trial_by_gait_cycle.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/__init__.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/batchdisp.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/compute_sampling_rate_from_time.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/findfield.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/get_split_events.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/split_trial.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/zload.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/zplot.py +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo.egg-info/SOURCES.txt +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo.egg-info/dependency_links.txt +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo.egg-info/entry_points.txt +0 -0
- {biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo.egg-info/top_level.txt +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: biomechzoo
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Python implementation of the biomechZoo toolbox
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/mcgillmotionlab/biomechzoo
|
|
7
|
-
Requires-Python:
|
|
7
|
+
Requires-Python: <3.12,>=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: ezc3d>=1.5.19
|
|
@@ -12,6 +12,7 @@ Requires-Dist: matplotlib>=3.10.6
|
|
|
12
12
|
Requires-Dist: numpy==2.2.6
|
|
13
13
|
Requires-Dist: pandas>=2.3.2
|
|
14
14
|
Requires-Dist: scipy>=1.16.2
|
|
15
|
+
Requires-Dist: pyarrow>=19.0.0
|
|
15
16
|
Dynamic: license-file
|
|
16
17
|
|
|
17
18
|
# BiomechZoo for Python
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "biomechzoo"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.4"
|
|
4
4
|
description = "Python implementation of the biomechZoo toolbox"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.11"
|
|
6
|
+
requires-python = ">=3.11,<3.12" # max version for opencap-process tools is 3.11
|
|
7
7
|
license = "MIT"
|
|
8
8
|
license-files = ["LICEN[CS]E*"]
|
|
9
9
|
dependencies = [
|
|
10
|
-
"ezc3d>=1.5.19",
|
|
10
|
+
"ezc3d>=1.5.19", # for reading c3d files
|
|
11
11
|
"matplotlib>=3.10.6",
|
|
12
|
-
"numpy==2.2.6",
|
|
12
|
+
"numpy==2.2.6", # max version for opencap-process tools
|
|
13
13
|
"pandas>=2.3.2",
|
|
14
14
|
"scipy>=1.16.2",
|
|
15
|
+
"pyarrow>=19.0.0", # for parquet support
|
|
15
16
|
]
|
|
16
17
|
|
|
17
18
|
[project.urls]
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import inspect
|
|
3
|
+
import time
|
|
2
4
|
from biomechzoo.utils.engine import engine # assumes this returns .zoo files in folder
|
|
3
5
|
from biomechzoo.utils.zload import zload
|
|
4
6
|
from biomechzoo.utils.zsave import zsave
|
|
@@ -20,15 +22,27 @@ from biomechzoo.biomech_ops.continuous_relative_phase_data import continuous_rel
|
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class BiomechZoo:
|
|
23
|
-
def __init__(self, in_folder, inplace=False, verbose=0):
|
|
25
|
+
def __init__(self, in_folder, inplace=False, subfolders=None, name_contains=None, verbose=0):
|
|
24
26
|
self.verbose = verbose
|
|
25
27
|
self.in_folder = in_folder
|
|
26
28
|
self.verbose = verbose
|
|
27
|
-
self.inplace = inplace
|
|
29
|
+
self.inplace = inplace # choice to save processed files to new folder
|
|
30
|
+
self.subfolders = subfolders # only run processes on list in subfolder
|
|
31
|
+
self.name_contains = name_contains # only run processes on files with name_contains in file name
|
|
28
32
|
|
|
29
33
|
batchdisp('BiomechZoo initialized', level=1, verbose=verbose)
|
|
30
34
|
batchdisp('verbosity set to: {}'.format(verbose), level=1, verbose=verbose)
|
|
31
|
-
batchdisp('processing folder set to: {}'.format(self.in_folder), level=1, verbose=verbose)
|
|
35
|
+
batchdisp('root processing folder set to: {}'.format(self.in_folder), level=1, verbose=verbose)
|
|
36
|
+
if name_contains is not None:
|
|
37
|
+
batchdisp('only include files containing name_contains string: {}'.format(self.name_contains), level=1, verbose=verbose)
|
|
38
|
+
if subfolders is not None:
|
|
39
|
+
if type(subfolders) is list:
|
|
40
|
+
batchdisp('only process files in subfolder(s):', level=1, verbose=verbose)
|
|
41
|
+
for subfolder in self.subfolders:
|
|
42
|
+
batchdisp('{}'.format(os.path.join(self.in_folder, subfolder)), level=1, verbose=verbose)
|
|
43
|
+
else:
|
|
44
|
+
batchdisp('only process files in subfolder(s): {}'.format(os.path.join(self.in_folder, self.subfolders)), level=1, verbose=verbose)
|
|
45
|
+
|
|
32
46
|
if inplace:
|
|
33
47
|
batchdisp('Processing mode: overwrite (inplace=True) (each step will be applied to same folder)', level=1, verbose=verbose)
|
|
34
48
|
else:
|
|
@@ -50,57 +64,75 @@ class BiomechZoo:
|
|
|
50
64
|
|
|
51
65
|
def mvnx2zoo(self, out_folder=None, inplace=False):
|
|
52
66
|
""" Converts all .mvnx files in the folder to .zoo format """
|
|
67
|
+
start_time = time.time()
|
|
53
68
|
verbose = self.verbose
|
|
54
69
|
in_folder = self.in_folder
|
|
55
70
|
if inplace is None:
|
|
56
71
|
inplace = self.inplace
|
|
57
|
-
|
|
58
|
-
fl = engine(in_folder, extension='.mvnx')
|
|
72
|
+
fl = engine(in_folder, extension='.mvnx', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
59
73
|
for f in fl:
|
|
60
74
|
batchdisp('converting mvnx to zoo for {}'.format(f), level=2, verbose=verbose)
|
|
61
75
|
data = mvnx2zoo_data(f)
|
|
62
76
|
f_zoo = f.replace('.mvnx', '.zoo')
|
|
63
77
|
zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
79
|
+
batchdisp('{} conversion complete for {} files'.format(method_name, len(fl)), level=1, verbose=verbose)
|
|
66
80
|
# Update self.folder after processing
|
|
67
81
|
self._update_folder(out_folder, inplace, in_folder)
|
|
68
82
|
|
|
69
83
|
def c3d2zoo(self, out_folder=None, inplace=None):
|
|
70
84
|
""" Converts all .c3d files in the folder to .zoo format """
|
|
85
|
+
start_time = time.time()
|
|
71
86
|
from ezc3d import c3d
|
|
72
87
|
verbose = self.verbose
|
|
73
88
|
in_folder = self.in_folder
|
|
74
89
|
if inplace is None:
|
|
75
90
|
inplace = self.inplace
|
|
76
|
-
|
|
77
|
-
fl = engine(in_folder, extension='.c3d')
|
|
91
|
+
fl = engine(in_folder, extension='.c3d', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
78
92
|
for f in fl:
|
|
79
93
|
batchdisp('converting c3d to zoo for {}'.format(f), level=2, verbose=verbose)
|
|
80
94
|
c3d_obj = c3d(f)
|
|
81
95
|
data = c3d2zoo_data(c3d_obj)
|
|
82
96
|
f_zoo = f.replace('.c3d', '.zoo')
|
|
83
97
|
zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
99
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
86
100
|
# Update self.folder after processing
|
|
87
101
|
self._update_folder(out_folder, inplace, in_folder)
|
|
88
102
|
|
|
89
|
-
def csv2zoo(self, out_folder=None, inplace=None):
|
|
103
|
+
def csv2zoo(self, out_folder=None, inplace=None, skip_rows=0):
|
|
90
104
|
""" Converts generic .csv file in the folder to .zoo format """
|
|
105
|
+
start_time = time.time()
|
|
91
106
|
verbose = self.verbose
|
|
92
107
|
in_folder = self.in_folder
|
|
93
108
|
if inplace is None:
|
|
94
109
|
inplace = self.inplace
|
|
95
|
-
|
|
96
|
-
fl = engine(in_folder, extension='.csv')
|
|
110
|
+
fl = engine(in_folder, extension='.csv', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
97
111
|
for f in fl:
|
|
98
112
|
batchdisp('converting csv to zoo for {}'.format(f), level=2, verbose=verbose)
|
|
99
|
-
data = csv2zoo_data(f)
|
|
113
|
+
data = csv2zoo_data(f, type='csv', skip_rows=skip_rows)
|
|
100
114
|
f_zoo = f.replace('.csv', '.zoo')
|
|
101
115
|
zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
102
|
-
|
|
116
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
117
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
118
|
+
# Update self.folder after processing
|
|
119
|
+
self._update_folder(out_folder, inplace, in_folder)
|
|
103
120
|
|
|
121
|
+
def parquet2zoo(self, out_folder=None, inplace=None):
|
|
122
|
+
""" Converts generic .csv file in the folder to .zoo format """
|
|
123
|
+
start_time = time.time()
|
|
124
|
+
verbose = self.verbose
|
|
125
|
+
in_folder = self.in_folder
|
|
126
|
+
if inplace is None:
|
|
127
|
+
inplace = self.inplace
|
|
128
|
+
fl = engine(in_folder, extension='.parquet', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
129
|
+
for f in fl:
|
|
130
|
+
batchdisp('converting parquet to zoo for {}'.format(f), level=2, verbose=verbose)
|
|
131
|
+
data = csv2zoo_data(f, type='parquet')
|
|
132
|
+
f_zoo = f.replace('.parquet', '.zoo')
|
|
133
|
+
zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
134
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
135
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
104
136
|
# Update self.folder after processing
|
|
105
137
|
self._update_folder(out_folder, inplace, in_folder)
|
|
106
138
|
|
|
@@ -110,67 +142,70 @@ class BiomechZoo:
|
|
|
110
142
|
|
|
111
143
|
def phase_angle(self, ch, out_folder=None, inplace=None):
|
|
112
144
|
""" computes phase angles"""
|
|
145
|
+
start_time = time.time()
|
|
113
146
|
verbose = self.verbose
|
|
114
147
|
in_folder = self.in_folder
|
|
115
148
|
if inplace is None:
|
|
116
149
|
inplace = self.inplace
|
|
117
|
-
|
|
118
|
-
fl = engine(in_folder)
|
|
150
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
119
151
|
for f in fl:
|
|
120
152
|
if verbose:
|
|
121
153
|
batchdisp('computing phase angles for {}'.format(f), level=2, verbose=verbose)
|
|
122
154
|
data = zload(f)
|
|
123
155
|
data = phase_angle_data(data, ch)
|
|
124
|
-
zsave(f, data, inplace=inplace,
|
|
125
|
-
|
|
156
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
157
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
158
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
126
159
|
|
|
127
160
|
# Update self.folder after processing
|
|
128
161
|
self._update_folder(out_folder, inplace, in_folder)
|
|
129
162
|
|
|
130
163
|
def continuous_relative_phase(self, ch_prox, ch_dist, out_folder=None, inplace=None):
|
|
131
164
|
""" computes CRP angles"""
|
|
165
|
+
start_time = time.time()
|
|
132
166
|
verbose = self.verbose
|
|
133
167
|
in_folder = self.in_folder
|
|
134
168
|
if inplace is None:
|
|
135
169
|
inplace = self.inplace
|
|
136
|
-
|
|
137
|
-
fl = engine(in_folder)
|
|
170
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
138
171
|
for f in fl:
|
|
139
172
|
if verbose:
|
|
140
173
|
batchdisp('computing CRP angles between channel {} (prox) and {} (dist) for {}'.format(ch_prox, ch_dist, f), level=2, verbose=verbose)
|
|
141
174
|
data = zload(f)
|
|
142
175
|
data = continuous_relative_phase_data(data, ch_dist, ch_prox)
|
|
143
|
-
zsave(f, data, inplace=inplace,
|
|
144
|
-
|
|
176
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
177
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
178
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
145
179
|
|
|
146
180
|
# Update self.folder after processing
|
|
147
181
|
self._update_folder(out_folder, inplace, in_folder)
|
|
148
182
|
|
|
149
183
|
def split_trial_by_gait_cycle(self, first_event_name, out_folder=None, inplace=None):
|
|
150
184
|
""" split by gait cycle according to event_name"""
|
|
185
|
+
start_time = time.time()
|
|
151
186
|
verbose = self.verbose
|
|
152
187
|
in_folder = self.in_folder
|
|
153
188
|
if inplace is None:
|
|
154
189
|
inplace = self.inplace
|
|
155
|
-
|
|
156
|
-
fl = engine(in_folder)
|
|
190
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
157
191
|
for f in fl:
|
|
158
192
|
f_name = os.path.splitext(os.path.basename(f))[0]
|
|
159
|
-
batchdisp('splitting by gait cycle for {} by {}'.format(f, first_event_name), level=2, verbose=verbose)
|
|
160
193
|
data = zload(f)
|
|
161
194
|
split_events = get_split_events(data, first_event_name)
|
|
162
195
|
if split_events is None:
|
|
163
196
|
print('no event {} found, saving original file'.format(first_event_name))
|
|
164
|
-
zsave(f, data, inplace=inplace,
|
|
197
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
165
198
|
else:
|
|
166
199
|
for i, _ in enumerate(split_events[0:-1]):
|
|
167
200
|
fl_new = f.replace(f_name, f_name + '_' + str(i + 1))
|
|
168
201
|
start = split_events[i]
|
|
169
202
|
end = split_events[i + 1]
|
|
203
|
+
batchdisp('splitting by gait cycle from {} to {} for {}'.format(start, end, f), level=2,
|
|
204
|
+
verbose=verbose)
|
|
170
205
|
data_new = split_trial(data, start, end)
|
|
171
|
-
zsave(fl_new, data_new, inplace=inplace,
|
|
172
|
-
|
|
173
|
-
batchdisp('
|
|
206
|
+
zsave(fl_new, data_new, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
207
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
208
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
174
209
|
|
|
175
210
|
# Update self.folder after processing
|
|
176
211
|
self._update_folder(out_folder, inplace, in_folder)
|
|
@@ -195,130 +230,137 @@ class BiomechZoo:
|
|
|
195
230
|
# self._update_folder(out_folder, inplace, in_folder)
|
|
196
231
|
def renameevent(self, evt, nevt, out_folder=None, inplace=None):
|
|
197
232
|
""" renames event evt to nevt in all zoo files """
|
|
233
|
+
start_time = time.time()
|
|
198
234
|
verbose = self.verbose
|
|
199
235
|
in_folder = self.in_folder
|
|
200
236
|
if inplace is None:
|
|
201
237
|
inplace = self.inplace
|
|
202
|
-
|
|
203
|
-
fl = engine(in_folder)
|
|
238
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
204
239
|
for f in fl:
|
|
205
240
|
batchdisp('renaming events from {} to {} for {}'.format(evt, nevt ,f), level=2, verbose=verbose)
|
|
206
241
|
data = zload(f)
|
|
207
242
|
data = renameevent_data(data, evt, nevt)
|
|
208
|
-
zsave(f, data, inplace=inplace,
|
|
209
|
-
|
|
243
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
244
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
245
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
210
246
|
|
|
211
247
|
# Update self.folder after processing
|
|
212
248
|
self._update_folder(out_folder, inplace, in_folder)
|
|
213
249
|
|
|
214
250
|
def renamechannnel(self, ch, ch_new, out_folder=None, inplace=None):
|
|
215
251
|
""" renames channels from ch to ch_new in all zoo files """
|
|
252
|
+
start_time = time.time()
|
|
216
253
|
verbose = self.verbose
|
|
217
254
|
in_folder = self.in_folder
|
|
218
255
|
if inplace is None:
|
|
219
256
|
inplace = self.inplace
|
|
220
|
-
|
|
221
|
-
fl = engine(in_folder)
|
|
257
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
222
258
|
for f in fl:
|
|
223
259
|
batchdisp('renaming channels from {} to {} for {}'.format(ch, ch_new ,f), level=2, verbose=verbose)
|
|
224
260
|
data = zload(f)
|
|
225
261
|
data = renamechannel_data(data, ch, ch_new)
|
|
226
|
-
zsave(f, data, inplace=inplace,
|
|
227
|
-
|
|
262
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
263
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
264
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
228
265
|
|
|
229
266
|
# Update self.folder after processing
|
|
230
267
|
self._update_folder(out_folder, inplace, in_folder)
|
|
231
268
|
|
|
232
269
|
def removechannel(self, ch, mode='remove', out_folder=None, inplace=None):
|
|
233
270
|
""" removes channels from zoo files """
|
|
271
|
+
start_time = time.time()
|
|
234
272
|
verbose = self.verbose
|
|
235
273
|
in_folder = self.in_folder
|
|
236
274
|
if inplace is None:
|
|
237
275
|
inplace = self.inplace
|
|
238
|
-
|
|
239
|
-
fl = engine(in_folder)
|
|
276
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
240
277
|
for f in fl:
|
|
241
278
|
batchdisp('removing channels for {}'.format(f), level=2, verbose=verbose)
|
|
242
279
|
data = zload(f)
|
|
243
280
|
data = removechannel_data(data, ch, mode)
|
|
244
|
-
zsave(f, data, inplace=inplace,
|
|
245
|
-
|
|
281
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
282
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
283
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
246
284
|
|
|
247
285
|
# Update self.folder after processing
|
|
248
286
|
self._update_folder(out_folder, inplace, in_folder)
|
|
249
287
|
|
|
250
288
|
def explodechannel(self, out_folder=None, inplace=None):
|
|
251
289
|
""" explodes all channels in a zoo file """
|
|
290
|
+
start_time = time.time()
|
|
252
291
|
verbose = self.verbose
|
|
253
292
|
in_folder = self.in_folder
|
|
254
293
|
if inplace is None:
|
|
255
294
|
inplace = self.inplace
|
|
256
|
-
|
|
257
|
-
fl = engine(in_folder)
|
|
295
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
258
296
|
for f in fl:
|
|
259
297
|
if verbose:
|
|
260
298
|
batchdisp('removing channels for {}'.format(f), level=2, verbose=verbose)
|
|
261
299
|
data = zload(f)
|
|
262
300
|
data = explodechannel_data(data)
|
|
263
|
-
zsave(f, data, inplace=inplace,
|
|
264
|
-
|
|
301
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
302
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
303
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
265
304
|
|
|
266
305
|
# Update self.folder after processing
|
|
267
306
|
self._update_folder(out_folder, inplace, in_folder)
|
|
268
307
|
|
|
269
308
|
def normalize(self, nlen=101, out_folder=None, inplace=None):
|
|
270
309
|
""" time normalizes all channels to length nlen """
|
|
310
|
+
start_time = time.time()
|
|
271
311
|
verbose = self.verbose
|
|
272
312
|
in_folder = self.in_folder
|
|
273
313
|
if inplace is None:
|
|
274
314
|
inplace = self.inplace
|
|
275
|
-
|
|
276
|
-
fl = engine(in_folder)
|
|
315
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
277
316
|
for f in fl:
|
|
278
317
|
if verbose:
|
|
279
318
|
batchdisp('normalizing channels to length {} for {}'.format(nlen, f), level=2, verbose=verbose)
|
|
280
319
|
data = zload(f)
|
|
281
320
|
data = normalize_data(data, nlen)
|
|
282
|
-
zsave(f, data, inplace=inplace,
|
|
283
|
-
|
|
321
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
322
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
323
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
284
324
|
|
|
285
325
|
# Update self.folder after processing
|
|
286
326
|
self._update_folder(out_folder, inplace, in_folder)
|
|
287
327
|
|
|
288
328
|
def addevent(self, ch, evt_type, evt_name, out_folder=None, inplace=None):
|
|
289
329
|
""" adds events of type evt_type with name evt_name to channel ch """
|
|
330
|
+
start_time = time.time()
|
|
290
331
|
verbose = self.verbose
|
|
291
332
|
in_folder = self.in_folder
|
|
292
333
|
if inplace is None:
|
|
293
334
|
inplace = self.inplace
|
|
294
|
-
|
|
295
|
-
fl = engine(in_folder)
|
|
335
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
296
336
|
for f in fl:
|
|
297
337
|
if verbose:
|
|
298
338
|
batchdisp('adding event {} to channel {} for {}'.format(evt_type, ch, f), level=2, verbose=verbose)
|
|
299
339
|
data = zload(f)
|
|
300
340
|
data = addevent_data(data, ch, evt_type, evt_name)
|
|
301
|
-
zsave(f, data, inplace=inplace,
|
|
302
|
-
|
|
341
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
342
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
343
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
303
344
|
|
|
304
345
|
# Update self.folder after processing
|
|
305
346
|
self._update_folder(out_folder, inplace, in_folder)
|
|
306
347
|
|
|
307
348
|
def partition(self, evt_start, evt_end, out_folder=None, inplace=None):
|
|
308
349
|
""" partitions data between events evt_start and evt_end """
|
|
350
|
+
start_time = time.time()
|
|
309
351
|
verbose = self.verbose
|
|
310
352
|
in_folder = self.in_folder
|
|
311
353
|
if inplace is None:
|
|
312
354
|
inplace = self.inplace
|
|
313
|
-
|
|
314
|
-
fl = engine(in_folder)
|
|
355
|
+
fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
|
|
315
356
|
for f in fl:
|
|
316
357
|
if verbose:
|
|
317
358
|
batchdisp('partitioning data between events {} and {} for {}'.format(evt_start, evt_end, f), level=2, verbose=verbose)
|
|
318
359
|
data = zload(f)
|
|
319
360
|
data = partition_data(data, evt_start, evt_end)
|
|
320
|
-
zsave(f, data, inplace=inplace,
|
|
321
|
-
|
|
361
|
+
zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
|
|
362
|
+
method_name = inspect.currentframe().f_code.co_name
|
|
363
|
+
batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
|
|
322
364
|
# Update self.folder after processing
|
|
323
365
|
self._update_folder(out_folder, inplace, in_folder)
|
|
324
366
|
|
|
@@ -340,8 +382,9 @@ class BiomechZoo:
|
|
|
340
382
|
# batchdisp('filtering data in channels {} for {}'.format(ch, f), level=2, verbose=verbose)
|
|
341
383
|
# data = zload(f)
|
|
342
384
|
# data = filter_data(data, ch, filt)
|
|
343
|
-
# zsave(f, data, inplace=inplace, root_folder=in_folder, out_folder=out_folder)
|
|
344
|
-
#
|
|
385
|
+
# zsave(f, data, inplace=inplace, root_folder=in_folder, out_folder=out_folder, verbose=verbose)
|
|
386
|
+
# method_name = inspect.currentframe().f_code.co_name
|
|
387
|
+
# batchdisp('{} computation complete for {} file(s)'.format(method_name, len(fl)), level=1, verbose=verbose)
|
|
345
388
|
#
|
|
346
389
|
# # Update self.folder after processing
|
|
347
390
|
# self._update_folder(out_folder, inplace, in_folder)
|
|
@@ -5,25 +5,32 @@ import re
|
|
|
5
5
|
from biomechzoo.utils.compute_sampling_rate_from_time import compute_sampling_rate_from_time
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def csv2zoo_data(csv_path,
|
|
8
|
+
def csv2zoo_data(csv_path, type='csv',skip_rows=0, freq=None):
|
|
9
|
+
# todo: check calculation for sampling rate
|
|
9
10
|
|
|
10
11
|
# Read header lines until 'endheader'
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
metadata = {}
|
|
13
|
+
if type == 'csv' and skip_rows > 0:
|
|
14
|
+
header_lines = []
|
|
15
|
+
with open(csv_path, 'r') as f:
|
|
16
|
+
for line in f:
|
|
17
|
+
header_lines.append(line.strip())
|
|
18
|
+
if line.strip().lower() == 'endheader':
|
|
19
|
+
break
|
|
20
|
+
# Parse metadata
|
|
21
|
+
metadata = _parse_metadata(header_lines)
|
|
22
|
+
|
|
23
|
+
if type == 'csv':
|
|
24
|
+
df = pd.read_csv(csv_path, skiprows=skip_rows)
|
|
25
|
+
elif type =='parquet':
|
|
26
|
+
df = pd.read_parquet(csv_path)
|
|
27
|
+
else:
|
|
28
|
+
raise ValueError('type must be csv or parquet')
|
|
29
|
+
|
|
30
|
+
# Use all columns
|
|
31
|
+
data = df.iloc[:, 0:]
|
|
32
|
+
|
|
33
|
+
# assemble zoo data
|
|
27
34
|
zoo_data = {}
|
|
28
35
|
for ch in data.columns:
|
|
29
36
|
zoo_data[ch] = {
|
|
@@ -31,13 +38,26 @@ def csv2zoo_data(csv_path, header_len=10):
|
|
|
31
38
|
'event': []
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
#
|
|
35
|
-
|
|
41
|
+
# try to find frequency in metadata
|
|
42
|
+
if freq is None:
|
|
43
|
+
if 'freq' in metadata:
|
|
44
|
+
freq = metadata['freq']
|
|
45
|
+
elif 'sampling_rate' in metadata:
|
|
46
|
+
freq = metadata['sampling_rate']
|
|
47
|
+
else:
|
|
48
|
+
freq = None # or raise an error
|
|
49
|
+
|
|
50
|
+
# now try to calculate freq from a time column
|
|
51
|
+
if freq is None:
|
|
52
|
+
time_col = [col for col in df.columns if 'time' in col.lower()]
|
|
53
|
+
if time_col is not None:
|
|
54
|
+
time_data = df[time_col].to_numpy()[:,0]
|
|
55
|
+
freq = compute_sampling_rate_from_time(time_data)
|
|
36
56
|
|
|
37
57
|
# add metadata
|
|
38
58
|
# todo update zoosystem to match biomechzoo requirements
|
|
39
59
|
zoo_data['zoosystem'] = metadata
|
|
40
|
-
zoo_data['zoosystem']['Freq'] =
|
|
60
|
+
zoo_data['zoosystem']['Freq'] = freq
|
|
41
61
|
|
|
42
62
|
return zoo_data
|
|
43
63
|
|
|
@@ -6,14 +6,14 @@ def partition_data(data, evt_start, evt_end):
|
|
|
6
6
|
""" partition data for all channels between events evt_start and evt_end"""
|
|
7
7
|
|
|
8
8
|
# extract event values
|
|
9
|
-
e1, _ = findfield(data,evt_start)
|
|
10
|
-
e2, _ = findfield(data,evt_end)
|
|
9
|
+
e1, _ = findfield(data, evt_start)
|
|
10
|
+
e2, _ = findfield(data, evt_end)
|
|
11
11
|
|
|
12
12
|
data_new = data.copy()
|
|
13
13
|
for ch_name, ch_data in data_new.items():
|
|
14
14
|
if ch_name != 'zoosystem':
|
|
15
15
|
try:
|
|
16
|
-
data_new[ch_name]['line'] = ch_data[e1[0]:e2[0],
|
|
16
|
+
data_new[ch_name]['line'] = ch_data[e1[0]:e2[0],]
|
|
17
17
|
except (IndexError, ValueError) as e:
|
|
18
18
|
# IndexError: if e1[0]:e2[0] goes beyond the available indices
|
|
19
19
|
# ValueError: less likely, but may arise with shape mismatches
|
|
@@ -48,4 +48,4 @@ def _partition_event(event_dict, evt_start, evt_end, arr_len):
|
|
|
48
48
|
#
|
|
49
49
|
#
|
|
50
50
|
#
|
|
51
|
-
# return event_dict_new
|
|
51
|
+
# return event_dict_new
|
|
@@ -19,7 +19,7 @@ def engine(root_folder, extension='.zoo', subfolders=None, name_contains=None, v
|
|
|
19
19
|
Arguments:
|
|
20
20
|
root_folder (str): The root directory path where the search begins.
|
|
21
21
|
extension (str): File extension to search for (e.g., '.zoo', '.c3d'). Default .zoo
|
|
22
|
-
subfolders (list
|
|
22
|
+
subfolders (list or str, optional): List of folder names to restrict the search to.
|
|
23
23
|
Only files inside these folders (or their subfolders) are included.
|
|
24
24
|
If None, search all subfolders.
|
|
25
25
|
name_contains (str, optional): Substring that must be present in the filename
|
|
@@ -28,6 +28,11 @@ def engine(root_folder, extension='.zoo', subfolders=None, name_contains=None, v
|
|
|
28
28
|
Returns:
|
|
29
29
|
list of str: List of full file paths matching the criteria.
|
|
30
30
|
"""
|
|
31
|
+
# check format of subfolder (string or list)
|
|
32
|
+
if subfolders is not None:
|
|
33
|
+
if type(subfolders) is str:
|
|
34
|
+
subfolders = [subfolders]
|
|
35
|
+
|
|
31
36
|
matched_files = []
|
|
32
37
|
|
|
33
38
|
subfolders_set = set(subfolders) if subfolders else None
|
|
@@ -2,8 +2,10 @@ from scipy.io import savemat
|
|
|
2
2
|
import inspect
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
from biomechzoo.utils.batchdisp import batchdisp
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
def zsave(fl, data, inplace=True, out_folder=None, root_folder=None, verbose=False):
|
|
7
9
|
"""
|
|
8
10
|
Save zoo data to .zoo file (MAT format)
|
|
9
11
|
|
|
@@ -28,9 +30,9 @@ def zsave(fl, data, inplace=True, out_folder=None, root_folder=None):
|
|
|
28
30
|
|
|
29
31
|
# Determine save path
|
|
30
32
|
if inplace:
|
|
31
|
-
|
|
33
|
+
fl_new = fl
|
|
34
|
+
out_dir = os.path.dirname(fl)
|
|
32
35
|
else:
|
|
33
|
-
filename = os.path.basename(fl)
|
|
34
36
|
if out_folder is None:
|
|
35
37
|
out_folder = 'processed'
|
|
36
38
|
|
|
@@ -47,4 +49,6 @@ def zsave(fl, data, inplace=True, out_folder=None, root_folder=None):
|
|
|
47
49
|
|
|
48
50
|
# Save the .zoo file
|
|
49
51
|
savemat(fl_new, data)
|
|
52
|
+
batchdisp('all files saved to ' + out_dir, level=1, verbose=verbose)
|
|
53
|
+
|
|
50
54
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: biomechzoo
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Python implementation of the biomechZoo toolbox
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/mcgillmotionlab/biomechzoo
|
|
7
|
-
Requires-Python:
|
|
7
|
+
Requires-Python: <3.12,>=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: ezc3d>=1.5.19
|
|
@@ -12,6 +12,7 @@ Requires-Dist: matplotlib>=3.10.6
|
|
|
12
12
|
Requires-Dist: numpy==2.2.6
|
|
13
13
|
Requires-Dist: pandas>=2.3.2
|
|
14
14
|
Requires-Dist: scipy>=1.16.2
|
|
15
|
+
Requires-Dist: pyarrow>=19.0.0
|
|
15
16
|
Dynamic: license-file
|
|
16
17
|
|
|
17
18
|
# BiomechZoo for Python
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/continuous_relative_phase_data.py
RENAMED
|
File without changes
|
{biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/biomech_ops/continuous_relative_phase_line.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/processing/split_trial_by_gait_cycle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{biomechzoo-0.4.1 → biomechzoo-0.4.4}/src/biomechzoo/utils/compute_sampling_rate_from_time.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|