autogaita 1.5.2__py3-none-any.whl → 1.5.4__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.
- autogaita-1.5.4.dist-info/METADATA +173 -0
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/RECORD +13 -20
- autogaita-1.5.4.dist-info/top_level.txt +2 -0
- autogaita_backup/common2D_run_and_done_gui.py +414 -0
- autogaita_backup/dlc_1_preparation.py +426 -0
- autogaita_backup/dlc_2_sc_extraction.py +217 -0
- autogaita_backup/dlc_gui.py +359 -0
- autogaita_backup/sleap_1_preparation.py +303 -0
- autogaita_backup/sleap_2_sc_extraction.py +167 -0
- autogaita_backup/sleap_gui.py +359 -0
- autogaita-1.5.2.dist-info/METADATA +0 -40
- autogaita-1.5.2.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/test_common2D_unit_1_preparation.py +0 -340
- tests/test_common2D_unit_2_sc_extraction.py +0 -367
- tests/test_common2D_unit_3_analysis.py +0 -245
- tests/test_common2D_unit_utils.py +0 -137
- tests/test_dlc_approval.py +0 -110
- tests/test_dlc_unit_1_preparation.py +0 -114
- tests/test_group_approval.py +0 -162
- tests/test_group_unit.py +0 -393
- tests/test_universal3D_approval.py +0 -89
- tests/test_universal3D_unit_1_preparation.py +0 -69
- tests/test_universal3D_unit_2_sc_extraction.py +0 -108
- tests/test_universal3D_unit_3_analysis.py +0 -248
- tests/test_utils.py +0 -442
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/WHEEL +0 -0
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/entry_points.txt +0 -0
- {autogaita-1.5.2.dist-info → autogaita-1.5.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
from autogaita.universal3D.universal3D_3_analysis import (
|
|
2
|
-
standardise_y_z_flip_gait_add_features_to_one_step,
|
|
3
|
-
add_features,
|
|
4
|
-
)
|
|
5
|
-
from hypothesis import HealthCheck, given, settings, strategies as st
|
|
6
|
-
import pytest
|
|
7
|
-
import pandas as pd
|
|
8
|
-
import pandas.testing as pdt
|
|
9
|
-
import numpy as np
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# %%................................ fixtures ........................................
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def extract_info(tmp_path):
|
|
16
|
-
info = {}
|
|
17
|
-
info["name"] = "SK"
|
|
18
|
-
info["results_dir"] = os.path.join(tmp_path, info["name"])
|
|
19
|
-
return info
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@pytest.fixture
|
|
23
|
-
def extract_folderinfo():
|
|
24
|
-
folderinfo = {}
|
|
25
|
-
folderinfo["root_dir"] = "tests/test_data/universal3D_data/test_data"
|
|
26
|
-
folderinfo["sctable_filename"] = "SC Latency Table.xlsx"
|
|
27
|
-
folderinfo["postname_string"] = ""
|
|
28
|
-
return folderinfo
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@pytest.fixture
|
|
32
|
-
def extract_cfg():
|
|
33
|
-
# note space in end of "Midfoot, left " must be here bc. we don't run our fix and
|
|
34
|
-
# check cfg function of 1_prep_
|
|
35
|
-
cfg = {}
|
|
36
|
-
cfg["sampling_rate"] = 100 # base cfg
|
|
37
|
-
cfg["dont_show_plots"] = True
|
|
38
|
-
cfg["y_acceleration"] = True
|
|
39
|
-
cfg["angular_acceleration"] = True
|
|
40
|
-
cfg["bin_num"] = 25
|
|
41
|
-
cfg["plot_SE"] = True
|
|
42
|
-
cfg["standardise_z_at_SC_level"] = True
|
|
43
|
-
cfg["standardise_z_to_a_joint"] = True
|
|
44
|
-
cfg["z_standardisation_joint"] = ["Midfoot, left "]
|
|
45
|
-
cfg["plot_joint_number"] = 5
|
|
46
|
-
cfg["color_palette"] = "Set2"
|
|
47
|
-
cfg["legend_outside"] = True
|
|
48
|
-
cfg["flip_gait_direction"] = True
|
|
49
|
-
cfg["analyse_average_y"] = True
|
|
50
|
-
cfg["standardise_y_coordinates"] = True
|
|
51
|
-
cfg["y_standardisation_joint"] = ["Midfoot, left "]
|
|
52
|
-
cfg["coordinate_standardisation_xls"] = ""
|
|
53
|
-
cfg["joints"] = [
|
|
54
|
-
"Midfoot",
|
|
55
|
-
"Ankle",
|
|
56
|
-
"Knee",
|
|
57
|
-
"Hip",
|
|
58
|
-
]
|
|
59
|
-
cfg["angles"] = {
|
|
60
|
-
"name": ["Ankle", "Knee"],
|
|
61
|
-
"lower_joint": ["Midfoot", "Ankle"],
|
|
62
|
-
"upper_joint": ["Knee", "Hip"],
|
|
63
|
-
}
|
|
64
|
-
cfg["direction_joint"] = "Midfoot, left Y"
|
|
65
|
-
return cfg
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
sample_step_len = 10 # 10 datapoints (e.g. time)
|
|
69
|
-
sample_step_col_num = 20 # 10 for each Y and Z
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@pytest.fixture
|
|
73
|
-
def sample_step():
|
|
74
|
-
joint_strings = [
|
|
75
|
-
"Midfoot, left",
|
|
76
|
-
"Ankle, left",
|
|
77
|
-
"Knee, left",
|
|
78
|
-
"Hip, left",
|
|
79
|
-
"Midfoot, right",
|
|
80
|
-
"Ankle, right",
|
|
81
|
-
"Knee, right",
|
|
82
|
-
"Hip, right",
|
|
83
|
-
"Shoulder",
|
|
84
|
-
"Neck",
|
|
85
|
-
]
|
|
86
|
-
sample_step = {}
|
|
87
|
-
for joint in joint_strings:
|
|
88
|
-
for coord in [" Y", " Z"]:
|
|
89
|
-
sample_step[joint + coord] = list(
|
|
90
|
-
np.random.randint(1, 101, sample_step_len)
|
|
91
|
-
)
|
|
92
|
-
sample_step["Time"] = np.arange(sample_step_len)
|
|
93
|
-
return pd.DataFrame(sample_step)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# ............................ for property tests ....................................
|
|
97
|
-
# 1. define the sample_steps_data strategy using random ints as data
|
|
98
|
-
sample_steps_data_strategy = st.lists(
|
|
99
|
-
st.lists(
|
|
100
|
-
st.integers(min_value=-1000, max_value=1000),
|
|
101
|
-
min_size=sample_step_col_num, # 20 cols (10 for each Y and Z)
|
|
102
|
-
max_size=sample_step_col_num,
|
|
103
|
-
),
|
|
104
|
-
min_size=sample_step_len, # 10 rows
|
|
105
|
-
max_size=sample_step_len,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# 2. create a custom decorator that can be used instead of writing given/settings
|
|
110
|
-
# always
|
|
111
|
-
def sample_data_for_property_tests(func):
|
|
112
|
-
return settings(suppress_health_check=(HealthCheck.function_scoped_fixture,))(
|
|
113
|
-
given(sample_steps_data=sample_steps_data_strategy)(func)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# .................................. tests .........................................
|
|
118
|
-
# %% workflow step #3 - y-flipping, y-stand, features, df-creation & exports
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def test_standardise_z_at_SC_level(sample_step, extract_info, extract_cfg):
|
|
122
|
-
extract_cfg["standardise_z_at_SC_level"] = True
|
|
123
|
-
extract_cfg["standardise_z_to_a_joint"] = False
|
|
124
|
-
extract_cfg["flip_gait_direction"] = False
|
|
125
|
-
extract_cfg["standardise_y_coordinates"] = False
|
|
126
|
-
z_cols = [col for col in sample_step.columns if col.endswith("Z")]
|
|
127
|
-
function_step = standardise_y_z_flip_gait_add_features_to_one_step(
|
|
128
|
-
sample_step, 10, extract_info, extract_cfg
|
|
129
|
-
)
|
|
130
|
-
sample_step = add_features(
|
|
131
|
-
sample_step, extract_info, extract_cfg
|
|
132
|
-
) # otherwise df-shape mismatch
|
|
133
|
-
steps_global_z_minimum = sample_step[z_cols].min().min() # global == all joints
|
|
134
|
-
expected_step = sample_step.copy()
|
|
135
|
-
expected_step[z_cols] -= steps_global_z_minimum
|
|
136
|
-
pdt.assert_frame_equal(function_step, expected_step)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
# because we have random integers in step, we might get a near-zero vectors when
|
|
140
|
-
# computing angles - ignore those warnings
|
|
141
|
-
@pytest.mark.filterwarnings("ignore:invalid value")
|
|
142
|
-
def test_flip_gait_direction(sample_step, extract_info, extract_cfg):
|
|
143
|
-
# prepare some vars
|
|
144
|
-
extract_cfg["flip_gait_direction"] = True
|
|
145
|
-
extract_cfg["standardise_y_coordinates"] = False
|
|
146
|
-
global_Y_max = 10
|
|
147
|
-
to_be_flipped_step = sample_step.copy()
|
|
148
|
-
to_not_be_flipped_step = sample_step.copy()
|
|
149
|
-
y_cols = [col for col in sample_step.columns if col.endswith("Y")]
|
|
150
|
-
# step sample is random integers, so first fix the direction of y-values, either
|
|
151
|
-
# increasing or decreasing (decreasing must be flipped)
|
|
152
|
-
for col in y_cols:
|
|
153
|
-
to_be_flipped_step[col] = to_be_flipped_step[col].sort_values(
|
|
154
|
-
ascending=False, ignore_index=True
|
|
155
|
-
)
|
|
156
|
-
to_not_be_flipped_step[col] = to_not_be_flipped_step[col].sort_values(
|
|
157
|
-
ascending=True, ignore_index=True
|
|
158
|
-
)
|
|
159
|
-
to_be_flipped_step = standardise_y_z_flip_gait_add_features_to_one_step(
|
|
160
|
-
to_be_flipped_step, global_Y_max, extract_info, extract_cfg
|
|
161
|
-
)
|
|
162
|
-
to_not_be_flipped_step = standardise_y_z_flip_gait_add_features_to_one_step(
|
|
163
|
-
to_not_be_flipped_step, global_Y_max, extract_info, extract_cfg
|
|
164
|
-
)
|
|
165
|
-
# first test if the y-values are flipped (increasing y-cols progressively)
|
|
166
|
-
for col in y_cols:
|
|
167
|
-
assert to_be_flipped_step[col][0] < to_be_flipped_step[col].mean()
|
|
168
|
-
# now, test that if you reverse the impact of global_y_max you get your original df
|
|
169
|
-
# 1. reverse subtraction of global y max before comparison for df-equivalence
|
|
170
|
-
# => e.g. 10 - 2 = 8 || reverse via: 10 - 8 = 2!
|
|
171
|
-
to_be_flipped_step[y_cols] = global_Y_max - to_be_flipped_step[y_cols]
|
|
172
|
-
# 2. reverting of 1. changes y-values to now be decreasing (2 4 8 becomes 8 4 2)
|
|
173
|
-
# => sooo manually make it so that it is increasing again and then you should have
|
|
174
|
-
# equal dfs
|
|
175
|
-
for col in y_cols:
|
|
176
|
-
to_be_flipped_step[col] = to_be_flipped_step[col].sort_values(
|
|
177
|
-
ascending=True, ignore_index=True
|
|
178
|
-
)
|
|
179
|
-
# 3. remove the cols that were created by add_features, because those won't match
|
|
180
|
-
# (understandably)
|
|
181
|
-
# fmt:off
|
|
182
|
-
cols_to_drop = [col for col in to_be_flipped_step.columns if not (col.endswith("Z") or col.endswith("Y"))]
|
|
183
|
-
for df in [to_be_flipped_step, to_not_be_flipped_step]:
|
|
184
|
-
df.drop(columns=cols_to_drop, inplace=True)
|
|
185
|
-
# fmt:on
|
|
186
|
-
pdt.assert_frame_equal(to_be_flipped_step, to_not_be_flipped_step)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
# because we have random integers in step, we might get a near-zero vectors when
|
|
190
|
-
# computing angles - ignore those warnings
|
|
191
|
-
@pytest.mark.filterwarnings("ignore:invalid value")
|
|
192
|
-
@sample_data_for_property_tests
|
|
193
|
-
def test_standardise_y_coordinates_no_gait_flipping(
|
|
194
|
-
sample_step, extract_info, extract_cfg, sample_steps_data
|
|
195
|
-
):
|
|
196
|
-
extract_cfg["standardise_y_coordinates"] = True
|
|
197
|
-
extract_cfg["flip_gait_direction"] = False
|
|
198
|
-
y_cols = [col for col in sample_step.columns if col.endswith("Y")]
|
|
199
|
-
# prep data
|
|
200
|
-
# => because we are property testing, insert the hypothesis-generated data into the
|
|
201
|
-
# sample_step df (which has the correct columns)
|
|
202
|
-
# => the -1 in pd.df line is because we first want to exclude the time column...
|
|
203
|
-
sample_step = pd.DataFrame(columns=sample_step.columns[:-1], data=sample_steps_data)
|
|
204
|
-
sample_step["Time"] = np.arange(len(sample_step)) # ... to include it manually
|
|
205
|
-
non_stand_step, y_stand_step = standardise_y_z_flip_gait_add_features_to_one_step(
|
|
206
|
-
sample_step, 10, extract_info, extract_cfg
|
|
207
|
-
)
|
|
208
|
-
steps_y_min = (
|
|
209
|
-
sample_step[extract_cfg["y_standardisation_joint"][0] + "Y"].min().min()
|
|
210
|
-
)
|
|
211
|
-
non_stand_step[y_cols] -= steps_y_min
|
|
212
|
-
pdt.assert_frame_equal(non_stand_step, y_stand_step)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def test_standardise_y_coordinates_gait_flipping(
|
|
216
|
-
sample_step, extract_info, extract_cfg
|
|
217
|
-
):
|
|
218
|
-
# prep vars
|
|
219
|
-
extract_cfg["standardise_y_coordinates"] = True
|
|
220
|
-
extract_cfg["flip_gait_direction"] = True
|
|
221
|
-
global_Y_max = 10
|
|
222
|
-
y_cols = [col for col in sample_step.columns if col.endswith("Y")]
|
|
223
|
-
|
|
224
|
-
# ---------------
|
|
225
|
-
# NOTE FOR MYSELF
|
|
226
|
-
# => look at this again - returned math domain errors if property testing
|
|
227
|
-
# => uncomment the sample_step =... line & use the @sample_data.. fixture as in the
|
|
228
|
-
# test above to have another look
|
|
229
|
-
# => because we are property testing, insert the hypothesis-generated data into the
|
|
230
|
-
# sample_step df (which has the correct columns)
|
|
231
|
-
# sample_step = pd.DataFrame(columns=sample_step.columns, data=sample_steps_data)
|
|
232
|
-
# ---------------
|
|
233
|
-
|
|
234
|
-
# run function on a to-be-flipped step
|
|
235
|
-
to_be_flipped_step = sample_step.copy()
|
|
236
|
-
for col in y_cols:
|
|
237
|
-
to_be_flipped_step[col] = to_be_flipped_step[col].sort_values(
|
|
238
|
-
ascending=False, ignore_index=True
|
|
239
|
-
)
|
|
240
|
-
non_stand_step, y_stand_step = standardise_y_z_flip_gait_add_features_to_one_step(
|
|
241
|
-
to_be_flipped_step, global_Y_max, extract_info, extract_cfg
|
|
242
|
-
)
|
|
243
|
-
steps_y_min = (
|
|
244
|
-
non_stand_step[extract_cfg["y_standardisation_joint"][0] + "Y"].min().min()
|
|
245
|
-
)
|
|
246
|
-
reverted_step = y_stand_step.copy()
|
|
247
|
-
reverted_step[y_cols] += steps_y_min
|
|
248
|
-
pdt.assert_frame_equal(reverted_step, non_stand_step)
|
tests/test_utils.py
DELETED
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
from autogaita.resources.utils import (
|
|
2
|
-
standardise_primary_joint_coordinates,
|
|
3
|
-
compute_angle,
|
|
4
|
-
define_bins,
|
|
5
|
-
write_angle_warning,
|
|
6
|
-
)
|
|
7
|
-
from autogaita.common2D.common2D_1_preparation import some_prep as some_prep_2D
|
|
8
|
-
from autogaita.universal3D.universal3D_1_preparation import some_prep as some_prep_3D
|
|
9
|
-
from autogaita.common2D.common2D_2_sc_extraction import extract_stepcycles
|
|
10
|
-
from autogaita.common2D.common2D_3_analysis import analyse_and_export_stepcycles
|
|
11
|
-
import os
|
|
12
|
-
import math
|
|
13
|
-
import numpy as np
|
|
14
|
-
import pandas as pd
|
|
15
|
-
import pandas.testing as pdt
|
|
16
|
-
import pytest
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# %%........................... 2D GaitA fixtures ....................................
|
|
20
|
-
@pytest.fixture
|
|
21
|
-
def extract_2D_info(tmp_path):
|
|
22
|
-
info = {}
|
|
23
|
-
info["mouse_num"] = 15
|
|
24
|
-
info["run_num"] = 3
|
|
25
|
-
info["name"] = "ID " + str(info["mouse_num"]) + " - Run " + str(info["run_num"])
|
|
26
|
-
info["results_dir"] = os.path.join(tmp_path, info["name"])
|
|
27
|
-
return info
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@pytest.fixture
|
|
31
|
-
def extract_2D_folderinfo():
|
|
32
|
-
folderinfo = {}
|
|
33
|
-
folderinfo["root_dir"] = "tests/test_data/dlc_data"
|
|
34
|
-
folderinfo["sctable_filename"] = (
|
|
35
|
-
"correct_annotation_table.xlsx" # has to be an excel file
|
|
36
|
-
)
|
|
37
|
-
folderinfo["data_string"] = "SIMINewOct"
|
|
38
|
-
folderinfo["beam_string"] = "BeamTraining"
|
|
39
|
-
folderinfo["premouse_string"] = "Mouse"
|
|
40
|
-
folderinfo["postmouse_string"] = "25mm"
|
|
41
|
-
folderinfo["prerun_string"] = "run"
|
|
42
|
-
folderinfo["postrun_string"] = "6DLC"
|
|
43
|
-
return folderinfo
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@pytest.fixture
|
|
47
|
-
def extract_2D_cfg():
|
|
48
|
-
cfg = {}
|
|
49
|
-
cfg["sampling_rate"] = 100
|
|
50
|
-
cfg["subtract_beam"] = False
|
|
51
|
-
cfg["dont_show_plots"] = True
|
|
52
|
-
cfg["convert_to_mm"] = False # false!
|
|
53
|
-
cfg["pixel_to_mm_ratio"] = 3.76
|
|
54
|
-
cfg["x_sc_broken_threshold"] = 200
|
|
55
|
-
cfg["y_sc_broken_threshold"] = 50
|
|
56
|
-
cfg["x_acceleration"] = True
|
|
57
|
-
cfg["angular_acceleration"] = True
|
|
58
|
-
cfg["save_to_xls"] = True
|
|
59
|
-
cfg["bin_num"] = 25
|
|
60
|
-
cfg["plot_SE"] = True
|
|
61
|
-
cfg["standardise_y_at_SC_level"] = False
|
|
62
|
-
cfg["standardise_y_to_a_joint"] = True
|
|
63
|
-
cfg["y_standardisation_joint"] = ["Knee"]
|
|
64
|
-
cfg["plot_joint_number"] = 3
|
|
65
|
-
cfg["color_palette"] = "viridis"
|
|
66
|
-
cfg["legend_outside"] = True
|
|
67
|
-
cfg["invert_y_axis"] = True
|
|
68
|
-
cfg["flip_gait_direction"] = False
|
|
69
|
-
cfg["analyse_average_x"] = True
|
|
70
|
-
cfg["standardise_x_coordinates"] = True
|
|
71
|
-
cfg["x_standardisation_joint"] = ["Hind paw tao"]
|
|
72
|
-
cfg["coordinate_standardisation_xls"] = ""
|
|
73
|
-
cfg["hind_joints"] = ["Hind paw tao", "Ankle", "Knee", "Hip", "Iliac Crest"]
|
|
74
|
-
cfg["fore_joints"] = [
|
|
75
|
-
"Front paw tao ",
|
|
76
|
-
"Wrist ",
|
|
77
|
-
"Elbow ",
|
|
78
|
-
"Lower Shoulder ",
|
|
79
|
-
"Upper Shoulder ",
|
|
80
|
-
]
|
|
81
|
-
cfg["beam_col_left"] = ["BeamLeft"] # BEAM_COL_LEFT & _RIGHT must be lists of len=1
|
|
82
|
-
cfg["beam_col_right"] = ["BeamRight"]
|
|
83
|
-
cfg["beam_hind_jointadd"] = ["Tail base ", "Tail center ", "Tail tip "]
|
|
84
|
-
cfg["beam_fore_jointadd"] = ["Nose ", "Ear base "]
|
|
85
|
-
cfg["angles"] = {
|
|
86
|
-
"name": ["Ankle ", "Knee ", "Hip "],
|
|
87
|
-
"lower_joint": ["Hind paw tao ", "Ankle ", "Knee "],
|
|
88
|
-
"upper_joint": ["Knee ", "Hip ", "Iliac Crest "],
|
|
89
|
-
}
|
|
90
|
-
return cfg
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# %%........................... 3D GaitA fixtures ....................................
|
|
94
|
-
@pytest.fixture
|
|
95
|
-
def extract_3D_info(tmp_path):
|
|
96
|
-
info = {}
|
|
97
|
-
info["name"] = "TestSubject"
|
|
98
|
-
info["results_dir"] = os.path.join(tmp_path, info["name"])
|
|
99
|
-
return info
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
@pytest.fixture
|
|
103
|
-
def extract_3D_folderinfo():
|
|
104
|
-
folderinfo = {}
|
|
105
|
-
folderinfo["root_dir"] = "tests/test_data/universal3D_data/test_data/"
|
|
106
|
-
folderinfo["sctable_filename"] = "SC Latency Table"
|
|
107
|
-
folderinfo["postname_string"] = ""
|
|
108
|
-
return folderinfo
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@pytest.fixture
|
|
112
|
-
def extract_3D_cfg():
|
|
113
|
-
cfg = {}
|
|
114
|
-
cfg["sampling_rate"] = 100
|
|
115
|
-
cfg["dont_show_plots"] = True
|
|
116
|
-
cfg["y_acceleration"] = True
|
|
117
|
-
cfg["angular_acceleration"] = True
|
|
118
|
-
cfg["bin_num"] = 25
|
|
119
|
-
cfg["plot_SE"] = True
|
|
120
|
-
cfg["standardise_z_at_SC_level"] = True
|
|
121
|
-
cfg["standardise_z_to_a_joint"] = False
|
|
122
|
-
cfg["z_standardisation_joint"] = ["Midfoot, left"]
|
|
123
|
-
cfg["plot_joint_number"] = 7
|
|
124
|
-
cfg["legend_outside"] = True
|
|
125
|
-
cfg["flip_gait_direction"] = False
|
|
126
|
-
cfg["color_palette"] = "viridis"
|
|
127
|
-
cfg["analyse_average_y"] = False
|
|
128
|
-
cfg["standardise_y_coordinates"] = True
|
|
129
|
-
cfg["y_standardisation_joint"] = ["Midfoot, left"]
|
|
130
|
-
cfg["coordinate_standardisation_xls"] = ""
|
|
131
|
-
cfg["joints"] = ["Midfoot", "Ankle", "Knee", "Hip", "Pelvis "]
|
|
132
|
-
cfg["angles"] = {
|
|
133
|
-
"name": ["Ankle", "Knee", "Hip"],
|
|
134
|
-
"lower_joint": ["Midfoot", "Ankle", "Knee"],
|
|
135
|
-
"upper_joint": ["Knee", "Hip", "Pelvis "],
|
|
136
|
-
}
|
|
137
|
-
return cfg
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
# %% ................................. tests .........................................
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def test_compute_angle():
|
|
144
|
-
# Test case 1: Basic case
|
|
145
|
-
joint1 = [0, 0]
|
|
146
|
-
joint2 = [1, 0]
|
|
147
|
-
joint3 = [0, 1]
|
|
148
|
-
expected_angle = 90
|
|
149
|
-
result, _ = compute_angle(joint1, joint2, joint3)
|
|
150
|
-
assert math.isclose(result, expected_angle)
|
|
151
|
-
# Test case 2: two joints are equal - this won't happen in autogaita because of the
|
|
152
|
-
# test in extract_stepcycles but I want to make sure that the function returns
|
|
153
|
-
# broken=True correctly
|
|
154
|
-
joint1 = [5, 5]
|
|
155
|
-
joint2 = [2, 2]
|
|
156
|
-
joint3 = [2, 2]
|
|
157
|
-
expected_angle = None
|
|
158
|
-
result, broken = compute_angle(joint1, joint2, joint3)
|
|
159
|
-
assert result == 0
|
|
160
|
-
assert broken is True
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def test_write_angle_warning(extract_2D_cfg, extract_2D_info):
|
|
164
|
-
# prep: remove existing Issues.txt
|
|
165
|
-
results_dir = extract_2D_info["results_dir"]
|
|
166
|
-
if not os.path.exists(results_dir):
|
|
167
|
-
os.makedirs(results_dir)
|
|
168
|
-
issues_path = os.path.join(results_dir, "Issues.txt")
|
|
169
|
-
if os.path.exists(issues_path):
|
|
170
|
-
os.remove(issues_path)
|
|
171
|
-
# prep: more vars
|
|
172
|
-
step = pd.DataFrame()
|
|
173
|
-
step["Time"] = [0.1, 0.2, 0.3]
|
|
174
|
-
step["dummy_coord"] = [1, 2, 3] # need this too otherwise stuff breaks
|
|
175
|
-
angles = extract_2D_cfg["angles"]
|
|
176
|
-
a = 0
|
|
177
|
-
broken_angle_idxs = [0, 2]
|
|
178
|
-
# run
|
|
179
|
-
write_angle_warning(step, a, angles, broken_angle_idxs, extract_2D_info)
|
|
180
|
-
# assert
|
|
181
|
-
with open(issues_path, "r") as f:
|
|
182
|
-
issues = f.read()
|
|
183
|
-
assert (
|
|
184
|
-
"Angle: Ankle" in issues # bc. of extract_2D_cfg
|
|
185
|
-
and "Lower Joint: Hind paw tao" in issues
|
|
186
|
-
and "Upper Joint: Knee" in issues
|
|
187
|
-
and "Cycle-time: 0.1-0.3s" in issues
|
|
188
|
-
)
|
|
189
|
-
# run again - test legname works as expected
|
|
190
|
-
os.remove(issues_path) # first remove previous textfile
|
|
191
|
-
write_angle_warning(
|
|
192
|
-
step, a, angles, broken_angle_idxs, extract_2D_info, legname="left"
|
|
193
|
-
)
|
|
194
|
-
with open(issues_path, "r") as f:
|
|
195
|
-
issues = f.read()
|
|
196
|
-
assert "Leg: left" in issues
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def test_correct_coordinate_standardisation(
|
|
200
|
-
extract_2D_info,
|
|
201
|
-
extract_2D_folderinfo,
|
|
202
|
-
extract_2D_cfg,
|
|
203
|
-
extract_3D_info,
|
|
204
|
-
extract_3D_folderinfo,
|
|
205
|
-
extract_3D_cfg,
|
|
206
|
-
):
|
|
207
|
-
for tracking_software in ["DLC", "Universal 3D"]:
|
|
208
|
-
# prep vars
|
|
209
|
-
if tracking_software == "DLC":
|
|
210
|
-
info = extract_2D_info
|
|
211
|
-
folderinfo = extract_2D_folderinfo
|
|
212
|
-
cfg = extract_2D_cfg
|
|
213
|
-
else:
|
|
214
|
-
info = extract_3D_info
|
|
215
|
-
folderinfo = extract_3D_folderinfo
|
|
216
|
-
cfg = extract_3D_cfg
|
|
217
|
-
# run respective some_prep functions to get dfs
|
|
218
|
-
if tracking_software == "DLC":
|
|
219
|
-
# unstandardised data
|
|
220
|
-
cfg["coordinate_standardisation_xls"] = ""
|
|
221
|
-
unstandardised_data = some_prep_2D(tracking_software, info, folderinfo, cfg)
|
|
222
|
-
# standardised data
|
|
223
|
-
cfg["coordinate_standardisation_xls"] = (
|
|
224
|
-
"tests/test_data/utils/Correct DLC CoordStand Table.xlsx"
|
|
225
|
-
)
|
|
226
|
-
standardised_data = some_prep_2D(tracking_software, info, folderinfo, cfg)
|
|
227
|
-
elif tracking_software == "Universal 3D":
|
|
228
|
-
# unstandardised data
|
|
229
|
-
cfg["coordinate_standardisation_xls"] = ""
|
|
230
|
-
unstandardised_data = some_prep_3D(info, folderinfo, cfg)[0] # tuple!
|
|
231
|
-
# standardised data
|
|
232
|
-
cfg["coordinate_standardisation_xls"] = (
|
|
233
|
-
"tests/test_data/utils/Correct Universal 3D CoordStand Table.xlsx"
|
|
234
|
-
)
|
|
235
|
-
standardised_data, global_Y_max = some_prep_3D(info, folderinfo, cfg)
|
|
236
|
-
# revert standardisation
|
|
237
|
-
reverted_data = standardised_data.copy()
|
|
238
|
-
standardisation_df = pd.read_excel(
|
|
239
|
-
cfg["coordinate_standardisation_xls"]
|
|
240
|
-
).astype(str)
|
|
241
|
-
if tracking_software == "DLC":
|
|
242
|
-
condition = (standardisation_df["ID"] == str(info["mouse_num"])) & (
|
|
243
|
-
standardisation_df["Run"] == str(info["run_num"])
|
|
244
|
-
)
|
|
245
|
-
elif tracking_software == "Universal 3D":
|
|
246
|
-
condition = standardisation_df["ID"] == info["name"]
|
|
247
|
-
standardisation_value = float(
|
|
248
|
-
standardisation_df.loc[condition, "Standardisation Value"]
|
|
249
|
-
)
|
|
250
|
-
if tracking_software == "DLC":
|
|
251
|
-
cols_to_revert = [
|
|
252
|
-
col
|
|
253
|
-
for col in reverted_data.columns
|
|
254
|
-
if (not col.endswith("likelihood"))
|
|
255
|
-
and any([joint in col for joint in cfg["hind_joints"]])
|
|
256
|
-
]
|
|
257
|
-
elif tracking_software == "Universal 3D":
|
|
258
|
-
cols_to_revert = [
|
|
259
|
-
col
|
|
260
|
-
for col in reverted_data.columns
|
|
261
|
-
if any([joint in col for joint in cfg["joints"]])
|
|
262
|
-
]
|
|
263
|
-
reverted_data[cols_to_revert] *= standardisation_value
|
|
264
|
-
|
|
265
|
-
# compare dataframes
|
|
266
|
-
pd.testing.assert_frame_equal(
|
|
267
|
-
reverted_data, unstandardised_data, check_exact=False, check_dtype=False
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
def test_angles_are_unaffected_by_coordinate_standardisation(
|
|
272
|
-
extract_2D_info, extract_2D_folderinfo, extract_2D_cfg
|
|
273
|
-
):
|
|
274
|
-
# prep: run dlc_main's first 3 steps to get dfs with angles
|
|
275
|
-
# 1) for unstandardised data
|
|
276
|
-
data = some_prep_2D("DLC", extract_2D_info, extract_2D_folderinfo, extract_2D_cfg)
|
|
277
|
-
all_cycles = extract_stepcycles(
|
|
278
|
-
"DLC", data, extract_2D_info, extract_2D_folderinfo, extract_2D_cfg
|
|
279
|
-
)
|
|
280
|
-
unstandardised_results = analyse_and_export_stepcycles(
|
|
281
|
-
data, all_cycles, extract_2D_info, extract_2D_cfg
|
|
282
|
-
)
|
|
283
|
-
# 2) for standardised data
|
|
284
|
-
extract_2D_cfg["coordinate_standardisation_xls"] = (
|
|
285
|
-
"autogaita/resources/Coordinate Standardisation Table Template.xlsx"
|
|
286
|
-
)
|
|
287
|
-
data = some_prep_2D("DLC", extract_2D_info, extract_2D_folderinfo, extract_2D_cfg)
|
|
288
|
-
all_cycles = extract_stepcycles(
|
|
289
|
-
"DLC", data, extract_2D_info, extract_2D_folderinfo, extract_2D_cfg
|
|
290
|
-
)
|
|
291
|
-
standardised_results = analyse_and_export_stepcycles(
|
|
292
|
-
data, all_cycles, extract_2D_info, extract_2D_cfg
|
|
293
|
-
)
|
|
294
|
-
# compare angles
|
|
295
|
-
cols_to_compare = [
|
|
296
|
-
col
|
|
297
|
-
for col in unstandardised_results["average_data"].columns
|
|
298
|
-
if col.endswith("Angle")
|
|
299
|
-
]
|
|
300
|
-
pdt.assert_frame_equal(
|
|
301
|
-
unstandardised_results["average_data"][cols_to_compare],
|
|
302
|
-
standardised_results["average_data"][cols_to_compare],
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
# Parameterized test for error cases
|
|
307
|
-
@pytest.mark.parametrize(
|
|
308
|
-
"xls_path, expected_error",
|
|
309
|
-
[
|
|
310
|
-
(
|
|
311
|
-
"autogaita/resources/This CoordStand Table is Missing.xlsx",
|
|
312
|
-
"No coordinate standardisation xls file found at:",
|
|
313
|
-
),
|
|
314
|
-
(
|
|
315
|
-
"tests/test_data/utils/This CoordStand Table has wrong columns.xlsx",
|
|
316
|
-
"does not have the correct column names",
|
|
317
|
-
),
|
|
318
|
-
(
|
|
319
|
-
"tests/test_data/utils/This CoordStand Table has wrong Run.xlsx",
|
|
320
|
-
"Unable to find",
|
|
321
|
-
),
|
|
322
|
-
(
|
|
323
|
-
"tests/test_data/utils/This CoordStand Table has multiple Names.xlsx",
|
|
324
|
-
"Found multiple entries for",
|
|
325
|
-
),
|
|
326
|
-
(
|
|
327
|
-
"tests/test_data/utils/This CoordStand Table doesn't have a float as standval.xlsx",
|
|
328
|
-
"Unable to convert standardisation value for",
|
|
329
|
-
),
|
|
330
|
-
(
|
|
331
|
-
"tests/test_data/utils/This CoordStand Table has a value smaller than 1.xlsx",
|
|
332
|
-
"smaller than 1!",
|
|
333
|
-
),
|
|
334
|
-
],
|
|
335
|
-
)
|
|
336
|
-
def test_standardisation_xls_error_cases(
|
|
337
|
-
extract_2D_info, extract_2D_folderinfo, extract_2D_cfg, xls_path, expected_error
|
|
338
|
-
):
|
|
339
|
-
# prep: remove existing Issues.txt
|
|
340
|
-
results_dir = extract_2D_info["results_dir"]
|
|
341
|
-
issues_path = os.path.join(results_dir, "Issues.txt")
|
|
342
|
-
if os.path.exists(issues_path):
|
|
343
|
-
os.remove(issues_path)
|
|
344
|
-
|
|
345
|
-
# set the xls path in the config & run some_prep which will run the standardisation
|
|
346
|
-
extract_2D_cfg["coordinate_standardisation_xls"] = xls_path
|
|
347
|
-
with pytest.raises(Exception):
|
|
348
|
-
data = some_prep_2D(
|
|
349
|
-
"DLC", extract_2D_info, extract_2D_folderinfo, extract_2D_cfg
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
# assert the error message - inform about what error failed if it did
|
|
353
|
-
with open(issues_path, "r") as f:
|
|
354
|
-
issues = f.read()
|
|
355
|
-
assert (
|
|
356
|
-
expected_error in issues
|
|
357
|
-
), f"Expected error '{expected_error}' not found in Issues.txt"
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
def test_angle_joints_not_in_joints(
|
|
361
|
-
extract_2D_info, extract_2D_folderinfo, extract_2D_cfg
|
|
362
|
-
):
|
|
363
|
-
# prep cfg stuff
|
|
364
|
-
cfg = extract_2D_cfg.copy()
|
|
365
|
-
cfg["coordinate_standardisation_xls"] = (
|
|
366
|
-
"tests/test_data/utils/Correct DLC CoordStand Table.xlsx"
|
|
367
|
-
)
|
|
368
|
-
cfg["hind_joints"] = ["Ankle", "Knee", "Hip"]
|
|
369
|
-
cfg["angles"] = { # hind paw is to be removed (not in cfg["joints"]!)
|
|
370
|
-
"name": ["Hind paw tao", "Knee", "Knee", "Knee"],
|
|
371
|
-
"lower_joint": ["Ankle", "Hind paw tao", "Ankle", "Ankle"],
|
|
372
|
-
"upper_joint": ["Hip", "Hip", "Hind paw tao", "Hip"],
|
|
373
|
-
}
|
|
374
|
-
# prep: remove existing Issues.txt
|
|
375
|
-
results_dir = extract_2D_info["results_dir"]
|
|
376
|
-
issues_path = os.path.join(results_dir, "Issues.txt")
|
|
377
|
-
if os.path.exists(issues_path):
|
|
378
|
-
os.remove(issues_path)
|
|
379
|
-
# run (not just some prep, because we want the cfg, too)
|
|
380
|
-
data = some_prep_2D("DLC", extract_2D_info, extract_2D_folderinfo, cfg)
|
|
381
|
-
data, cfg = standardise_primary_joint_coordinates(data, "DLC", extract_2D_info, cfg)
|
|
382
|
-
# assert error
|
|
383
|
-
with open(issues_path, "r") as f:
|
|
384
|
-
issues = f.read()
|
|
385
|
-
assert "We will update your cfg (!)" in issues
|
|
386
|
-
# assert updated cfg
|
|
387
|
-
assert cfg["angles"] == {
|
|
388
|
-
"name": ["Knee "],
|
|
389
|
-
"lower_joint": ["Ankle "],
|
|
390
|
-
"upper_joint": ["Hip "],
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
def test_define_bins_longer_trial():
|
|
395
|
-
# triallength > bin_num: should return a list of lists covering all indices
|
|
396
|
-
triallength = 100
|
|
397
|
-
bin_num = 25
|
|
398
|
-
bins = define_bins(triallength, bin_num)
|
|
399
|
-
assert isinstance(bins, list)
|
|
400
|
-
assert len(bins) == bin_num
|
|
401
|
-
# each element should be a list of indices
|
|
402
|
-
assert all(isinstance(b, list) for b in bins)
|
|
403
|
-
flattened = [i for sub in bins for i in sub]
|
|
404
|
-
assert set(flattened) == set(range(triallength))
|
|
405
|
-
assert min(flattened) == 0
|
|
406
|
-
assert max(flattened) == triallength - 1
|
|
407
|
-
# check indices are correct for moving averages
|
|
408
|
-
triallength = 8
|
|
409
|
-
bin_num = 3
|
|
410
|
-
bins = define_bins(triallength, bin_num)
|
|
411
|
-
assert bins == [[0, 1, 2], [3, 4, 5], [6, 7]]
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
def test_define_bins_shorter_trial():
|
|
415
|
-
# triallength < bin_num: should return an array (or list) of length bin_num
|
|
416
|
-
# with values in the range [0, triallength-1] and max == triallength-1
|
|
417
|
-
triallength = 10
|
|
418
|
-
bin_num = 25
|
|
419
|
-
bins = define_bins(triallength, bin_num)
|
|
420
|
-
# allow either numpy array or list-like
|
|
421
|
-
bins_list = list(bins)
|
|
422
|
-
assert len(bins_list) == bin_num
|
|
423
|
-
assert all(0 <= v <= (triallength - 1) for v in bins_list)
|
|
424
|
-
assert max(bins_list) == triallength - 1
|
|
425
|
-
assert min(bins_list) == 0
|
|
426
|
-
# check that values are spaced evenly across the trial
|
|
427
|
-
triallength = 5
|
|
428
|
-
bin_num = 12
|
|
429
|
-
bins = define_bins(triallength, bin_num)
|
|
430
|
-
assert bins == [0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4]
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
def test_define_bins_equal_length():
|
|
434
|
-
# triallength == bin_num: should return a list of ints equal to range(triallength)
|
|
435
|
-
triallength = 25
|
|
436
|
-
bin_num = 25
|
|
437
|
-
bins = define_bins(triallength, bin_num)
|
|
438
|
-
assert isinstance(bins, list)
|
|
439
|
-
assert len(bins) == bin_num
|
|
440
|
-
# elements should be ints 0..triallength-1
|
|
441
|
-
assert all(isinstance(b, (int, np.integer)) for b in bins)
|
|
442
|
-
assert bins == list(range(triallength))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|