Anaropia
This module provides functions to import and preprocess data collected from the Anaropia virtual reality software package.
- class balancepy.anaropia.AnaropiaPreprocessingConfig(anaropia_version='legacy', samplingrate_Hz=90, resample=True, end_time_seconds=260, filter_type=None, filter_order=2, filter_cutoff_Hz=8, cut_to_cycles=False, cycle_start_samples=1800, cycle_length_samples=1800)
Bases:
objectConfiguration for Anaropia data preprocessing.
Unified configuration class for both standard and legacy Anaropia data processing. All parameters have sensible defaults, allowing flexible use for different scenarios.
- samplingrate_Hz
Desired sampling rate in Hz. 0 means no resampling. Used for standard Anaropia.
- Type:
int, default=90
- resample
If True, resample data to samplingrate_Hz.
- Type:
bool, default=True
- filter_type
Type of filter to apply: ‘low’, ‘high’, ‘band’, or None for no filtering. Zeros phase filtering is applied to avoid phase shifts.
- Type:
str, default=None
- filter_order
Order of the filter.
- Type:
int, default=2
- filter_cutoff_Hz
Cutoff frequency for filtering. For ‘band’ filter_type, provide a tuple (low, high).
- Type:
float, default=8
- cut_to_cycles
If True, cut data to cycles.
- Type:
bool, default=True
- end_time_seconds
End time of the experiment in seconds for resampling. Standard: 260, Legacy: 220 (adjust as needed).
- Type:
float, default=260
- cycle_start_samples
Start of the first cycle in samples (20*90 = 1800).
- Type:
int, default=1800
- cycle_length_samples
Cycle length in samples (20*90 = 1800).
- Type:
int, default=1800
Examples
Legacy Anaropia:
>>> config = AnaropiaPreprocessingConfig(stimulus_name='stim_pitch', end_time_seconds=220) >>> com, time = getdata_legacy('legacy_data.csv', output='com', body_height_m=1.75, config=config) >>> stim, time = getdata_legacy('legacy_data.csv', output='stimulus', body_height_m=1.75, config=config)
Standard Anaropia:
>>> config = AnaropiaPreprocessingConfig() >>> com, time = getdata_anaropia('data.csv', output='com', direction='ap', body_height_m=1.75, config=config) >>> stim_ap, time = getdata_anaropia('data.csv', output='stimulus', direction='ap', body_height_m=1.75, config=config) >>> stim_ml, time = getdata_anaropia('data.csv', output='stimulus', direction='ml', body_height_m=1.75, config=config)
- anaropia_version: str = 'legacy'
- cut_to_cycles: bool = False
- cycle_length_samples: int = 1800
- cycle_start_samples: int = 1800
- end_time_seconds: float = 260
- filter_cutoff_Hz: float = 8
- filter_order: int = 2
- filter_type: str = None
- resample: bool = True
- samplingrate_Hz: int = 90
- balancepy.anaropia.batch_iterator(metadata, subjects=None, conditions=None, data_dir='data/raw', skip_nfu=[4, 5, 6])
Iterate over subject-condition combinations in metadata, yielding file paths and configs.
Yields one tuple per valid subject-condition pair, with flexible indexing: - Subjects: row number (int), subject ID (str), or list of either - Conditions: condition number (int, e.g. 1 for “c1: …”), full name (str), or list of either
Missing files indicated by -1 in the metadata cell are skipped silently without warnings.
- Parameters:
metadata (
DataFrame) – Metadata table (typically from get_metadata()).subjects (
Union[int,str,List[Union[int,str]],None]) – Subject(s) to process. Can be: - None: all subjects - int: single row number (0-indexed) - str: single subject ID - list: mix of row numbers and/or subject IDs, e.g. [0, ‘MW09’, 2]conditions (
Union[int,str,List[Union[int,str]],None]) – Condition(s) to process. Can be: - None: all conditions - int: single condition number (1, 2, 3, …) - str: full condition name (e.g. ‘c1: eyes open’) - list: mix of numbers and/or names, e.g. [1, ‘c2: eyes closed’]data_dir (
str) – Data directory relative to parent folder. Default: ‘data/raw’skip_nfu (
Optional[List[int]]) – NFU (Not For Use) categories to skip. Entries with matching NFU values will not be yielded. Convention: NFU columns are named “NFU {conditionname}”. Default: [4, 5, 6] (Poor FRF Fit, Marker Missing, File Missing). Set to None to disable NFU filtering.
- Yields:
subject_id (str) – Subject identifier from metadata
condition (str) – Full condition column name (e.g. ‘c1: eyes open’)
filepath (str) – Full path to data file
config (AnaropiaPreprocessingConfig) – Config with body_height_m set from metadata
Notes
Files marked with -1 in the metadata condition cell are treated as missing and are skipped silently without warning messages.
Entries with NFU (Not For Use) values in the skip_nfu list are also skipped silently. The NFU column is identified by the naming convention “NFU {conditionname}”. By default, NFU categories 4, 5, and 6 are skipped (Poor FRF Fit, Marker Missing, File Missing). Set skip_nfu=None to disable this filtering.
Examples
>>> # All subjects, first 2 conditions (skips NFU 4, 5, 6 by default) >>> for sid, cond, fp, cfg in batch_iterator(metadata, conditions=[1, 2]): ... plot_datacheck(fp, cfg)
>>> # Specific subjects by ID, all conditions >>> for sid, cond, fp, cfg in batch_iterator(metadata, subjects=['MW09', 'AN16']): ... plot_datacheck(fp, cfg)
>>> # Row 0, condition by name, skip only NFU 6 (file missing) >>> for sid, cond, fp, cfg in batch_iterator(metadata, subjects=0, conditions='c1: eyes open', skip_nfu=[6]): ... plot_datacheck(fp, cfg)
>>> # Disable NFU filtering entirely >>> for sid, cond, fp, cfg in batch_iterator(metadata, subjects=[0, 'MW09'], conditions=[1, 2], skip_nfu=None): ... # process each file ... pass
- balancepy.anaropia.get_filename_from_metadata(metadata=None, subject=None, condition=None, data_dir='data/raw')
Obtain the data filename from metadata, with intelligent fallback and fuzzy matching.
Retrieves a data filename from metadata by subject and condition, with automatic defaults and fuzzy file matching. If the metadata cell contains just a number, searches for filenames matching: _s{subject_id}_t{number}.csv
- Parameters:
metadata (
DataFrame|None) – Metadata DataFrame. If None, loads default metadata file from ‘../data/metadata.xlsx’.subject (
str|int|None) –str→ matched against the ‘Subject ID’ column (e.g.'MW09AB13').int→ used as a zero-based row index (e.g.0for first subject).None→ defaults to row 0.condition (
str|int|None) –str→ exact or prefix match against condition column names (e.g.'c1: eyes open','c1').int→ condition number, matched asc{n}...(e.g.1→'c1: ...').None→ uses the first condition column found.data_dir (
str) – Directory where data files are located.
- Returns:
Full path to the data file.
- Return type:
str- Raises:
FileNotFoundError – If metadata or data files not found, or if no matching file exists.
ValueError – If subject not found, row index out of bounds, or condition not found.
Examples
>>> filepath = get_filename_from_metadata(metadata, 'MW09AB13', 'c1: eyes open') >>> filepath = get_filename_from_metadata(metadata, 'MW09AB13', 1) >>> filepath = get_filename_from_metadata(metadata, 0, 'c1: eyes open') >>> filepath = get_filename_from_metadata(metadata, 0, 1)
- balancepy.anaropia.get_metadata(filename=None, print_information=True)
Load and validate metadata from an Excel file.
This function reads metadata containing subject information and experimental conditions, then performs several checks: - Validates that all required columns are present and properly formatted - Extracts condition information (columns starting with ‘c’) - Reports data quality metrics for each condition using NFU (Not For Use) scores
- Parameters:
filename (
str) – Path to metadata Excel file. If None, loads default file from ‘../data/metadata.xlsx’ relative to current working directory.- Returns:
Metadata table with subject information and condition data.
- Return type:
DataFrame- Raises:
Exception – If no condition columns are found in the metadata file.
Required Columns –
---------------- –
The metadata file must contain the following columns: –
:raises - Subject ID : Unique identifier for each subject: :raises - body_height_m : Subject height in meters: :raises - body_weight_kg : Subject weight in kilograms: :raises - age_years : Subject age in years: :raises - sex : Subject sex (e.g., ‘m’, ‘w’): :raises Column Naming Conventions: :raises ————————-: :raises Condition columns : Start with ‘c’ (e.g., ‘c1: eyes open’, ‘c2: eyes closed’): :raises NFU columns : Start with ‘NFU ‘ followed by condition name (e.g., ‘NFU c1: eyes open’): :raises NFU (Not For Use) Category Definitions: :raises —————————————-: :raises Score 0 - All Good : The data file is complete, with no irregularities detected.: :raises Score 1 - Big Movements : Excessive participant movements were detected.: :raises Score 2 - >5 HMD Spikes : The data contains more than five spikes in the: Head-Mounted Display (HMD) measurements. :raises Score 3 - Other Irregularities : Any other issues not covered by the above categories: that might affect data quality. :raises Score 4 - Poor FRF Fit : The Frequency Response Function (FRF) fit does not meet: the required criteria. :raises Score 5 - Marker Missing : One or more required markers are missing.: :raises Score 6 - File Missing : The corresponding data file is not available.: :raises Data Quality Grouping: :raises ———————-: :raises - Valid (0): Ready for analysis: :raises - Warnings (1-3): Use with caution, review data quality: :raises - Invalid (4-6): Do not use for analysis:
- balancepy.anaropia.getdata_anaropia(filename, output='com', direction='ap', body_height_m=None, body_weight_kg=None, config=None)
Access and format data from balance experiments recorded with Anaropia.
Reads data recorded using the Anaropia virtual-reality application for balance experiments. Calculates stimulus and center of mass (COM) data.
- Parameters:
filename (
str) – Path and filename to be analyzed.output (
str) –Specifies which data column to return alongside time. - ‘com’ : Center of mass sway (computed from shoulder/hip positions). - ‘stimulus’ : Stimulus signal (column determined by config.stimulus_name
and the direction parameter).
any other str : Raw column name from the data file (e.g. ‘LeftShoulder_pos_z’).
body_height_m (
float) – Used for center of mass calculations.body_weight_kg (
float) – Used for center of pressure calculations.config (
AnaropiaPreprocessingConfig) – Configuration object containing processing parameters. If None, uses default configuration with standard settings.
- Return type:
tuple- Returns:
signal (NDArray) – The requested data array (com, stimulus, or raw column).
time (NDArray) – Time data.
See also
AnaropiaPreprocessingConfigConfiguration class for Anaropia data preprocessing
Examples
>>> config = AnaropiaPreprocessingConfig() >>> com, time = getdata_anaropia('data.csv', output='com', direction='ap', body_height_m=1.75, config=config) >>> stim_ap, time = getdata_anaropia('data.csv', output='stimulus', direction='ap', body_height_m=1.75, config=config) >>> stim_ml, time = getdata_anaropia('data.csv', output='stimulus', direction='ml', body_height_m=1.75, config=config)
- balancepy.anaropia.plot_datacheck(filename, body_height_m=None, config=None, save_fig=True)
Plot data for comprehensive visual inspection of quality and structure.
Generates a DinA4 landscape-formatted figure with multiple subplots for visually inspecting stimulus and response data using Plotly. The figure includes raw data translations, resampled stimulus and response with cycle indicators, and cycle-by-cycle analysis.
- Parameters:
filename (
str) – Path to the Anaropia data file (CSV format) to be plotted.config (
AnaropiaPreprocessingConfig) – Configuration object for data preprocessing. If None, uses default configuration.subject_id (str, optional) – Subject identifier for output filename. If None, uses input filename stem.
condition_name (str, optional) – Condition name for output filename. If None, uses input filename stem.
output_dir (str, optional) – Directory where the HTML will be saved. If None, displays the figure in browser.
- Returns:
Path to the saved HTML file, or None if displayed instead of saved.
- Return type:
str
Example
>>> config = AnaropiaPreprocessingConfig(body_height_m=1.75) >>> plot_datacheck('data/raw/sMW09ON27_t1.csv', config, save_fig=True, ... subject_id='MW09', condition_name='t1', ... output_dir='results/')
- balancepy.anaropia.run_csmi(filename, body_height_m, body_weight_kg, name=None, config=None)
Run a CSMI (Continuous Sensory Manipulation Identification) analysis for one trial.
Loads the stimulus and COM response from filename, constructs an sr_data object, fits the Peterka18 model, and returns the fitted model object.
- Parameters:
filename (
str) – Path to the Anaropia CSV data file.body_height_m (
float) – Subject body height in metres.body_weight_kg (
float) – Subject body weight in kilograms.config (
AnaropiaPreprocessingConfig) – Preprocessing configuration. Defaults to AnaropiaPreprocessingConfig().
- Returns:
subj – Fitted Peterka18 model object with .params, .fit_output, and .plot().
- Return type:
Examples
>>> config = bp.AnaropiaPreprocessingConfig() >>> config.end_time_seconds = 220 >>> config.cut_to_cycles = True >>> subj = bp.run_csmi(filename, body_height_m=1.75, body_weight_kg=70, config=config) >>> subj.plot()
- balancepy.anaropia.run_csmi_batch(metadata, subjects=None, conditions=None, config=None, n_jobs=-1, plot=False, overwrite_plots=True)
Run CSMI analysis for all subjects and conditions in parallel.
Returns a results DataFrame (Subject ID + fitted parameters) that can be saved independently and merged with metadata as needed. Fitted parameter columns follow the naming convention
{param_name}_{condition_slug}(e.g.W_c5_s0_v1).- Parameters:
metadata (
DataFrame) – Metadata table as returned by get_metadata(). Used for file lookup and to extract body_height_m / body_weight_kg per subject.subjects (
Union[int,str,List[Union[int,str]],None]) – Subject selection passed to batch_iterator. None = all subjects.conditions (
Union[int,str,List[Union[int,str]],None]) – Condition selection passed to batch_iterator. None = all conditions.config (
AnaropiaPreprocessingConfig) – Shared preprocessing config for all trials. Defaults to AnaropiaPreprocessingConfig().n_jobs (
int) – Number of parallel workers (passed to joblib.Parallel). -1 uses all available CPUs.plot (
bool) – If True, saves a Bode plot for each subject/condition asresults/csmi_plots/csmi_{subject_id}_{condition_slug}.pdf.overwrite_plots (
bool) – If False, skips saving a plot when the PDF file already exists. Has no effect whenplot=False.
- Returns:
Results table with ‘Subject ID’ as key plus one column per fitted parameter per condition. One row per subject.
- Return type:
DataFrame
Examples
>>> config = bp.AnaropiaPreprocessingConfig() >>> config.end_time_seconds = 220 >>> config.cut_to_cycles = True >>> csmi_df = bp.run_csmi_batch(metadata, conditions=['c5: s0_v1', 'c6: s0_v2'], config=config) >>> csmi_df.to_parquet('../results/csmi_results.parquet', index=False) >>> metadata = metadata.merge(csmi_df, on='Subject ID', how='left')
- balancepy.biomechanics.get_com(shoulder_t, shoulder_marker_height, hip_t, hip_marker_height, height_m, rotation=True)
Calculate center of mass (COM) from hip and shoulder movement.
The function uses a 2-segment model and anthropometric tables.
- Parameters:
shoulder_t (
ndarray[tuple[Any,...],dtype[number]]) – 1D array of shoulder translation in meters.shoulder_marker_height (
number) – Shoulder marker height above support surface in meters. Can be a single value or an array.hip_t (
ndarray[tuple[Any,...],dtype[number]]) – 1D array of hip anterior-posterior translation in meters.hip_marker_height (
number) – Hip marker height above support surface in meters. Can be a single value or an array.height_m (
float) – Height of subject in meters.rotation (
bool) – If False, returns COM translation in meters. If True, returns COM rotation about ankle joints in degrees.
- Returns:
1D array of center of mass (COM) values.
- Return type:
ndarray[tuple[Any,...],dtype[TypeVar(_ScalarT, bound=generic)]]