33import numpy as np
44import pandas as pd
55import xarray as xr
6+ import logging
67
78import gaitalytics .events as events
89import gaitalytics .io as io
1213import gaitalytics .utils .math as math
1314import gaitalytics .utils .mocap as mocap
1415
16+ logger = logging .getLogger (__name__ )
17+
1518
1619class FeatureCalculation (ABC ):
1720 """Base class for feature calculations.
@@ -542,17 +545,17 @@ def _calculate(self, trial: model.Trial) -> xr.DataArray:
542545 )
543546 )
544547
545- toe_markers = [marker_dict ["ipsi_toe_2" ]]
546- if marker_dict ["ipsi_toe_5" ] is not None :
547- toe_markers .append (marker_dict ["ipsi_toe_5" ])
548+ toe_markers = [marker_dict ["ipsi_toe_2" ], marker_dict ["ipsi_toe_5" ]]
549+
548550 results_dict .update (
549551 self ._calculate_minimal_toe_clearance (
550552 trial ,
551553 toe_markers , # type: ignore
552554 marker_dict ["ipsi_heel" ], # type: ignore
553555 )
554556 )
555- if marker_dict ["xcom" ] is not None :
557+
558+ try :
556559 results_dict .update (
557560 self ._calculate_ap_margin_of_stability (
558561 trial ,
@@ -561,23 +564,26 @@ def _calculate(self, trial: model.Trial) -> xr.DataArray:
561564 marker_dict ["xcom" ],
562565 )
563566 )
564- if (marker_dict ["ipsi_ankle" ] is not None ) and (
565- marker_dict ["contra_ankle" ] is not None
566- ):
567- results_dict .update (
568- self ._calculate_ml_margin_of_stability (
569- trial ,
570- marker_dict ["ipsi_ankle" ],
571- marker_dict ["contra_ankle" ],
572- marker_dict ["xcom" ],
573- )
567+
568+ results_dict .update (
569+ self ._calculate_ml_margin_of_stability (
570+ trial ,
571+ marker_dict ["ipsi_ankle" ],
572+ marker_dict ["contra_ankle" ],
573+ marker_dict ["xcom" ],
574574 )
575+ )
576+ except KeyError :
577+ logger .info (
578+ "Margin of stability markers are missing. No margin of stability calculated"
579+ )
575580
576581 return self ._create_result_from_dict (results_dict )
577582
583+ @staticmethod
578584 def select_markers_for_spatial_features (
579- self , trial : model .Trial
580- ) -> dict [str , mapping .MappedMarkers | None ]:
585+ trial : model .Trial ,
586+ ) -> dict [str , mapping .MappedMarkers ]:
581587 """Select markers based on the trial's context (Right or Left). If some markers are missing, return them as None
582588
583589 Args:
@@ -589,24 +595,24 @@ def select_markers_for_spatial_features(
589595 if trial .events is not None and trial .events .attrs ["context" ] == "Right" :
590596 ipsi_heel_marker = mapping .MappedMarkers .R_HEEL
591597 ipsi_toe_2_marker = mapping .MappedMarkers .R_TOE
592- ipsi_toe_5_marker = self . get_optional_marker ( "R_TOE_5" )
593- ipsi_ankle_marker = self . get_optional_marker ( " R_ANKLE" )
598+ ipsi_toe_5_marker = mapping . MappedMarkers . R_TOE_2
599+ ipsi_ankle_marker = mapping . MappedMarkers . R_ANKLE
594600
595601 contra_toe_2_marker = mapping .MappedMarkers .L_TOE
596602 contra_heel_marker = mapping .MappedMarkers .L_HEEL
597- contra_ankle_marker = self . get_optional_marker ( " L_ANKLE" )
603+ contra_ankle_marker = mapping . MappedMarkers . L_ANKLE
598604
599605 else :
600606 ipsi_heel_marker = mapping .MappedMarkers .L_HEEL
601607 ipsi_toe_2_marker = mapping .MappedMarkers .L_TOE
602- ipsi_toe_5_marker = self . get_optional_marker ( "L_TOE_5" )
603- ipsi_ankle_marker = self . get_optional_marker ( " L_ANKLE" )
608+ ipsi_toe_5_marker = mapping . MappedMarkers . L_TOE_2
609+ ipsi_ankle_marker = mapping . MappedMarkers . L_ANKLE
604610
605611 contra_toe_2_marker = mapping .MappedMarkers .R_TOE
606612 contra_heel_marker = mapping .MappedMarkers .R_HEEL
607- contra_ankle_marker = self . get_optional_marker ( " R_ANKLE" )
613+ contra_ankle_marker = mapping . MappedMarkers . R_ANKLE
608614
609- xcom_marker = self . get_optional_marker ( " XCOM" )
615+ xcom_marker = mapping . MappedMarkers . XCOM
610616
611617 return {
612618 "ipsi_toe_2" : ipsi_toe_2_marker ,
@@ -619,14 +625,6 @@ def select_markers_for_spatial_features(
619625 "xcom" : xcom_marker ,
620626 }
621627
622- def get_optional_marker (self , marker_name : str ) -> mapping .MappedMarkers | None :
623- """Returns the marker if exists, else returns None
624-
625- Args:
626- marker_name (str): The marker name
627- """
628- return getattr (mapping .MappedMarkers , marker_name , None )
629-
630628 def _calculate_step_length (
631629 self ,
632630 trial : model .Trial ,
@@ -774,21 +772,24 @@ def _calculate_minimal_toe_clearance(
774772
775773 # Get all ipsi toe marker positions and calculate the mean velocity of them
776774 for ipsi_toe_marker in ipsi_toe_markers :
777- ipsi_toe = self ._get_marker_data (trial , ipsi_toe_marker ).sel (
778- time = slice (event_times [3 ], event_times [4 ])
779- )
780- ipsi_toes .append (ipsi_toe )
781- if ipsi_toe_velocities is None :
782- ipsi_toe_velocities = linalg .calculate_speed_norm (ipsi_toe )
783- else :
784- ipsi_toe_velocities += linalg .calculate_speed_norm (ipsi_toe )
775+ try :
776+ ipsi_toe = self ._get_marker_data (trial , ipsi_toe_marker ).sel (
777+ time = slice (event_times [3 ], event_times [4 ])
778+ )
779+ ipsi_toes .append (ipsi_toe )
780+ if ipsi_toe_velocities is None :
781+ ipsi_toe_velocities = linalg .calculate_speed_norm (ipsi_toe )
782+ else :
783+ ipsi_toe_velocities += linalg .calculate_speed_norm (ipsi_toe )
784+ except KeyError :
785+ pass
785786
786787 if ipsi_toe_velocities is None :
787788 raise ValueError (
788789 "No toe markers found for minimal toe clearance calculation"
789790 )
790791
791- ipsi_toe_velocities /= len (ipsi_toe_markers )
792+ ipsi_toe_velocities /= len (ipsi_toes )
792793
793794 minimal_toe_clearance = np .full (1 , np .inf )
794795 for ipsi_toe in ipsi_toes :
0 commit comments