Skip to main content

A Python package for computing countermovement and squat jump events and metrics from force plate data

Project description

JumpMetrics

License: MIT Python Version PyPI version

Overview

The JumpMetrics package is a completely free, open-source toolkit for analyzing force plate data during countermovement and squat (pause) jumps. The package provides comprehensive analyses to help in understanding various aspects of jump performance and mechanics and helps with batch processing jump trials.

Features

  • Batch processing of countermovement jump and squat jump data from .txt files exported from force plates (e.g., NetForce).
  • Signal processing and filtering.
  • Automated data cropping, events, metrics, and visualization tools.
  • Easy-to-use functions for detailed jump performance insights.
  • Extensible framework for additional analysis and custom data processing.

Installation

Prerequisites

  • Python: Ensure Python 3.10 or higher is installed.
  • pixi: Install pixi on your machine.

Option 1: Install via PyPI (Recommended)

pip install jumpmetrics

or if using pixi, run:

pixi add jumpmetrics

Option 2: Direct Installation from GitHub (may be slow due to repo size)

Ensure that you have pip installed in your python environment. Then, just run:

pip install git+https://github.com/stevenhirsch/force-plate-jump-analyses.git 

or if using pixi, run:

pixi add git+https://github.com/stevenhirsch/force-plate-jump-analyses.git

Method B: Using pip directly

  1. Clone the repository:
git clone https://github.com/stevenhirsch/force-plate-jump-analyses.git
cd force-plate-jump-analyses
  1. Build the package:
pip install build
python -m build
  1. Install the wheel file (adjust the filename to whichever version you have built):
pip install dist/jumpmetrics-0.1.0-py3-none-any.whl

After going through these steps, verify the installation:

python test_install.py

Option 3: Development Setup with pixi

  1. Clone the repository:
git clone https://github.com/stevenhirsch/force-plate-jump-analyses.git
cd force-plate-jump-analyses
  1. Create and activate the development environment:
pixi install
pixi shell --feature dev

This will set up a development environment with all necessary dependencies including:

  • Core dependencies: pandas, matplotlib, scipy, scikit-learn
  • Development tools: pytest, mypy, flake8, jupyter, ipython
  • Visualization libraries: plotly, seaborn
  • Build tools and utilities

The development environment provides additional tools for contributing to the project.

Available pixi Tasks

The project includes several predefined tasks that can be run using pixi run <task-name>:

Main Tasks (Default Environment)

  • verify: Run installation verification test
  • batch_process_study_1: Process data for study 1
  • batch_process_study_2: Process data for study 2
  • batch_process_study_3: Process data for study 3

Development Tasks

  • build: Build the package distribution
  • lint: Run flake8 linting on source code
  • clean: Remove build artifacts and cache files
  • test: Run pytest test suite
  • typecheck: Run mypy type checking

Example usage:

# Run tests
pixi run test

# Build the package
pixi run build

# Run linting
pixi run lint

Option 4: Using Docker

For users who prefer Docker or desire a reproducible environment across different systems, we also provide a Dockerfile to easily set up and run jumpmetrics. This Dockerfile provides a way for you to separate your environment (the Docker image) from the analysis code (mounted scripts).

  1. Build the Docker image:
docker build -t jumpmetrics .

You can also add tags to this build:

docker build -t jumpmetrics:v1.0 .
  1. Create your analysis script. See an example at docker_example/scripts/docker_example.py.

  2. Run your analysis with Docker:

docker run -it --rm \
  -v /path/to/your/scripts:/scripts \
  -v /path/to/your/input/data:/data/input \
  -v /path/to/your/output:/data/output \
  jumpmetrics python /scripts/my_analysis.py

The Docker container provides a pre-configured environment with jumpmetrics installed. You can:

  • Mount your analysis scripts using -v /path/to/your/scripts:/scripts
  • Mount your input data using -v /path/to/your/input/data:/data/input
  • Mount an output directory using -v /path/to/your/output:/data/output

Directory structure example:

~/my_jump_analysis/
  ├── scripts/
  │   └── my_analysis.py    # Your analysis script
  ├── input/
  │   └── F02_CTRL1.txt    # Your force plate data
  └── output/              # Results will appear here
      ├── jump_metrics.csv
      ├── kinematic_data.csv
      └── force_curve.png

With the current repository structure, you could therefore run:

docker run -it --rm \
  -v ./docker_example/scripts:/scripts \
  -v ./docker_example/input:/data/input \
  -v ./docker_example/output:/data/output \
  jumpmetrics python /scripts/docker_example.py

to test this output (check out /docker_example/input and /docker_example/output to see what this looks like).

This setup allows you to:

  1. Keep your analysis scripts separate from the package
  2. Modify your analysis without rebuilding the Docker image
  3. Run different analyses using the same container
  4. Share your analysis scripts while ensuring they run in the same environment

If you want to explore this environment interactively, run:

docker run -it jumpmetrics bash

Data Processing

The following code snippet should help to generally showcase how one could get started quickly with jumpmetrics for calculating takeoff metrics:

from jumpmetrics.core.processors import ForceTimeCurveCMJTakeoffProcessor
from jumpmetrics.core.io import (
    load_raw_force_data_with_no_column_headers, sum_dual_force_components,
    find_first_frame_where_force_exceeds_threshold,
    find_frame_when_off_plate, get_n_seconds_before_takeoff
)
from jumpmetrics.signal_processing.filters import butterworth_filter

# Load a force dataset
tmp_force_df = load_raw_force_data_with_no_column_headers(filepath)
# Sum the vertical force components from a data collection that uses dual force plates
# Note that the goal is to simply just get a force waveform, and these are helper functions to do so
# However, you could use your own custom code to obtain a force trace for processing
full_summed_force = sum_dual_force_components(tmp_force_df)
# Helper function to help narrow force series to identify when someone is on or off the plate
frame = find_first_frame_where_force_exceeds_threshold(
    force_trace=full_summed_force,
    threshold=1000
)
# Helper function to find when someone is off the plate. Can be used to determine the moment of takeoff
takeoff_frame = find_frame_when_off_plate(
    force_trace=full_summed_force.iloc[frame:],
    sampling_frequency=2000
)
# Cropped force trace provides just the first n seconds before takeoff for processing
 cropped_force_trace = get_n_seconds_before_takeoff(
            force_trace=full_summed_force,
            sampling_frequency=2000,
            takeoff_frame=takeoff_frame,
            n=TIME_BEFORE_TAKEOFF
)
# Filtering the force trace data (optional step)
filtered_force_series = butterworth_filter(
    arr=cropped_force_trace,
    cutoff_frequency=50,
    fps=2000,
    padding=2000
)
# Instantiative the takeoff processor class
CMJ = ForceTimeCurveCMJTakeoffProcessor(
    force_series=filtered_force_series,
    sampling_frequency=2000
)
# Get the jump events
CMJ.get_jump_events()
# Get the jump metrics
CMJ.compute_jump_metrics()
# Create a jump metric dataframe
CMJ.create_jump_metrics_dataframe()
# Create a kinematic data dataframe
CMJ.create_kinematic_dataframe()
# Plot the waveform data
CMJ.plot_waveform(
    waveform_type='force',
    title='Testing',
    savefig=True,
    figname=os.path.join('force.png')
)
# Save the dataframe
CMJ.save_kinematic_dataframe(
    dataframe_filepath=os.path.join(pid_data_dir, 'kinematic_data.csv')
)

Processing an entire jump (takeoff and landing) can be done using the following code example OR building your own wrapper functions using the landing and takeoff classes.

tmp_force_df = load_raw_force_data_with_no_column_headers(filepath)
full_summed_force = sum_dual_force_components(tmp_force_df)
results_dict = process_jump_trial(
    full_force_series=full_summed_force,
    sampling_frequency=2000,
    jump_type='countermovement',
    weighing_time=0.25,
    pid='test1',
    threshold_for_helping_determine_takeoff=1000,
    lowpass_filter=True,
    lowpass_cutoff_frequency=26.64,
    compute_jump_height_from_flight_time=True
)

For a complete guide on available functions and their usage, please refer to the Documentation.

Example Output

The following is an example of the events that can be detected during the takeoff phase of a vertical jump:

Example countermovement jump force-time trace with events detected during the takeoff phase.

These events are the foundation for all computed metrics. For a full list of metrics for both the takeoff and landing phases, please refer to the tables in .

Assumptions In this Package

Please read the paper for full details. A summary of a portion of the paper is provided below:

To compute the relevant events and metrics, there are specific methods that a user should adhere to that are outlined in previous literature. These methods underpin the assumptions required to collect and process data with the code provided in this package. First, the jumper must stand still (i.e., minimizing swaying or any other body movements) at the start of the data collection and for at least 1 second before starting the initiation of the jump. This quiet standing is used to calculate one's bodyweight, and bodyweight is used for subsequent acceleration, velocity, and displacement calculations used for event detections (as well as for computing net vertical impulse). The default setting in this package is currently to use the first 0.4 seconds of the trial to compute bodyweight (as this was found to work well for previous analyses), but users can tune this parameter themselves for their own data collections depending on the length of the quiet standing at the start of the data collection. For a countermovement jump, the functions in this package also require the person to perform one continuous downwards and upwards motion during the jump; any pausing may negatively impact the event detection algorithms provided. In contrast, for the squat jump the default parameter for identifying the start of the propulsive phase expects at least a 1 second pause. In practice, previous research has outlined a pause should be approximately 3 seconds. The functions provided in JumpMetrics permit the user to select a different minimum pause to assume if the default of 1 second is not appropriate for their research.

Reporting Issues

If you encounter any issues, please open an issue on GitHub.

Contributing

We welcome contributions from researchers, practitioners, and developers! Please see our Contributing Guidelines for details on how to get started.

Testing and Code Coverage

JumpMetrics maintains a comprehensive test suite to ensure reliability and correctness of the analysis algorithms.

Test Coverage

  • Overall Coverage: 84% (543/650 lines)
  • Test Suite: 203 tests including unit tests, integration tests, and edge case validation
  • Critical Algorithm Coverage: 100% coverage of event detection, metrics computation, and signal processing modules

Coverage Breakdown

Module Coverage Lines Covered
events/ 95%+ Event detection algorithms
metrics/ 100% Jump metrics computation
signal_processing/ 98%+ Filtering and numerical methods
core/io.py 96% Data loading and file handling
core/processors.py 66% Main processor classes

Running Tests

# Run all tests
pixi run test

# Run tests with coverage report
pixi run test --cov=src/jumpmetrics --cov-report=html

# View detailed coverage report
open htmlcov/index.html

Test Philosophy

The test suite emphasizes:

  • Comprehensive edge case testing to ensure robust error handling
  • Integration tests with real force plate data to validate end-to-end functionality
  • Algorithm correctness verification for all event detection and metrics computation
  • Graceful failure handling for unusual or invalid input data

All critical analysis algorithms maintain 100% test coverage, ensuring reliable scientific computations.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Contact

For any questions or further information, please contact me on my website or via LinkedIn.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jumpmetrics-0.1.2.tar.gz (6.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jumpmetrics-0.1.2-py3-none-any.whl (28.3 kB view details)

Uploaded Python 3

File details

Details for the file jumpmetrics-0.1.2.tar.gz.

File metadata

  • Download URL: jumpmetrics-0.1.2.tar.gz
  • Upload date:
  • Size: 6.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for jumpmetrics-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9f3b118193606ebecf53247ac7590c77965dac4d30e87c56be8102b4f60defea
MD5 de0bdbccc9bab3fb7ca55b6b3ab38d59
BLAKE2b-256 ac11abc73ade98482285960034df134a7c6101940c360814a1095855a3089d35

See more details on using hashes here.

File details

Details for the file jumpmetrics-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: jumpmetrics-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 28.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for jumpmetrics-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b8ac5c1b6fd5fa99678195e08eba8a962b9bc0c63d5304eb424739ef1b0261be
MD5 625f745f9efb79abae157601441a2a79
BLAKE2b-256 69af47e0a6782e6459cda36fce268f21028dda3cf7f3972cd7a17e2fd2796bc7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page