autogaita 1.5.1__py3-none-any.whl → 1.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.
- autogaita-1.5.2.dist-info/METADATA +40 -0
- {autogaita-1.5.1.dist-info → autogaita-1.5.2.dist-info}/RECORD +20 -13
- autogaita-1.5.2.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_common2D_unit_1_preparation.py +340 -0
- tests/test_common2D_unit_2_sc_extraction.py +367 -0
- tests/test_common2D_unit_3_analysis.py +245 -0
- tests/test_common2D_unit_utils.py +137 -0
- tests/test_dlc_approval.py +110 -0
- tests/test_dlc_unit_1_preparation.py +114 -0
- tests/test_group_approval.py +162 -0
- tests/test_group_unit.py +393 -0
- tests/test_universal3D_approval.py +89 -0
- tests/test_universal3D_unit_1_preparation.py +69 -0
- tests/test_universal3D_unit_2_sc_extraction.py +108 -0
- tests/test_universal3D_unit_3_analysis.py +248 -0
- tests/test_utils.py +442 -0
- autogaita-1.5.1.dist-info/METADATA +0 -173
- autogaita-1.5.1.dist-info/top_level.txt +0 -2
- autogaita_backup/common2D_run_and_done_gui.py +0 -414
- autogaita_backup/dlc_1_preparation.py +0 -426
- autogaita_backup/dlc_2_sc_extraction.py +0 -217
- autogaita_backup/dlc_gui.py +0 -359
- autogaita_backup/sleap_1_preparation.py +0 -303
- autogaita_backup/sleap_2_sc_extraction.py +0 -167
- autogaita_backup/sleap_gui.py +0 -359
- {autogaita-1.5.1.dist-info → autogaita-1.5.2.dist-info}/WHEEL +0 -0
- {autogaita-1.5.1.dist-info → autogaita-1.5.2.dist-info}/entry_points.txt +0 -0
- {autogaita-1.5.1.dist-info → autogaita-1.5.2.dist-info}/licenses/LICENSE +0 -0
tests/test_group_unit.py
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
from autogaita.group.group_main import (
|
|
2
|
+
group,
|
|
3
|
+
import_data,
|
|
4
|
+
avg_and_std,
|
|
5
|
+
PCA_main,
|
|
6
|
+
create_stats_df,
|
|
7
|
+
cluster_extent_test,
|
|
8
|
+
anova_design_sanity_check,
|
|
9
|
+
tukeys_only_info_message,
|
|
10
|
+
ANOVA_main,
|
|
11
|
+
plot_results,
|
|
12
|
+
print_finish,
|
|
13
|
+
)
|
|
14
|
+
from autogaita.group.group_1_preparation import some_prep
|
|
15
|
+
from autogaita.group.group_2_data_processing import (
|
|
16
|
+
load_previous_runs_dataframes,
|
|
17
|
+
check_PCA_and_stats_variables,
|
|
18
|
+
)
|
|
19
|
+
from autogaita.group.group_3_PCA import run_PCA, convert_PCA_bins_to_list
|
|
20
|
+
from autogaita.group.group_4_stats import run_ANOVA, multcompare_SC_Percentages
|
|
21
|
+
from autogaita.resources.utils import bin_num_to_percentages
|
|
22
|
+
import os
|
|
23
|
+
import math
|
|
24
|
+
import pytest
|
|
25
|
+
from sklearn import datasets
|
|
26
|
+
import pandas as pd
|
|
27
|
+
import pandas.testing as pdt
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# %%................................ fixtures ........................................
|
|
32
|
+
@pytest.fixture
|
|
33
|
+
def extract_folderinfo(tmp_path):
|
|
34
|
+
return {
|
|
35
|
+
"group_names": ["group1", "group2"],
|
|
36
|
+
"group_dirs": ["/path/to/group1", "/path/to/group2"],
|
|
37
|
+
"results_dir": tmp_path,
|
|
38
|
+
"load_dir": "",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def extract_cfg():
|
|
44
|
+
return {
|
|
45
|
+
"do_permtest": True,
|
|
46
|
+
"do_anova": True,
|
|
47
|
+
"permutation_number": 100,
|
|
48
|
+
"PCA_n_components": 3,
|
|
49
|
+
"PCA_custom_scatter_PCs": "",
|
|
50
|
+
"PCA_save_3D_video": False, # True
|
|
51
|
+
"PCA_bins": "",
|
|
52
|
+
"stats_threshold": 0.05,
|
|
53
|
+
"plot_SE": False,
|
|
54
|
+
"color_palette": "viridis",
|
|
55
|
+
"dont_show_plots": True,
|
|
56
|
+
"legend_outside": True,
|
|
57
|
+
"which_leg": "left",
|
|
58
|
+
"anova_design": "Mixed ANOVA",
|
|
59
|
+
"PCA_variables": [],
|
|
60
|
+
"stats_variables": [],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# %%.............................. 1. preparation ....................................
|
|
65
|
+
def test_some_preps_cfg_files(extract_folderinfo, extract_cfg):
|
|
66
|
+
"""Check for equivalence of the cfg file using load dir and the cfg dict generated by some_prep"""
|
|
67
|
+
# 1. Ensure that the cfg is equal to that used in group approval (with repo data)
|
|
68
|
+
extract_folderinfo["group_names"] = ["5 mm", "12 mm", "25 mm"]
|
|
69
|
+
extract_folderinfo["group_dirs"] = [
|
|
70
|
+
"example data/5mm/Results/",
|
|
71
|
+
"example data/12mm/Results/",
|
|
72
|
+
"example data/25mm/Results/",
|
|
73
|
+
]
|
|
74
|
+
extract_cfg["do_permtest"] = False
|
|
75
|
+
extract_cfg["anova_design"] = "RM ANOVA"
|
|
76
|
+
extract_cfg["PCA_variables"] = [
|
|
77
|
+
"Knee y",
|
|
78
|
+
"Ankle y",
|
|
79
|
+
"Hind paw tao y",
|
|
80
|
+
"Ankle Angle",
|
|
81
|
+
"Knee Angle",
|
|
82
|
+
"Hip Angle",
|
|
83
|
+
]
|
|
84
|
+
extract_cfg["stats_variables"] = extract_cfg["PCA_variables"]
|
|
85
|
+
# 2. Run some_prep twice with and without load dir & test cfg match
|
|
86
|
+
extract_folderinfo, generated_cfg = some_prep(extract_folderinfo, extract_cfg)
|
|
87
|
+
extract_folderinfo["load_dir"] = "example data/group"
|
|
88
|
+
extract_folderinfo, loaded_cfg = some_prep(extract_folderinfo, extract_cfg)
|
|
89
|
+
# 3. Check that cfgs are result of different processes (being loaded or not) and
|
|
90
|
+
# not exact copies (e.g. references to the same object) for some Python-reason
|
|
91
|
+
assert "loaded" in loaded_cfg.keys()
|
|
92
|
+
assert "loaded" not in generated_cfg.keys()
|
|
93
|
+
# 4. Check that the cfgs are equivalent
|
|
94
|
+
loaded_cfg.pop("loaded") # except this key of course - get rid of it
|
|
95
|
+
generated_cfg == loaded_cfg
|
|
96
|
+
# 5. Finally change some cfg key and assert that it has not been overwritten by
|
|
97
|
+
# loading a previous run's cfg
|
|
98
|
+
# => might seem unnecessary but the way this was implemented initially did
|
|
99
|
+
# overwrite cfgs due to how it was loading stuff
|
|
100
|
+
extract_cfg["do_permtest"] = True
|
|
101
|
+
extract_cfg["anova_design"] = "Mixed ANOVA"
|
|
102
|
+
extract_cfg["PCA_variables"] = ["Ankle Angle", "Knee Angle", "Hip Angle"]
|
|
103
|
+
extract_folderinfo, loaded_cfg_2 = some_prep(extract_folderinfo, extract_cfg)
|
|
104
|
+
differing_keys = ["do_permtest", "anova_design", "PCA_variables"]
|
|
105
|
+
for key in differing_keys:
|
|
106
|
+
assert loaded_cfg_2[key] != loaded_cfg[key]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# %%............................ 2. data processing ..................................
|
|
110
|
+
def test_check_PCA_and_stats_variables(extract_folderinfo, extract_cfg):
|
|
111
|
+
"""Test this sanity check does not have the necessary columns"""
|
|
112
|
+
group_names = ["5 mm", "12 mm", "25 mm"]
|
|
113
|
+
extract_folderinfo["group_names"] = group_names
|
|
114
|
+
extract_folderinfo["load_dir"] = "example data/group"
|
|
115
|
+
# changing PCA/stats vars like this already performs a first test because the load
|
|
116
|
+
# function only runs successfully, if the test_PCA_.. function does
|
|
117
|
+
# => i.e. the features are present in the all 3 kinds of dfs
|
|
118
|
+
extract_cfg["PCA_variables"] = ["Ankle Angle", "Knee Angle"]
|
|
119
|
+
extract_cfg["stats_variables"] = ["Ankle Angle", "Knee Angle"]
|
|
120
|
+
avg_dfs, _, _, extract_cfg = load_repos_group_data(extract_folderinfo, extract_cfg)
|
|
121
|
+
# test correct failure
|
|
122
|
+
# => code means: test that after removing the column our check correctly raises a
|
|
123
|
+
# ValueError
|
|
124
|
+
avg_dfs[0].drop(columns=["Ankle Angle"], inplace=True)
|
|
125
|
+
with pytest.raises(ValueError):
|
|
126
|
+
check_PCA_and_stats_variables(
|
|
127
|
+
avg_dfs[0], "5 mm", "Average", extract_folderinfo, extract_cfg
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_smoke_load_dir(extract_folderinfo, extract_cfg):
|
|
132
|
+
"""Test that load dir runs through without errors and produces Results"""
|
|
133
|
+
extract_folderinfo["group_names"] = ["5 mm", "12 mm", "25 mm"]
|
|
134
|
+
extract_folderinfo["load_dir"] = "example data/group"
|
|
135
|
+
group(extract_folderinfo, extract_cfg)
|
|
136
|
+
# I checked and hardcoded the 39 here, shouldn't change.
|
|
137
|
+
# If it does I want to know and this should fail
|
|
138
|
+
assert len(os.listdir(extract_folderinfo["results_dir"])) == 39
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_load_previous_runs_dataframes(extract_folderinfo, extract_cfg):
|
|
142
|
+
"""Testing if errors are raised correctly and if the loaded dfs are eqiuvalent to the ones import_data generates"""
|
|
143
|
+
# NOTE
|
|
144
|
+
# ----
|
|
145
|
+
# => last tests fail on git actions but not locally
|
|
146
|
+
# => quite sure it's because of how git actions' os lists the files when loading
|
|
147
|
+
# => we have a similar issue in test_group_approval see os.getenv("CI") there
|
|
148
|
+
# 1: fails as wanted if group name wrong (df not found in load dir)
|
|
149
|
+
extract_folderinfo["group_names"] = ["not 5 mm", "12 mm", "25 mm"]
|
|
150
|
+
extract_folderinfo["load_dir"] = "example data/group"
|
|
151
|
+
with pytest.raises(FileNotFoundError):
|
|
152
|
+
avg_dfs, _, _, extract_cfg = load_repos_group_data(
|
|
153
|
+
extract_folderinfo, extract_cfg
|
|
154
|
+
)
|
|
155
|
+
# 2: avg_dfs equivalent to import_data's avg_dfs
|
|
156
|
+
extract_folderinfo["group_names"] = ["5 mm", "12 mm", "25 mm"]
|
|
157
|
+
extract_folderinfo["group_dirs"] = [ # for "import_data" later
|
|
158
|
+
"example data/5mm/Results/",
|
|
159
|
+
"example data/12mm/Results/",
|
|
160
|
+
"example data/25mm/Results/",
|
|
161
|
+
]
|
|
162
|
+
avg_dfs, _, _, extract_cfg = load_repos_group_data(extract_folderinfo, extract_cfg)
|
|
163
|
+
# some prep required for import data & avg_and_std
|
|
164
|
+
extract_cfg["sampling_rate"] = 100
|
|
165
|
+
extract_cfg["bin_num"] = 25
|
|
166
|
+
extract_cfg["save_to_xls"] = [True, True, True]
|
|
167
|
+
extract_cfg["tracking_software"] = "DLC"
|
|
168
|
+
extract_cfg["analyse_average_x"] = True
|
|
169
|
+
i_dfs, _, extract_cfg = import_data(extract_folderinfo, extract_cfg)
|
|
170
|
+
i_avg_dfs, _ = avg_and_std(i_dfs, extract_folderinfo, extract_cfg)
|
|
171
|
+
if not os.getenv("CI"):
|
|
172
|
+
for g in range(len(extract_folderinfo["group_names"])):
|
|
173
|
+
pdt.assert_frame_equal(
|
|
174
|
+
avg_dfs[g], i_avg_dfs[g], check_dtype=False, check_exact=False
|
|
175
|
+
)
|
|
176
|
+
# 3: check that it also works for Universal 3D
|
|
177
|
+
# => only do it locally, I don't want to upload the MoVi Data to git
|
|
178
|
+
if not os.getenv("CI"):
|
|
179
|
+
extract_folderinfo["group_names"] = ["crawling", "jogging"]
|
|
180
|
+
extract_folderinfo["group_dirs"] = [
|
|
181
|
+
"/Users/mahan/sciebo/Research/AutoGaitA/Showcase 3/MOVI/Final/crawling/Results/",
|
|
182
|
+
"/Users/mahan/sciebo/Research/AutoGaitA/Showcase 3/MOVI/Final/jogging/Results/",
|
|
183
|
+
]
|
|
184
|
+
extract_folderinfo["load_dir"] = (
|
|
185
|
+
"/Users/mahan/sciebo/Research/AutoGaitA/Showcase 3/MOVI/Unit Test of load_prev_groupruns_dfs/"
|
|
186
|
+
)
|
|
187
|
+
avg_dfs, _, _, extract_cfg = load_repos_group_data(
|
|
188
|
+
extract_folderinfo, extract_cfg
|
|
189
|
+
)
|
|
190
|
+
extract_cfg["tracking_software"] = "Universal 3D"
|
|
191
|
+
extract_cfg["which_leg"] = "right"
|
|
192
|
+
extract_cfg["analyse_average_y"] = True
|
|
193
|
+
i_dfs, _, extract_cfg = import_data(extract_folderinfo, extract_cfg)
|
|
194
|
+
i_avg_dfs, _ = avg_and_std(i_dfs, extract_folderinfo, extract_cfg)
|
|
195
|
+
if not os.getenv("CI"):
|
|
196
|
+
for g in range(len(extract_folderinfo["group_names"])):
|
|
197
|
+
pdt.assert_frame_equal(
|
|
198
|
+
avg_dfs[g], i_avg_dfs[g], check_dtype=False, check_exact=False
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# %%.............................. 4. statistics .....................................
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_load_dfs_stats_df_perm_test_smoke(extract_folderinfo, extract_cfg):
|
|
206
|
+
"""Smoke test three functions:
|
|
207
|
+
1. load_previous_runs-dataframes
|
|
208
|
+
2. create_stats_df
|
|
209
|
+
3. cluster_extent_test
|
|
210
|
+
=> Check if it all runs without errors on our repo's example data
|
|
211
|
+
"""
|
|
212
|
+
# first, prepare approrpiately & overwrite the fixtures of this script as needed
|
|
213
|
+
# 1) for load_previous_runs_dataframes (called by load repos group data)
|
|
214
|
+
extract_folderinfo["group_names"] = ["5 mm", "12 mm", "25 mm"]
|
|
215
|
+
extract_folderinfo["load_dir"] = "example data/group"
|
|
216
|
+
avg_dfs, g_avg_dfs, g_std_dfs, extract_cfg = load_repos_group_data(
|
|
217
|
+
extract_folderinfo, extract_cfg
|
|
218
|
+
)
|
|
219
|
+
# 2) for stats_df & the permutation test functions
|
|
220
|
+
extract_cfg["sampling_rate"] = 100
|
|
221
|
+
extract_cfg["do_permtest"] = True
|
|
222
|
+
extract_cfg["dont_show_plots"] = True
|
|
223
|
+
extract_cfg["tracking_software"] = "DLC"
|
|
224
|
+
extract_cfg["group_color_dict"] = {"5 mm": "red", "12 mm": "blue", "25 mm": "green"}
|
|
225
|
+
extract_folderinfo["contrasts"] = ["5 mm & 12 mm", "5 mm & 25 mm", "12 mm & 25 mm"]
|
|
226
|
+
stats_df = create_stats_df(avg_dfs, extract_folderinfo, extract_cfg)
|
|
227
|
+
plot_panel_instance = None
|
|
228
|
+
cluster_extent_test(
|
|
229
|
+
stats_df,
|
|
230
|
+
g_avg_dfs,
|
|
231
|
+
g_std_dfs,
|
|
232
|
+
"Ankle Angle",
|
|
233
|
+
extract_folderinfo,
|
|
234
|
+
extract_cfg,
|
|
235
|
+
plot_panel_instance,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_Mixed_ANOVA(extract_cfg):
|
|
240
|
+
# Adopted example data from https://real-statistics.com/
|
|
241
|
+
# See the Mixed ANOVA xlsx file for complete link
|
|
242
|
+
extract_cfg["do_anova"] = True
|
|
243
|
+
extract_cfg["anova_design"] = "Mixed ANOVA"
|
|
244
|
+
stats_df = pd.read_excel("tests/test_data/group_data/Mixed ANOVA Example Data.xlsx")
|
|
245
|
+
stats_var = "Value"
|
|
246
|
+
result = run_ANOVA(stats_df, stats_var, extract_cfg)
|
|
247
|
+
# no GG p vals here
|
|
248
|
+
assert math.isclose(result["p-unc"][0], stats_df["p(A)"][0], abs_tol=1e-05)
|
|
249
|
+
assert math.isclose(result["p-unc"][1], stats_df["p(B)"][0], abs_tol=1e-05)
|
|
250
|
+
assert math.isclose(result["p-unc"][2], stats_df["p(AxB)"][0], abs_tol=1e-05)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_multcomp_df_with_scipy_example(extract_folderinfo, extract_cfg):
|
|
254
|
+
# Adopted example from https://docs.scipy.org/doc/scipy-1.15.0/reference/generated/
|
|
255
|
+
# scipy.stats.tukey_hsd.html
|
|
256
|
+
extract_folderinfo["group_names"] = ["group0", "group1", "group2"]
|
|
257
|
+
extract_folderinfo["contrasts"] = [
|
|
258
|
+
"group0 & group1",
|
|
259
|
+
"group0 & group2",
|
|
260
|
+
"group1 & group2",
|
|
261
|
+
]
|
|
262
|
+
# fmt: off
|
|
263
|
+
scipy_example = [24.5, 23.5, 26.4, 27.1, 29.9, 28.4, 34.2, 29.5, 32.2, 30.1, 26.1, 28.3, 24.3, 26.2, 27.8] # fmt: on
|
|
264
|
+
# prep data
|
|
265
|
+
data = []
|
|
266
|
+
bin_num = 25
|
|
267
|
+
for i in scipy_example:
|
|
268
|
+
data.append([i] * bin_num)
|
|
269
|
+
data = np.ravel(data)
|
|
270
|
+
# prep group_col
|
|
271
|
+
subject_num = 5
|
|
272
|
+
group_col = []
|
|
273
|
+
for group in extract_folderinfo["group_names"]:
|
|
274
|
+
for _ in range(subject_num):
|
|
275
|
+
group_col.append([group] * bin_num)
|
|
276
|
+
group_col = np.ravel(group_col)
|
|
277
|
+
# prep ID_col
|
|
278
|
+
total_subject_num = 15
|
|
279
|
+
IDs = np.arange(1, total_subject_num + 1)
|
|
280
|
+
ID_col = np.repeat(IDs, bin_num)
|
|
281
|
+
# prep SC percentage col
|
|
282
|
+
extract_cfg["bin_num"] = 25
|
|
283
|
+
SC_percentages = bin_num_to_percentages(extract_cfg["bin_num"])
|
|
284
|
+
SC_percentage_col = [SC_percentages] * total_subject_num
|
|
285
|
+
SC_percentage_col = np.ravel(SC_percentage_col)
|
|
286
|
+
# pytest.set_trace()
|
|
287
|
+
# manually create dummy stats df
|
|
288
|
+
stats_df = pd.DataFrame(data=data, columns=["Variable"])
|
|
289
|
+
stats_df["Group"] = group_col
|
|
290
|
+
stats_df["ID"] = ID_col
|
|
291
|
+
stats_df["SC Percentage"] = SC_percentage_col
|
|
292
|
+
# run the func and perform hard-coded check with vals of doc's example
|
|
293
|
+
# => multcomp_df's rows are all equal because our SC percentages were all equal here
|
|
294
|
+
# => correct results correspond to doc-example's rows 1, 4 and 2 first listing all
|
|
295
|
+
# stats-values, then p then CI low & CI high since that corresponds to the order
|
|
296
|
+
# of multcomp df's columns
|
|
297
|
+
# => i.e., col1= SC %, then 3 cols for each contrast's q, then p, then CI low, then
|
|
298
|
+
# CI high in the order of groups0 & 1, 0 & 2, and 1 & 2
|
|
299
|
+
multcomp_df = multcompare_SC_Percentages(
|
|
300
|
+
stats_df, "Variable", extract_folderinfo, extract_cfg
|
|
301
|
+
)
|
|
302
|
+
# fmt: off
|
|
303
|
+
correct_results = [-4.6, -0.260, 4.34, 0.014, 0.980, 0.02, -8.249, -3.909, 0.691, -0.951, 3.389, 7.989]
|
|
304
|
+
for r, result in enumerate(correct_results):
|
|
305
|
+
assert math.isclose(result, multcomp_df.iloc[0, r+1], abs_tol=0.001)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# %%.................................. PCA ...........................................
|
|
309
|
+
def test_run_PCA(extract_folderinfo, extract_cfg):
|
|
310
|
+
# Replicate the example found in https://www.kdnuggets.com/2023/05/
|
|
311
|
+
# principal-component-analysis-pca-scikitlearn.html using our PCA df and PCA_info
|
|
312
|
+
# structure
|
|
313
|
+
# fmt: off
|
|
314
|
+
true_results_PCA_eigenvectors = [[0.1443294, -0.24518758, -0.00205106, -0.23932041, 0.14199204, 0.39466085, 0.4229343, -0.2985331, 0.31342949, -0.0886167,0.29671456, 0.37616741, 0.28675223],
|
|
315
|
+
[-0.48365155, -0.22493093, -0.31606881, 0.0105905, -0.299634, -0.06503951
|
|
316
|
+
, 0.00335981, -0.02877949, -0.03930172, -0.52999567, 0.27923515, 0.16449619,
|
|
317
|
+
-0.36490283],
|
|
318
|
+
[-0.20738262, 0.08901289, 0.6262239, 0.61208035, 0.13075693, 0.14617896,
|
|
319
|
+
0.1506819, 0.17036816, 0.14945431, -0.13730621, 0.08522192, 0.16600459,
|
|
320
|
+
-0.12674592]]
|
|
321
|
+
true_results_PCA_explained_var = [0.36198848, 0.1920749 , 0.11123631]
|
|
322
|
+
# fmt: on
|
|
323
|
+
wine_data = datasets.load_wine(as_frame=True)
|
|
324
|
+
wine_df = wine_data.data
|
|
325
|
+
features = wine_df.columns
|
|
326
|
+
PCA_df, PCA_info = run_PCA(wine_df, features, extract_folderinfo, extract_cfg)
|
|
327
|
+
for i in range(3):
|
|
328
|
+
# absolute values are compared because the signs can be different w. eigenvecs
|
|
329
|
+
assert np.allclose(
|
|
330
|
+
np.absolute(PCA_info["eigenvectors"][i]),
|
|
331
|
+
np.absolute(true_results_PCA_eigenvectors[i]),
|
|
332
|
+
atol=1e-05,
|
|
333
|
+
)
|
|
334
|
+
assert np.allclose(
|
|
335
|
+
PCA_info["explained_vars"], true_results_PCA_explained_var, atol=1e-05
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
cases = (
|
|
340
|
+
(
|
|
341
|
+
"1-30,50,70-100",
|
|
342
|
+
100,
|
|
343
|
+
np.arange(1, 31).tolist() + [50] + np.arange(70, 101).tolist(),
|
|
344
|
+
),
|
|
345
|
+
("1-70", 25, [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68]),
|
|
346
|
+
("2, 10, 22, 44, 50, 79", 50, [2, 10, 22, 44, 50]),
|
|
347
|
+
("1-50,70-80", 10, [10, 20, 30, 40, 50, 70, 80]),
|
|
348
|
+
) # fmt: skip
|
|
349
|
+
@pytest.mark.parametrize("PCA_bins, bin_num, expected_bins_list", cases)
|
|
350
|
+
def test_convert_PCA_bins_to_list(
|
|
351
|
+
PCA_bins, bin_num, expected_bins_list, extract_folderinfo, extract_cfg
|
|
352
|
+
):
|
|
353
|
+
extract_cfg["PCA_bins"] = PCA_bins
|
|
354
|
+
extract_cfg["bin_num"] = bin_num
|
|
355
|
+
updated_cfg = convert_PCA_bins_to_list(extract_folderinfo, extract_cfg)
|
|
356
|
+
assert np.array_equal(updated_cfg["PCA_bins"], np.array(expected_bins_list))
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def load_repos_group_data(extract_folderinfo, extract_cfg):
|
|
360
|
+
"""Use load_previous_runs_dataframes to load example data from the repo"""
|
|
361
|
+
extract_cfg["sampling_rate"] = 100
|
|
362
|
+
extract_cfg["group_color_dict"] = {"5 mm": "red", "12 mm": "blue", "25 mm": "green"}
|
|
363
|
+
extract_folderinfo["contrasts"] = ["5 mm & 12 mm", "5 mm & 25 mm", "12 mm & 25 mm"]
|
|
364
|
+
avg_dfs, g_avg_dfs, g_std_dfs, extract_cfg = load_previous_runs_dataframes(
|
|
365
|
+
extract_folderinfo, extract_cfg
|
|
366
|
+
)
|
|
367
|
+
return avg_dfs, g_avg_dfs, g_std_dfs, extract_cfg
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# NOTE !!!
|
|
371
|
+
# => This test is outdated and wrong because it is for two within subject factors (for
|
|
372
|
+
# example 3 separate behavioural tests and pre/post medication treatment or so -
|
|
373
|
+
# done by each subject)
|
|
374
|
+
# => I will not delete this since I might want to re-use it if I should support such
|
|
375
|
+
# designs
|
|
376
|
+
#
|
|
377
|
+
# def test_twoway_RM_ANOVA(extract_cfg):
|
|
378
|
+
# # Adopted example data from https://real-statistics.com/
|
|
379
|
+
# # See the RM ANOVA xlsx file for complete link
|
|
380
|
+
# extract_cfg["do_anova"] = True
|
|
381
|
+
# extract_cfg["anova_design"] = "RM ANOVA"
|
|
382
|
+
# stats_df = pd.read_excel("tests/test_data/group_data/RM ANOVA Example Data.xlsx")
|
|
383
|
+
# stats_var = "Value"
|
|
384
|
+
# result = run_ANOVA(stats_df, stats_var, extract_cfg)
|
|
385
|
+
# # Note that the last 2 assert statements have a different tolerance because those
|
|
386
|
+
# # p-values differed a bit. Not sure why but it's a tiny amount and tolerable IMO
|
|
387
|
+
# pytest.set_trace()
|
|
388
|
+
# assert math.isclose(result["p-unc"][0], stats_df["p(A)"][0], abs_tol=1e-05)
|
|
389
|
+
# assert math.isclose(result["p-unc"][1], stats_df["p(B)"][0], abs_tol=1e-05)
|
|
390
|
+
# assert math.isclose(result["p-unc"][2], stats_df["p(AxB)"][0], abs_tol=1e-05)
|
|
391
|
+
# assert math.isclose(result["p-GG-corr"][0], stats_df["GG-p(A)"][0], abs_tol=1e-05)
|
|
392
|
+
# assert math.isclose(result["p-GG-corr"][1], stats_df["GG-p(B)"][0], abs_tol=1e-04)
|
|
393
|
+
# assert math.isclose(result["p-GG-corr"][2], stats_df["GG-p(AxB)"][0], abs_tol=1e-02)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from autogaita.resources.utils import try_to_run_gaita
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import pandas.testing as pdt
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import pdb
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# ............................ SIMI APPROVAL TESTS STRUCTURE .........................
|
|
11
|
+
# 1. Run autogaita.simi for Subject (with the cfg used there)
|
|
12
|
+
# 2. Load the "Average Stepcycles".xlsx file from the repo and compare for
|
|
13
|
+
# equivalence to average_data
|
|
14
|
+
# 3. Do the same for "Standard Devs. Stepcycle.xlsx" and std_data
|
|
15
|
+
# 4. Pass the test if the two df-pairs are equal
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ............................... PREPARE - FOUR FIXTURES ...........................
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def extract_true_dir():
|
|
23
|
+
return "tests/test_data/universal3D_data/true_data/"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def extract_info(tmp_path):
|
|
28
|
+
info = {}
|
|
29
|
+
info["name"] = "TestSubject"
|
|
30
|
+
info["results_dir"] = os.path.join(tmp_path, info["name"])
|
|
31
|
+
return info
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def extract_folderinfo():
|
|
36
|
+
folderinfo = {}
|
|
37
|
+
folderinfo["root_dir"] = "tests/test_data/universal3D_data/test_data/"
|
|
38
|
+
folderinfo["sctable_filename"] = "SC Latency Table"
|
|
39
|
+
folderinfo["postname_string"] = ""
|
|
40
|
+
return folderinfo
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def extract_cfg():
|
|
45
|
+
cfg = {}
|
|
46
|
+
cfg["sampling_rate"] = 100
|
|
47
|
+
cfg["dont_show_plots"] = True
|
|
48
|
+
cfg["y_acceleration"] = True
|
|
49
|
+
cfg["angular_acceleration"] = True
|
|
50
|
+
cfg["bin_num"] = 25
|
|
51
|
+
cfg["plot_SE"] = True
|
|
52
|
+
cfg["standardise_z_at_SC_level"] = True
|
|
53
|
+
cfg["standardise_z_to_a_joint"] = False
|
|
54
|
+
cfg["z_standardisation_joint"] = ["Midfoot, left"]
|
|
55
|
+
cfg["plot_joint_number"] = 7
|
|
56
|
+
cfg["legend_outside"] = True
|
|
57
|
+
cfg["flip_gait_direction"] = True
|
|
58
|
+
cfg["color_palette"] = "viridis"
|
|
59
|
+
cfg["analyse_average_y"] = False
|
|
60
|
+
cfg["standardise_y_coordinates"] = True
|
|
61
|
+
cfg["y_standardisation_joint"] = ["Midfoot, left"]
|
|
62
|
+
cfg["coordinate_standardisation_xls"] = ""
|
|
63
|
+
cfg["joints"] = ["Midfoot", "Ankle", "Knee", "Hip", "Pelvis "]
|
|
64
|
+
cfg["angles"] = {
|
|
65
|
+
"name": ["Ankle", "Knee", "Hip"],
|
|
66
|
+
"lower_joint": ["Midfoot", "Ankle", "Knee"],
|
|
67
|
+
"upper_joint": ["Knee", "Hip", "Pelvis "],
|
|
68
|
+
}
|
|
69
|
+
return cfg
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ............................... RUN - ONE APPROVAL TEST ............................
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@pytest.mark.slow # https://docs.pytest.org/en/7.1.x/example/markers.html
|
|
76
|
+
def test_universal3D_approval(
|
|
77
|
+
extract_true_dir, extract_info, extract_folderinfo, extract_cfg
|
|
78
|
+
):
|
|
79
|
+
# run
|
|
80
|
+
try_to_run_gaita(
|
|
81
|
+
"Universal 3D", extract_info, extract_folderinfo, extract_cfg, False
|
|
82
|
+
)
|
|
83
|
+
for true_df_file in os.listdir(extract_true_dir):
|
|
84
|
+
if true_df_file.endswith(".xlsx"):
|
|
85
|
+
true_df = pd.read_excel(os.path.join(extract_true_dir, true_df_file))
|
|
86
|
+
test_df = pd.read_excel(
|
|
87
|
+
os.path.join(extract_info["results_dir"], true_df_file)
|
|
88
|
+
)
|
|
89
|
+
pdt.assert_frame_equal(test_df, true_df)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from autogaita.universal3D.universal3D_datafile_preparation import (
|
|
2
|
+
clean_a_file,
|
|
3
|
+
rename_a_column,
|
|
4
|
+
)
|
|
5
|
+
from hypothesis import given, strategies as st
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# %%.............................. preparation .......................................
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# property test for removing a string from column names via clean a file
|
|
13
|
+
column_names = st.lists(st.text(min_size=1), min_size=1, max_size=10, unique=True)
|
|
14
|
+
strings_to_remove = st.text(min_size=1)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@given(df_columns=column_names, string_to_remove=strings_to_remove)
|
|
18
|
+
def test_clean_a_file(df_columns, string_to_remove):
|
|
19
|
+
df = pd.DataFrame(columns=df_columns)
|
|
20
|
+
cleaned_df = clean_a_file(df, string_to_remove)
|
|
21
|
+
for col in df_columns:
|
|
22
|
+
if string_to_remove in col:
|
|
23
|
+
assert col.replace(string_to_remove, "") in cleaned_df.columns
|
|
24
|
+
else:
|
|
25
|
+
assert col in cleaned_df.columns
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Property test for renaming a given column according to 3 possible cases
|
|
29
|
+
candidate_side_ids = st.sampled_from(
|
|
30
|
+
["l", "L", "left", "LEFT", "Left", "r", "R", "right", "RIGHT", "Right"]
|
|
31
|
+
)
|
|
32
|
+
candidate_coord_ids = st.sampled_from(["x", "y", "z", "X", "Y", "Z"])
|
|
33
|
+
candidate_joint_ids = st.text(min_size=2)
|
|
34
|
+
separators = st.sampled_from(["_", "-", ":", "."])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@given(
|
|
38
|
+
candidate_side_id=candidate_side_ids,
|
|
39
|
+
candidate_coord_id=candidate_coord_ids,
|
|
40
|
+
candidate_joint_id=candidate_joint_ids,
|
|
41
|
+
separator=separators,
|
|
42
|
+
)
|
|
43
|
+
def test_rename_a_column(
|
|
44
|
+
candidate_side_id, candidate_coord_id, candidate_joint_id, separator
|
|
45
|
+
):
|
|
46
|
+
# only run main test below if separator is not the joint id anywhere
|
|
47
|
+
# => we require separators to be unique in column strings
|
|
48
|
+
if separator in candidate_joint_id:
|
|
49
|
+
return
|
|
50
|
+
# preparation - what output we are expecting
|
|
51
|
+
expected_side = "left" if candidate_side_id.lower() in ["l", "left"] else "right"
|
|
52
|
+
expected_coord = candidate_coord_id.capitalize()
|
|
53
|
+
# test 1 - column is side-specific
|
|
54
|
+
col_string = separator.join(
|
|
55
|
+
[candidate_side_id, candidate_coord_id, candidate_joint_id]
|
|
56
|
+
)
|
|
57
|
+
result = rename_a_column(col_string, separator)
|
|
58
|
+
assert isinstance(result, str)
|
|
59
|
+
assert (
|
|
60
|
+
result == f"{candidate_joint_id}, {expected_side} {expected_coord.capitalize()}"
|
|
61
|
+
)
|
|
62
|
+
# test 2 - column is central
|
|
63
|
+
col_string = separator.join([candidate_coord_id, candidate_joint_id])
|
|
64
|
+
result = rename_a_column(col_string, separator)
|
|
65
|
+
assert result == f"{candidate_joint_id} {expected_coord.capitalize()}"
|
|
66
|
+
# test 3 - column is not a coordinate column
|
|
67
|
+
col_string = separator.join([candidate_joint_id])
|
|
68
|
+
result = rename_a_column(col_string, separator)
|
|
69
|
+
assert result == col_string
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from autogaita.universal3D.universal3D_1_preparation import some_prep
|
|
2
|
+
from autogaita.universal3D.universal3D_2_sc_extraction import (
|
|
3
|
+
check_different_angle_joint_coords,
|
|
4
|
+
)
|
|
5
|
+
import os
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# %%................................ fixtures ........................................
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def extract_info(tmp_path):
|
|
13
|
+
info = {}
|
|
14
|
+
info["name"] = "TestSubject"
|
|
15
|
+
info["results_dir"] = os.path.join(tmp_path, info["name"])
|
|
16
|
+
return info
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def extract_folderinfo():
|
|
21
|
+
folderinfo = {}
|
|
22
|
+
folderinfo["root_dir"] = "tests/test_data/universal3D_data/test_data"
|
|
23
|
+
folderinfo["sctable_filename"] = "SC Latency Table.xlsx"
|
|
24
|
+
folderinfo["postname_string"] = ""
|
|
25
|
+
return folderinfo
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture
|
|
29
|
+
def extract_cfg():
|
|
30
|
+
# note space in end of "Midfoot, left " must be here bc. we don't run our fix and
|
|
31
|
+
# check cfg function of 1_prep_
|
|
32
|
+
cfg = {}
|
|
33
|
+
cfg["sampling_rate"] = 100 # base cfg
|
|
34
|
+
cfg["dont_show_plots"] = True
|
|
35
|
+
cfg["y_acceleration"] = True
|
|
36
|
+
cfg["angular_acceleration"] = True
|
|
37
|
+
cfg["bin_num"] = 25
|
|
38
|
+
cfg["plot_SE"] = True
|
|
39
|
+
cfg["standardise_z_at_SC_level"] = True
|
|
40
|
+
cfg["standardise_z_to_a_joint"] = True
|
|
41
|
+
cfg["z_standardisation_joint"] = ["Midfoot, left "]
|
|
42
|
+
cfg["plot_joint_number"] = 5
|
|
43
|
+
cfg["color_palette"] = "Set2"
|
|
44
|
+
cfg["legend_outside"] = True
|
|
45
|
+
cfg["flip_gait_direction"] = True
|
|
46
|
+
cfg["analyse_average_y"] = True
|
|
47
|
+
cfg["standardise_y_coordinates"] = True
|
|
48
|
+
cfg["y_standardisation_joint"] = ["Midfoot, left "]
|
|
49
|
+
cfg["coordinate_standardisation_xls"] = ""
|
|
50
|
+
cfg["joints"] = [
|
|
51
|
+
"Midfoot",
|
|
52
|
+
"Ankle",
|
|
53
|
+
"Knee",
|
|
54
|
+
"Hip",
|
|
55
|
+
]
|
|
56
|
+
cfg["angles"] = {
|
|
57
|
+
"name": ["Ankle", "Knee"],
|
|
58
|
+
"lower_joint": ["Midfoot", "Ankle"],
|
|
59
|
+
"upper_joint": ["Knee", "Hip"],
|
|
60
|
+
}
|
|
61
|
+
cfg["direction_joint"] = "Midfoot, left Y"
|
|
62
|
+
return cfg
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# .................................. tests .........................................
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_clean_cycles_different_angle_joint_coords(
|
|
69
|
+
extract_info, extract_folderinfo, extract_cfg
|
|
70
|
+
):
|
|
71
|
+
# start conditions
|
|
72
|
+
data, _ = some_prep(extract_info, extract_folderinfo, extract_cfg)
|
|
73
|
+
all_cycles = [[[111, 222], [333, 444]], [[555, 666], [777, 888]]]
|
|
74
|
+
# test one: leads to both lists still being non-empty, one SC removed
|
|
75
|
+
data.loc[123, "Ankle, left Y"] = 5.5
|
|
76
|
+
data.loc[123, "Ankle, left Z"] = 2.1
|
|
77
|
+
data.loc[123, "Midfoot, left Y"] = 5.5
|
|
78
|
+
data.loc[123, "Midfoot, left Z"] = 2.1
|
|
79
|
+
all_cycles = check_different_angle_joint_coords(
|
|
80
|
+
all_cycles, data, extract_info, extract_cfg
|
|
81
|
+
)
|
|
82
|
+
assert all_cycles == [[[333, 444]], [[555, 666], [777, 888]]]
|
|
83
|
+
with open(os.path.join(extract_info["results_dir"], "Issues.txt")) as f:
|
|
84
|
+
content = f.read()
|
|
85
|
+
assert "Run #1 - SC #1" in content
|
|
86
|
+
assert "equal LEFT" in content
|
|
87
|
+
assert "Lower joint: Midfoot" in content
|
|
88
|
+
# test two: leads to one empty list
|
|
89
|
+
data.loc[345, "Hip, right Y"] = 10.1
|
|
90
|
+
data.loc[345, "Hip, right Z"] = 0.333
|
|
91
|
+
data.loc[345, "Ankle, right Y"] = 10.1
|
|
92
|
+
data.loc[345, "Ankle, right Z"] = 0.333
|
|
93
|
+
all_cycles = check_different_angle_joint_coords(
|
|
94
|
+
all_cycles, data, extract_info, extract_cfg
|
|
95
|
+
)
|
|
96
|
+
assert all_cycles == [[], [[555, 666], [777, 888]]]
|
|
97
|
+
with open(os.path.join(extract_info["results_dir"], "Issues.txt")) as f:
|
|
98
|
+
content = f.read()
|
|
99
|
+
assert "RIGHT" in content
|
|
100
|
+
# test three: leads to all_cycles being None
|
|
101
|
+
data.loc[[567, 789], "Hip, right Y"] = 22.2
|
|
102
|
+
data.loc[[567, 789], "Hip, right Z"] = 0.11
|
|
103
|
+
data.loc[[567, 789], "Knee, right Y"] = 22.2
|
|
104
|
+
data.loc[[567, 789], "Knee, right Z"] = 0.11
|
|
105
|
+
assert (
|
|
106
|
+
check_different_angle_joint_coords(all_cycles, data, extract_info, extract_cfg)
|
|
107
|
+
is None
|
|
108
|
+
)
|