autogaita 0.1.0__tar.gz → 0.2.0__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.
Files changed (40) hide show
  1. autogaita-0.2.0/PKG-INFO +10 -0
  2. {autogaita-0.1.0 → autogaita-0.2.0}/README.md +18 -3
  3. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_dlc.py +196 -90
  4. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_dlc_gui.py +70 -12
  5. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_group.py +116 -19
  6. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_group_gui.py +67 -4
  7. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_simi.py +120 -57
  8. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_simi_gui.py +69 -12
  9. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/autogaita_dlc_multirun.py +7 -5
  10. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/autogaita_dlc_singlerun.py +9 -7
  11. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/autogaita_group_dlcrun.py +2 -0
  12. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/autogaita_group_simirun.py +2 -0
  13. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/autogaita_simi_multirun.py +2 -0
  14. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/autogaita_simi_singlerun.py +2 -0
  15. autogaita-0.2.0/autogaita/dlc_gui_config.json +82 -0
  16. autogaita-0.2.0/autogaita/group_gui_config.json +36 -0
  17. autogaita-0.2.0/autogaita/simi_gui_config.json +48 -0
  18. autogaita-0.2.0/autogaita.egg-info/PKG-INFO +10 -0
  19. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita.egg-info/SOURCES.txt +4 -6
  20. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita.egg-info/requires.txt +1 -0
  21. {autogaita-0.1.0 → autogaita-0.2.0}/setup.py +3 -2
  22. autogaita-0.1.0/PKG-INFO +0 -23
  23. autogaita-0.1.0/autogaita.egg-info/PKG-INFO +0 -23
  24. autogaita-0.1.0/tests/test_dlc_approval.py +0 -137
  25. autogaita-0.1.0/tests/test_dlc_unit2_sc_extraction.py +0 -250
  26. autogaita-0.1.0/tests/test_dlc_unit3_main_analysis.py +0 -219
  27. autogaita-0.1.0/tests/test_group_approval.py +0 -122
  28. autogaita-0.1.0/tests/test_simi_approval.py +0 -99
  29. {autogaita-0.1.0 → autogaita-0.2.0}/LICENSE +0 -0
  30. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/__init__.py +0 -0
  31. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/__main__.py +0 -0
  32. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita.py +0 -0
  33. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_icon.icns +0 -0
  34. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_icon.ico +0 -0
  35. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_logo.png +0 -0
  36. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/autogaita_utils.py +0 -0
  37. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita/batchrun_scripts/__init__.py +0 -0
  38. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita.egg-info/dependency_links.txt +0 -0
  39. {autogaita-0.1.0 → autogaita-0.2.0}/autogaita.egg-info/top_level.txt +0 -0
  40. {autogaita-0.1.0 → autogaita-0.2.0}/setup.cfg +0 -0
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.1
2
+ Name: autogaita
3
+ Version: 0.2.0
4
+ Summary: Automatic Gait Analysis in Python
5
+ Home-page: https://github.com/mahan-hosseini/AutoGaitA/
6
+ Author: Mahan Hosseini
7
+ License: GPLv3
8
+ Requires-Python: >=3.10
9
+ Provides-Extra: dev
10
+ License-File: LICENSE
@@ -16,7 +16,7 @@
16
16
 
17
17
  ## Getting Started
18
18
 
19
- ***Note!** Our documentation provides step-by-step walkthroughs of how to install autogaita for **[Windows](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.28j6wu2vamre)** and **[Mac](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit)***
19
+ ***Note!** [Our documentation](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit?usp=sharing) provides step-by-step walkthroughs of how to install autogaita for **[Windows](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.28j6wu2vamre)** and **[Mac](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit)***
20
20
 
21
21
  It is strongly recommended that a separate virtual environment for AutoGaitA is created (note that the approach below creates the virtual environment to your current directory):
22
22
 
@@ -31,6 +31,8 @@ It is strongly recommended that a separate virtual environment for AutoGaitA is
31
31
 
32
32
  - Access the main user interface via `python -m autogaita`.
33
33
 
34
+ - To update to the latest release (see the *Releases* panel on the right for the current version) activate your virtual environment & enter `pip install autogaita -U`.
35
+
34
36
  ## Tutorials and Examples
35
37
 
36
38
  ### Video Walkthrough Tutorials
@@ -45,7 +47,7 @@ We provide an example dataset in the **example data** folder of this repository,
45
47
  ### Annotation Table Examples and Templates
46
48
  Annotation Table example and template files for *AutoGaitA_DLC* and *AutoGaitA_Simi* can be found in the [**annotation tables**](https://github.com/mahan-hosseini/AutoGaitA/tree/main/annotation%20tables) folder of this repository.
47
49
 
48
- Users are advised to read the ***important note*** of that folder, use the template to enter their data's timestamp information and to then compare the resulting table with our example to check formatting.
50
+ Users are advised to read the ***important note*** of that folder, use the template to enter their data's timestamp information and to then compare the resulting table with our example to check formatting. Users working with ImageJ/FIJI are encouraged to check out the [AnnotationTable-Plugin](https://github.com/luca-flemming/AnnotationTable-Plugin) developed by our contributor Luca Flemming.
49
51
 
50
52
  ## Documentation
51
53
 
@@ -62,6 +64,9 @@ By default, *AutoGaitA DLC* and *AutoGaitA Simi* implement standard values for m
62
64
  - [Documentation - AutoGaitA DLC](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.20bg7b7ymt0b)
63
65
  - [Documentation - AutoGaitA Simi](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.uz61bpmua7qz)
64
66
 
67
+ ## Analysing other behaviours - AutoCyclA 🚴
68
+ Even though AutoGaitA's main focus is to automate and standardise gait analyses, our toolbox can be used to automate the analyses of any rhythmic behaviour of interest. For a proof-of-principle demonstration and an introduction of the general workflow of such analyses, see **[AutoCyclA - Automated Cycling Analysis with AutoGaitA.](https://github.com/mahan-hosseini/AutoGaitA/tree/main/autocycla)**
69
+
65
70
  ## Reference
66
71
  If you use this code or data please [cite our preprint](https://www.biorxiv.org/content/10.1101/2024.04.14.589409v1).
67
72
 
@@ -71,4 +76,14 @@ AutoGaitA is licensed under [GPL v3.0](https://github.com/mahan-hosseini/AutoGai
71
76
  The AutoGaitA software is provided without warranty of any kind, express or implied, including, but not limited to, the implied warranty of fitness for a particular purpose.
72
77
 
73
78
  ## Authors
74
- Mahan Hosseini
79
+ [Mahan Hosseini](https://github.com/mahan-hosseini)
80
+
81
+ ## Contributors
82
+ [Luca Flemming](https://github.com/luca-flemming) - Undergraduate Student
83
+
84
+ [Nicholas del Grosso](https://github.com/nickdelgrosso) - RSE Advisor
85
+
86
+ ## Contributing
87
+ If you would like to contribute to the AutoGaitA toolbox, feel free to open a pull request or contact us at autogaita@fz-juelich.de!
88
+
89
+ We are looking forward to your input and ideas 😊
@@ -9,6 +9,7 @@ import numpy as np
9
9
  import math
10
10
  import matplotlib
11
11
  import matplotlib.pyplot as plt
12
+ import seaborn as sns
12
13
 
13
14
  # %% constants
14
15
  plt.rcParams["figure.dpi"] = 300 # increase resolution of figures
@@ -38,6 +39,7 @@ AVERAGE_XLS_FILENAME = " - Average Stepcycle"
38
39
  STD_XLS_FILENAME = " - Standard Devs. Stepcycle"
39
40
  SC_LAT_LEGEND_FONTSIZE = 7
40
41
 
42
+
41
43
  # %% main program
42
44
 
43
45
 
@@ -92,6 +94,7 @@ def some_prep(info, folderinfo, cfg):
92
94
  normalise_height_at_SC_level = cfg["normalise_height_at_SC_level"]
93
95
  invert_y_axis = cfg["invert_y_axis"]
94
96
  flip_gait_direction = cfg["flip_gait_direction"]
97
+ export_average_x = cfg["export_average_x"]
95
98
 
96
99
  # ............................. move data ........................................
97
100
  # => see if we can delete a previous runs results folder if existant. if not, it's a
@@ -127,8 +130,8 @@ def some_prep(info, folderinfo, cfg):
127
130
  "\n******************\n! CRITICAL ERROR !\n******************\n"
128
131
  + "Unable to identify ANY RELEVANT FILES for "
129
132
  + name
130
- + "!\nThis is likely due to issues with pre/post-strings.. check "
131
- + "capitalisation!"
133
+ + "!\nThis is likely due to issues with unique file name identifiers.. "
134
+ + "check capitalisation!"
132
135
  )
133
136
  write_issues_to_textfile(no_files_error, info)
134
137
  print(no_files_error)
@@ -138,6 +141,15 @@ def some_prep(info, folderinfo, cfg):
138
141
  datadf = pd.DataFrame(data=None) # prep stuff for error handling
139
142
  datadf_duplicate_error = ""
140
143
  if subtract_beam:
144
+ if data_string == beam_string:
145
+ beam_and_data_string_error_message = (
146
+ "\n******************\n! CRITICAL ERROR !\n******************\n"
147
+ + "Your data & baseline (beam) identifiers ([G] in our "
148
+ + "file naming convention) are identical. "
149
+ + "\nNote that they must be different! \nTry again"
150
+ )
151
+ write_issues_to_textfile(beam_and_data_string_error_message, info)
152
+ return
141
153
  beamdf = pd.DataFrame(data=None)
142
154
  beamdf_duplicate_error = ""
143
155
  for filename in os.listdir(results_dir): # import
@@ -216,7 +228,7 @@ def some_prep(info, folderinfo, cfg):
216
228
  # IMPORTANT
217
229
  # ---------
218
230
  # MAIN TESTS OF USER-INPUT VALIDITY OCCUR HERE!
219
- cfg = test_and_expand_cfg(data, cfg, info)
231
+ cfg = check_and_expand_cfg(data, cfg, info)
220
232
  if cfg is None: # hind joints were empty
221
233
  return
222
234
  hind_joints = cfg["hind_joints"]
@@ -225,7 +237,6 @@ def some_prep(info, folderinfo, cfg):
225
237
  beam_hind_jointadd = cfg["beam_hind_jointadd"]
226
238
  beam_fore_jointadd = cfg["beam_fore_jointadd"]
227
239
  direction_joint = cfg["direction_joint"]
228
- export_average_x = cfg["export_average_x"]
229
240
  # store config json file @ group path
230
241
  # !!! NU - do this @ mouse path!
231
242
  group_path = results_dir.split(name)[0]
@@ -243,6 +254,29 @@ def some_prep(info, folderinfo, cfg):
243
254
  # note - using "w" will overwrite/truncate file, thus no need to remove it if exists
244
255
  with open(config_json_path, "w") as config_json_file:
245
256
  json.dump(config_vars_to_json, config_json_file, indent=4)
257
+ # a little test to see if columns make sense, i.e., same number of x/y/likelihood
258
+ x_col_count = len([c for c in data.columns if c.endswith(" x")])
259
+ y_col_count = len([c for c in data.columns if c.endswith(" y")])
260
+ likelihood_col_count = len([c for c in data.columns if c.endswith(" likelihood")])
261
+ if x_col_count == y_col_count == likelihood_col_count:
262
+ pass
263
+ else:
264
+ cols_are_weird_message = (
265
+ "\n***********\n! WARNING !\n***********\n"
266
+ + "We detected an unequal number of columns ending with x, y or "
267
+ + "likelihood!\nCounts were:\n"
268
+ + "x: "
269
+ + str(x_col_count)
270
+ + ", y: "
271
+ + str(y_col_count)
272
+ + ", likelihood: "
273
+ + str(likelihood_col_count)
274
+ + "!\n\n"
275
+ + "We continue with the analysis but we strongly suggest you have another "
276
+ + "look at your dataset, this should not happen.\n"
277
+ )
278
+ print(cols_are_weird_message)
279
+ write_issues_to_textfile(cols_are_weird_message, info)
246
280
  # if wanted: fix that deeplabcut inverses y
247
281
  if invert_y_axis:
248
282
  for col in data.columns:
@@ -346,7 +380,7 @@ def move_data_to_folders(info, folderinfo):
346
380
  write_issues_to_textfile(this_message, info)
347
381
 
348
382
 
349
- def test_and_expand_cfg(data, cfg, info):
383
+ def check_and_expand_cfg(data, cfg, info):
350
384
  """Test some important cfg variables and add new ones based on them
351
385
 
352
386
  Procedure
@@ -402,7 +436,7 @@ def test_and_expand_cfg(data, cfg, info):
402
436
  return
403
437
  cfg["direction_joint"] = hind_joints[0]
404
438
 
405
- # if subtracting beam, check that its colnames were valid.
439
+ # if subtracting beam, check identifier-strings & that beam colnames were valid.
406
440
  if cfg["subtract_beam"]:
407
441
  beam_col_error_message = (
408
442
  "\n******************\n! CRITICAL ERROR !\n******************\n"
@@ -585,12 +619,10 @@ def flip_mouse_body(data, info):
585
619
  write_issues_to_textfile(message, info)
586
620
 
587
621
  # 1) Flip all rows in x columns only and subtract max from all vals
588
- flipped_data = pd.DataFrame(data=None, columns=data.columns)
589
- for col in data.columns:
590
- if col.endswith("x"):
591
- flipped_data.loc[:, col] = max(data.loc[:, col]) - data.loc[:, col]
592
- else:
593
- flipped_data.loc[:, col] = data.loc[:, col]
622
+ flipped_data = data.copy()
623
+ x_cols = [col for col in flipped_data.columns if col.endswith(" x")]
624
+ for col in x_cols:
625
+ flipped_data[col] = max(flipped_data[col]) - flipped_data[col]
594
626
  return flipped_data
595
627
 
596
628
 
@@ -1330,6 +1362,24 @@ def add_step_separators(dataframe, nanvector, numvector):
1330
1362
 
1331
1363
 
1332
1364
  # .............................. master function .............................
1365
+
1366
+ # A note on updated colour cyclers after pull request that was merged 20.06.2024
1367
+ # => Using color palettes instead of colour maps as I had previously means that
1368
+ # we cycle through neighbouring colours
1369
+ # => I initially implemented an "equally distant" approach.
1370
+ # => So for viridis and n=2 (e.g. if 2 groups) it would be purple and yellow
1371
+ # (as far away as possible)
1372
+ # => Now it is dark blue and green
1373
+ # => Updated approach is aesthetically more pleasing IMO.
1374
+ # => However it does have the risk of not being able to tell the colours in
1375
+ # some cases - e.g. if some accelerations are very overlapping.
1376
+ # => But - because users can in theses cases just choose a colour palette that
1377
+ # in itself has categorical colours (Set1, Dark2, etc.) I still keep the new
1378
+ # behaviour
1379
+ # => Nonetheless, in case you want to use the "old behaviour" at some point it
1380
+ # would be coded as commented out in plot_joint_y_by_x
1381
+
1382
+
1333
1383
  def plot_results(info, results, folderinfo, cfg):
1334
1384
  """Plot results - y coords by x coords & average angles over SC %"""
1335
1385
  # unpack
@@ -1439,6 +1489,8 @@ def plot_joint_y_by_x(all_steps_data, sc_idxs, info, cfg):
1439
1489
  convert_to_mm = cfg["convert_to_mm"]
1440
1490
  plot_joints = cfg["plot_joints"]
1441
1491
  sampling_rate = cfg["sampling_rate"]
1492
+ legend_outside = cfg["legend_outside"]
1493
+ color_palette = cfg["color_palette"]
1442
1494
 
1443
1495
  # some prep
1444
1496
  sc_num = len(sc_idxs)
@@ -1448,49 +1500,37 @@ def plot_joint_y_by_x(all_steps_data, sc_idxs, info, cfg):
1448
1500
  # plot
1449
1501
  for j, joint in enumerate(plot_joints): # joint loop (figures)
1450
1502
  f[j], ax[j] = plt.subplots(1, 1)
1451
- ax[j].set_prop_cycle(
1452
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, sc_num)))
1503
+ # What "Old" colormap approach would look like with seaborn
1504
+ # this_map = sns.color_palette(cfg["color_palette"], as_cmap=True)
1505
+ # ax[j].set_prop_cycle(plt.cycler("color", this_map(np.linspace(0, 1, sc_num))))
1506
+ ax[j].set_prop_cycle( # New color palette approach
1507
+ plt.cycler("color", sns.color_palette(color_palette, sc_num))
1453
1508
  )
1454
- if joint == "Hind paw tao ":
1455
- ax[j].set_title(name + " - Foot")
1456
- else:
1457
- ax[j].set_title(name + " - " + joint)
1509
+ ax[j].set_title(name + " - " + joint)
1458
1510
  x_col_idx = all_steps_data.columns.get_loc(joint + "x")
1459
1511
  y_col_idx = all_steps_data.columns.get_loc(joint + "y")
1460
1512
  time_col_idx = all_steps_data.columns.get_loc(TIME_COL)
1461
1513
  for s in range(sc_num):
1462
1514
  this_x = all_steps_data.iloc[sc_idxs[s], x_col_idx]
1463
1515
  this_y = all_steps_data.iloc[sc_idxs[s], y_col_idx]
1464
- if sampling_rate <= 100:
1465
- float_precision = 2 # how many decimals we round to
1466
- else:
1467
- float_precision = 4
1468
- this_label = (
1469
- str(
1470
- round(
1471
- all_steps_data.iloc[sc_idxs[s][0], time_col_idx],
1472
- float_precision,
1473
- )
1474
- )
1475
- + "-"
1476
- + str(
1477
- round(
1478
- all_steps_data.iloc[sc_idxs[s][-1], time_col_idx],
1479
- float_precision,
1480
- )
1481
- )
1482
- + "s"
1516
+ this_label = generate_sc_latency_label(
1517
+ all_steps_data, sc_idxs[s], sampling_rate, time_col_idx
1483
1518
  )
1484
1519
  ax[j].plot(this_x, this_y, label=this_label)
1485
1520
  ax[j].set_xlabel("x (pixel)") # will be overwritten if we convert
1486
1521
  ax[j].set_ylabel("y (pixel)")
1487
- ax[j].legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1522
+ # legend adjustments
1523
+ if legend_outside is True:
1524
+ ax[j].legend(
1525
+ fontsize=SC_LAT_LEGEND_FONTSIZE,
1526
+ loc="center left",
1527
+ bbox_to_anchor=(1, 0.5),
1528
+ )
1529
+ elif legend_outside is False:
1530
+ ax[j].legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1488
1531
  if convert_to_mm:
1489
1532
  tickconvert_mm_to_cm(ax[j], "both")
1490
- if joint == "Hind paw tao ":
1491
- figure_file_string = " - Foot y by x coordinates"
1492
- else:
1493
- figure_file_string = " - " + joint + "y by x coordinates"
1533
+ figure_file_string = " - " + joint + "y by x coordinates"
1494
1534
  save_figures(f[j], results_dir, name, figure_file_string)
1495
1535
  if dont_show_plots:
1496
1536
  plt.close(f[j])
@@ -1504,6 +1544,9 @@ def plot_angles_by_time(all_steps_data, sc_idxs, info, cfg):
1504
1544
  results_dir = info["results_dir"]
1505
1545
  dont_show_plots = cfg["dont_show_plots"]
1506
1546
  angles = cfg["angles"]
1547
+ sampling_rate = cfg["sampling_rate"]
1548
+ legend_outside = cfg["legend_outside"]
1549
+ color_palette = cfg["color_palette"]
1507
1550
 
1508
1551
  # some prep
1509
1552
  sc_num = len(sc_idxs)
@@ -1514,17 +1557,30 @@ def plot_angles_by_time(all_steps_data, sc_idxs, info, cfg):
1514
1557
  for a, angle in enumerate(angles["name"]): # angle loop (figures)
1515
1558
  f[a], ax[a] = plt.subplots(1, 1)
1516
1559
  ax[a].set_prop_cycle(
1517
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, sc_num)))
1560
+ plt.cycler("color", sns.color_palette(color_palette, sc_num))
1518
1561
  )
1519
1562
  ax[a].set_title(name + " - " + angle)
1520
1563
  ax[a].set_ylabel("Angle")
1521
1564
  ax[a].set_xlabel("Time (s)")
1522
1565
  x_col_idx = all_steps_data.columns.get_loc(TIME_COL)
1523
1566
  y_col_idx = all_steps_data.columns.get_loc(angle + "Angle")
1567
+ time_col_idx = all_steps_data.columns.get_loc(TIME_COL)
1524
1568
  for s in range(sc_num):
1525
1569
  this_x = all_steps_data.iloc[sc_idxs[s], x_col_idx]
1526
1570
  this_y = all_steps_data.iloc[sc_idxs[s], y_col_idx]
1527
- ax[a].plot(this_x, this_y)
1571
+ this_label = generate_sc_latency_label(
1572
+ all_steps_data, sc_idxs[s], sampling_rate, time_col_idx
1573
+ )
1574
+ ax[a].plot(this_x, this_y, label=this_label)
1575
+ # legend adjustments
1576
+ if legend_outside is True:
1577
+ ax[a].legend(
1578
+ fontsize=SC_LAT_LEGEND_FONTSIZE,
1579
+ loc="center left",
1580
+ bbox_to_anchor=(1, 0.5),
1581
+ )
1582
+ elif legend_outside is False:
1583
+ ax[a].legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1528
1584
  figure_file_string = " - " + angle + "Angle by Time"
1529
1585
  save_figures(f[a], results_dir, name, figure_file_string)
1530
1586
  if dont_show_plots:
@@ -1541,11 +1597,13 @@ def plot_hindlimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
1541
1597
  convert_to_mm = cfg["convert_to_mm"]
1542
1598
  plot_joints = cfg["plot_joints"]
1543
1599
  sampling_rate = cfg["sampling_rate"]
1600
+ legend_outside = cfg["legend_outside"]
1601
+ color_palette = cfg["color_palette"]
1544
1602
 
1545
1603
  # some prep
1546
1604
  sc_num = len(sc_idxs)
1547
1605
  f, ax = plt.subplots(1, 1)
1548
- color_cycle = plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, sc_num)))
1606
+ color_cycle = plt.cycler("color", sns.color_palette(color_palette, sc_num))
1549
1607
  ax.set_prop_cycle(color_cycle)
1550
1608
  time_col_idx = all_steps_data.columns.get_loc(TIME_COL)
1551
1609
 
@@ -1553,21 +1611,8 @@ def plot_hindlimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
1553
1611
  # => for timepoints from SC1 to SCend - plot(joint1x, joint1y)
1554
1612
  for s, this_color_dict in zip(range(sc_num), color_cycle): # SC loop (colors)
1555
1613
  this_color = this_color_dict["color"][:3]
1556
- if sampling_rate <= 100:
1557
- float_precision = 2 # how many decimals we round to
1558
- else:
1559
- float_precision = 4
1560
- this_label = (
1561
- str(
1562
- round(all_steps_data.iloc[sc_idxs[s][0], time_col_idx], float_precision)
1563
- )
1564
- + "-"
1565
- + str(
1566
- round(
1567
- all_steps_data.iloc[sc_idxs[s][-1], time_col_idx], float_precision
1568
- )
1569
- )
1570
- + "s"
1614
+ this_label = generate_sc_latency_label(
1615
+ all_steps_data, sc_idxs[s], sampling_rate, time_col_idx
1571
1616
  )
1572
1617
  for i in sc_idxs[s]: # loop over timepoints of current SC
1573
1618
  this_xs = list() # for each timepoint, define joints' xy coord new
@@ -1586,7 +1631,13 @@ def plot_hindlimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
1586
1631
  ax.set_ylabel("y (pixel)")
1587
1632
  if convert_to_mm:
1588
1633
  tickconvert_mm_to_cm(ax, "both")
1589
- ax.legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1634
+ # legend adjustments
1635
+ if legend_outside is True:
1636
+ ax.legend(
1637
+ fontsize=SC_LAT_LEGEND_FONTSIZE, loc="center left", bbox_to_anchor=(1, 0.5)
1638
+ )
1639
+ elif legend_outside is False:
1640
+ ax.legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1590
1641
  figure_file_string = " - Hindlimb Stick Diagram"
1591
1642
  save_figures(f, results_dir, name, figure_file_string)
1592
1643
  if dont_show_plots:
@@ -1603,32 +1654,21 @@ def plot_forelimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
1603
1654
  convert_to_mm = cfg["convert_to_mm"]
1604
1655
  fore_joints = cfg["fore_joints"]
1605
1656
  sampling_rate = cfg["sampling_rate"]
1657
+ legend_outside = cfg["legend_outside"]
1658
+ color_palette = cfg["color_palette"]
1606
1659
 
1607
1660
  # some prep
1608
1661
  sc_num = len(sc_idxs)
1609
1662
  f, ax = plt.subplots(1, 1)
1610
- color_cycle = plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, sc_num)))
1663
+ color_cycle = plt.cycler("color", sns.color_palette(color_palette, sc_num))
1611
1664
  ax.set_prop_cycle(color_cycle)
1612
1665
  time_col_idx = all_steps_data.columns.get_loc(TIME_COL)
1613
1666
 
1614
1667
  # plot
1615
1668
  for s, this_color in zip(range(sc_num), color_cycle): # SC loop (colors)
1616
1669
  this_color = this_color["color"][:3]
1617
- if sampling_rate <= 100:
1618
- float_precision = 2 # how many decimals we round to
1619
- else:
1620
- float_precision = 4
1621
- this_label = (
1622
- str(
1623
- round(all_steps_data.iloc[sc_idxs[s][0], time_col_idx], float_precision)
1624
- )
1625
- + "-"
1626
- + str(
1627
- round(
1628
- all_steps_data.iloc[sc_idxs[s][-1], time_col_idx], float_precision
1629
- )
1630
- )
1631
- + "s"
1670
+ this_label = generate_sc_latency_label(
1671
+ all_steps_data, sc_idxs[s], sampling_rate, time_col_idx
1632
1672
  )
1633
1673
  for i in sc_idxs[s]:
1634
1674
  this_xs = list()
@@ -1647,7 +1687,13 @@ def plot_forelimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
1647
1687
  ax.set_ylabel("y (pixel)")
1648
1688
  if convert_to_mm:
1649
1689
  tickconvert_mm_to_cm(ax, "both")
1650
- ax.legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1690
+ # legend adjustments
1691
+ if legend_outside is True:
1692
+ ax.legend(
1693
+ fontsize=SC_LAT_LEGEND_FONTSIZE, loc="center left", bbox_to_anchor=(1, 0.5)
1694
+ )
1695
+ elif legend_outside is False:
1696
+ ax.legend(fontsize=SC_LAT_LEGEND_FONTSIZE)
1651
1697
  figure_file_string = " - Forelimb Stick Diagram"
1652
1698
  save_figures(f, results_dir, name, figure_file_string)
1653
1699
  if dont_show_plots:
@@ -1666,11 +1712,13 @@ def plot_joint_y_by_average_SC(average_data, std_data, info, cfg):
1666
1712
  plot_SE = cfg["plot_SE"]
1667
1713
  sc_num = cfg["sc_num"]
1668
1714
  hind_joints = cfg["hind_joints"]
1715
+ legend_outside = cfg["legend_outside"]
1716
+ color_palette = cfg["color_palette"]
1669
1717
 
1670
1718
  # plot
1671
1719
  f, ax = plt.subplots(1, 1)
1672
1720
  ax.set_prop_cycle(
1673
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, len(hind_joints))))
1721
+ plt.cycler("color", sns.color_palette(color_palette, len(hind_joints)))
1674
1722
  )
1675
1723
  x = np.linspace(0, 100, bin_num)
1676
1724
  for joint in hind_joints: # joint loop (lines)
@@ -1682,7 +1730,11 @@ def plot_joint_y_by_average_SC(average_data, std_data, info, cfg):
1682
1730
  this_std = std_data.iloc[:, y_col_idx]
1683
1731
  ax.plot(x, this_y, label=joint)
1684
1732
  ax.fill_between(x, this_y - this_std, this_y + this_std, alpha=0.2)
1685
- ax.legend()
1733
+ # legend adjustments
1734
+ if legend_outside is True:
1735
+ ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
1736
+ elif legend_outside is False:
1737
+ ax.legend()
1686
1738
  ax.set_title(name + " - Joint Y over average step cycle")
1687
1739
  ax.set_xlabel("Percentage")
1688
1740
  ax.set_ylabel("y (pixel)")
@@ -1705,11 +1757,13 @@ def plot_angles_by_average_SC(average_data, std_data, info, cfg):
1705
1757
  plot_SE = cfg["plot_SE"]
1706
1758
  sc_num = cfg["sc_num"]
1707
1759
  angles = cfg["angles"]
1760
+ legend_outside = cfg["legend_outside"]
1761
+ color_palette = cfg["color_palette"]
1708
1762
 
1709
1763
  # plot
1710
1764
  f, ax = plt.subplots(1, 1)
1711
1765
  ax.set_prop_cycle(
1712
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, len(angles["name"]))))
1766
+ plt.cycler("color", sns.color_palette(color_palette, len(angles["name"])))
1713
1767
  )
1714
1768
  x = np.linspace(0, 100, bin_num)
1715
1769
  ax.set_title(name + " - Joint angles over average step cycle")
@@ -1724,7 +1778,11 @@ def plot_angles_by_average_SC(average_data, std_data, info, cfg):
1724
1778
  this_std = std_data.iloc[:, y_col_idx]
1725
1779
  ax.plot(x, this_y, label=angle)
1726
1780
  ax.fill_between(x, this_y - this_std, this_y + this_std, alpha=0.2)
1727
- ax.legend()
1781
+ # legend adjustments
1782
+ if legend_outside is True:
1783
+ ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
1784
+ elif legend_outside is False:
1785
+ ax.legend()
1728
1786
  figure_file_string = " - Joint angles over average step cycle"
1729
1787
  save_figures(f, results_dir, name, figure_file_string)
1730
1788
  if dont_show_plots:
@@ -1744,11 +1802,13 @@ def plot_x_velocities_by_average_SC(average_data, std_data, info, cfg):
1744
1802
  plot_SE = cfg["plot_SE"]
1745
1803
  sc_num = cfg["sc_num"]
1746
1804
  hind_joints = cfg["hind_joints"]
1805
+ legend_outside = cfg["legend_outside"]
1806
+ color_palette = cfg["color_palette"]
1747
1807
 
1748
1808
  # plot
1749
1809
  f, ax = plt.subplots(1, 1)
1750
1810
  ax.set_prop_cycle(
1751
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, len(hind_joints))))
1811
+ plt.cycler("color", sns.color_palette(color_palette, len(hind_joints)))
1752
1812
  )
1753
1813
  x = np.linspace(0, 100, bin_num)
1754
1814
  ax.set_title(name + " - Joint velocities over average step cycle")
@@ -1761,7 +1821,11 @@ def plot_x_velocities_by_average_SC(average_data, std_data, info, cfg):
1761
1821
  this_std = std_data.iloc[:, y_col_idx]
1762
1822
  ax.plot(x, this_y, label=joint)
1763
1823
  ax.fill_between(x, this_y - this_std, this_y + this_std, alpha=0.2)
1764
- ax.legend()
1824
+ # legend adjustments
1825
+ if legend_outside is True:
1826
+ ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
1827
+ elif legend_outside is False:
1828
+ ax.legend()
1765
1829
  ax.set_xlabel("Percentage")
1766
1830
  ax.set_ylabel(
1767
1831
  "Velocity (x in pixel / " + str(int((1 / sampling_rate) * 1000)) + "ms)"
@@ -1789,11 +1853,13 @@ def plot_angular_velocities_by_average_SC(average_data, std_data, info, cfg):
1789
1853
  plot_SE = cfg["plot_SE"]
1790
1854
  sc_num = cfg["sc_num"]
1791
1855
  angles = cfg["angles"]
1856
+ legend_outside = cfg["legend_outside"]
1857
+ color_palette = cfg["color_palette"]
1792
1858
 
1793
1859
  # plot
1794
1860
  f, ax = plt.subplots(1, 1)
1795
1861
  ax.set_prop_cycle(
1796
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, len(angles["name"]))))
1862
+ plt.cycler("color", sns.color_palette(color_palette, len(angles["name"])))
1797
1863
  )
1798
1864
  x = np.linspace(0, 100, bin_num)
1799
1865
  ax.set_title(name + " - Angular velocities over average step cycle")
@@ -1808,7 +1874,11 @@ def plot_angular_velocities_by_average_SC(average_data, std_data, info, cfg):
1808
1874
  this_std = std_data.iloc[:, y_col_idx]
1809
1875
  ax.plot(x, this_y, label=angle)
1810
1876
  ax.fill_between(x, this_y - this_std, this_y + this_std, alpha=0.2)
1811
- ax.legend()
1877
+ # legend adjustments
1878
+ if legend_outside is True:
1879
+ ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
1880
+ elif legend_outside is False:
1881
+ ax.legend()
1812
1882
  figure_file_string = " - Angular velocities over average step cycle"
1813
1883
  save_figures(f, results_dir, name, figure_file_string)
1814
1884
  if dont_show_plots:
@@ -1828,11 +1898,13 @@ def plot_x_acceleration_by_average_SC(average_data, std_data, info, cfg):
1828
1898
  plot_SE = cfg["plot_SE"]
1829
1899
  sc_num = cfg["sc_num"]
1830
1900
  hind_joints = cfg["hind_joints"]
1901
+ legend_outside = cfg["legend_outside"]
1902
+ color_palette = cfg["color_palette"]
1831
1903
 
1832
1904
  # plot
1833
1905
  f, ax = plt.subplots(1, 1)
1834
1906
  ax.set_prop_cycle(
1835
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, len(hind_joints))))
1907
+ plt.cycler("color", sns.color_palette(color_palette, len(hind_joints)))
1836
1908
  )
1837
1909
  x = np.linspace(0, 100, bin_num)
1838
1910
  ax.set_title(name + " - Joint accelerations over average step cycle")
@@ -1845,7 +1917,11 @@ def plot_x_acceleration_by_average_SC(average_data, std_data, info, cfg):
1845
1917
  this_std = std_data.iloc[:, y_col_idx]
1846
1918
  ax.plot(x, this_y, label=joint)
1847
1919
  ax.fill_between(x, this_y - this_std, this_y + this_std, alpha=0.2)
1848
- ax.legend()
1920
+ # legend adjustments
1921
+ if legend_outside is True:
1922
+ ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
1923
+ elif legend_outside is False:
1924
+ ax.legend()
1849
1925
  ax.set_xlabel("Percentage")
1850
1926
  ax.set_ylabel(
1851
1927
  "Acceleration (x in pixel / " + str(int((1 / sampling_rate) * 1000)) + "ms)"
@@ -1875,11 +1951,13 @@ def plot_angular_acceleration_by_average_SC(average_data, std_data, info, cfg):
1875
1951
  plot_SE = cfg["plot_SE"]
1876
1952
  sc_num = cfg["sc_num"]
1877
1953
  angles = cfg["angles"]
1954
+ legend_outside = cfg["legend_outside"]
1955
+ color_palette = cfg["color_palette"]
1878
1956
 
1879
1957
  # plot
1880
1958
  f, ax = plt.subplots(1, 1)
1881
1959
  ax.set_prop_cycle(
1882
- plt.cycler("color", plt.cm.viridis(np.linspace(0, 1, len(angles["name"]))))
1960
+ plt.cycler("color", sns.color_palette(color_palette, len(angles["name"])))
1883
1961
  )
1884
1962
  x = np.linspace(0, 100, bin_num)
1885
1963
  ax.set_title(name + " - Angular accelerations over average step cycle")
@@ -1896,7 +1974,11 @@ def plot_angular_acceleration_by_average_SC(average_data, std_data, info, cfg):
1896
1974
  this_std = std_data.iloc[:, y_col_idx]
1897
1975
  ax.plot(x, this_y, label=angle)
1898
1976
  ax.fill_between(x, this_y - this_std, this_y + this_std, alpha=0.2)
1899
- ax.legend()
1977
+ # legend adjustments
1978
+ if legend_outside is True:
1979
+ ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
1980
+ elif legend_outside is False:
1981
+ ax.legend()
1900
1982
  figure_file_string = " - Angular acceleration over average step cycle"
1901
1983
  save_figures(f, results_dir, name, figure_file_string)
1902
1984
  if dont_show_plots:
@@ -1937,6 +2019,30 @@ def tickconvert_mm_to_cm(axis, whichlabel):
1937
2019
  axis.set_ylabel("y (cm)")
1938
2020
 
1939
2021
 
2022
+ def generate_sc_latency_label(all_steps_data, this_sc_idx, sampling_rate, time_col_idx):
2023
+ if sampling_rate <= 100:
2024
+ float_precision = 2 # how many decimals we round to
2025
+ else:
2026
+ float_precision = 4
2027
+ this_label = (
2028
+ str(
2029
+ round(
2030
+ all_steps_data.iloc[this_sc_idx[0], time_col_idx],
2031
+ float_precision,
2032
+ )
2033
+ )
2034
+ + "-"
2035
+ + str(
2036
+ round(
2037
+ all_steps_data.iloc[this_sc_idx[-1], time_col_idx],
2038
+ float_precision,
2039
+ )
2040
+ )
2041
+ + "s"
2042
+ )
2043
+ return this_label
2044
+
2045
+
1940
2046
  # %% local functions 5 - print finish
1941
2047
 
1942
2048