Skip to main content

Library for maximum likelihood principal component analysis for AnyBody models

Project description

sillywalk

CI pypi-version python-version

sillywalk is a Python library for statistical modeling of human motion and anthropometric data with the AnyBody Modeling System. It implements Maximum Likelihood Principal Component Analysis (ML‑PCA) to learn compact, low‑dimensional models from datasets, predict missing or individualized signals from partial inputs, and export those predictions as AnyScript include files that plug directly into AnyBody models.

Key features

  • AnyBody I/O and preprocessing: Post‑process AnyBody time series and convert them to Fourier coefficients compatible with AnyKinEqFourierDriver.
  • ML‑PCA modeling and prediction: Fit ML‑PCA models from tabular data, handle missing values naturally, and predict new samples from partial constraints; save/load models to and from .npz.
  • AnyBody model generation: Generate templated AnyScript include files (e.g., drivers and optional human model blocks) from predicted Fourier coefficients and anthropometry.
  • Friendly data interfaces: Works with pandas or polars DataFrames and NumPy arrays; installable via PyPI or pixi for reproducible workflows.

See Quick Start below for a minimal end‑to‑end example.

Installation

With pixi:

pixi add sillywalk

or from PyPI:

pip install sillywalk

or with conda:

conda create -n sillywalk -c conda-forge sillywalk
conda activate sillywalk

Developer Setup

This project uses pixi for dependency management and development tools.

git clone https://github.com/AnyBody-Research-Group/sillywalk
cd sillywalk
pixi install
pixi run test

See pixi documentation for more info.


Quick Start

1. Build a Model

import polars as pl
import sillywalk

data = {
    "Sex": [1, 1, 2, 2, 1, 2],
    "Age": [25, 30, 28, 22, 35, 29],
    "Stature": [175, 180, 165, 160, 185, 170],
    "Bodyweight": [70, 80, 60, 55, 85, 65],
    "Shoesize": [42, 44, 39, 38, 45, 40],
}
df = pl.DataFrame(data)
model = sillywalk.PCAPredictor(df)

2. Predict Missing Values

constraints = {"Stature": 180, "Bodyweight": 65}
result = model.predict(constraints)

3. Save and Load Models

model.export_pca_data("student_model.npz")
loaded = sillywalk.PCAPredictor.from_pca_data("student_model.npz")
prediction = loaded.predict({"Age": 24, "Shoesize": 43})

AnyBody Model Utilities

sillywalk can convert time series data to Fourier coefficients compatible with AnyBody's AnyKinEqFourierDriver:

import polars as pl
import numpy as np
import sillywalk

time = np.linspace(0, 1, 101)
hip = 30 * np.sin(2 * np.pi * time) + 10
knee = 60 * np.sin(2 * np.pi * time + np.pi/4)

df = pl.DataFrame({
    'Main.HumanModel.BodyModel.Interface.Trunk.PelvisThoraxExtension': hip,
    'Main.HumanModel.BodyModel.Interface.Right.KneeFlexion': knee,
})

fourier_df = sillywalk.anybody.compute_fourier_coefficients(df, n_modes=6)
print(fourier_df)

Each time series column is decomposed into Fourier coefficients (_a0 to _a5, _b1 to _b5).

┌────────────┬────────────┬───────────┬───┬───────────┬───────────┬───────────┐
│ ...tension ┆ ...tension ┆ ...tensio ┆ … ┆ ...Flexio ┆ ...Flexio ┆ ...Flexio │
│ _a0        ┆ _a1        ┆ n_a2      ┆   ┆ n_b3      ┆ n_b4      ┆ n_b5      │
│ ---        ┆ ---        ┆ ---       ┆   ┆ ---       ┆ ---       ┆ ---       │
│ f64        ┆ f64        ┆ f64       ┆   ┆ f64       ┆ f64       ┆ f64       │
╞════════════╪════════════╪═══════════╪═══╪═══════════╪═══════════╪═══════════╡
│ 10.0       ┆ 0.928198   ┆ -0.021042 ┆ … ┆ -0.550711 ┆ -0.218252 ┆ -0.169925 │
└────────────┴────────────┴───────────┴───┴───────────┴───────────┴───────────┘

Generate AnyBody Include Files

You can generate AnyScript include files from a dictionary or DataFrame with Fourier coefficients and/or anthropometric data:

Let us try to generate a model from an anthropometric dataset. First we will download the data

>>> df =  pl.read_parquet("https://anybodydatasets.blob.core.windows.net/sillywalk/kso-running/kso-running-fourier-2025-12-28-0.parquet")
>>> df
shape: (114, 1_317)
┌──────────┬────────────┬────────────┬───────────┬───┬───────────┬───────────┬───────────┬───────────┐
 freq      Main.Human  Main.Human  Main.Huma    CenterOfM  CenterOfM  CenterOfM  CenterOfM 
 ---       Model.Anth  Model.Anth  nModel.An     ass.PosZ_  ass.PosZ_  ass.PosZ_  ass.PosZ_ 
 f64       ropometric  ropometric  thropomet     b2         b3         b4         b5        
                                 ric          ---        ---        ---        ---       
           ---         ---         ---           f64        f64        f64        f64       
           f64         f64         f64                                                      
╞══════════╪════════════╪════════════╪═══════════╪═══╪═══════════╪═══════════╪═══════════╪═══════════╡
 1.415094  0.164147    0.107101    0.119941     0.000389   -0.000774  0.00013    0.000322  
 1.395349  0.164147    0.107101    0.119941     0.000571   -0.000821  0.000059   0.000282  
 1.382488  0.164147    0.107101    0.119941     -0.000097  -0.000827  0.000112   0.000301  
 1.395349  0.164147    0.107101    0.119941     0.000522   -0.000882  0.000209   0.000253  
 1.369863  0.164147    0.107101    0.119941     0.000482   -0.000949  0.000141   0.000215  
                                                                                   
 1.321586  0.1782      0.116442    0.13021      0.000114   -0.000281  -0.000132  0.000007  
 1.327434  0.1782      0.116442    0.13021      0.00038    -0.000289  -0.000112  -0.000051 
 1.382488  0.16105     0.107724    0.117678     -0.000125  -0.00011   0.000099   -0.000029 
 1.428571  0.16105     0.107724    0.117678     -0.000062  -0.000198  -0.000006  -0.00008  
 1.485149  0.16105     0.107724    0.117678     0.000787   -0.000113  0.000108   0.000021  
└──────────┴────────────┴────────────┴───────────┴───┴───────────┴───────────┴───────────┴───────────┘

This dataset contains data from a 114 running subjects, with their 'AnyBody' anthropometic dimensions and fourier coefficients to recreated their running patterns.

Sillywalk can create PCA model from this data set, and we can get a prediction of average person with a height of 1.8m.

>>> model = sillywalk.PCAPredictor(df)
>>> predicted_data = model.predict({"Height": 1.8})
>>> pl.DataFrame(predicted_data)
shape: (1, 1_317)
┌──────────┬────────────┬────────────┬───────────┬───┬───────────┬───────────┬───────────┬───────────┐
 freq      Main.Human  Main.Human  Main.Huma    CenterOfM  CenterOfM  CenterOfM  CenterOfM 
 ---       Model.Anth  Model.Anth  nModel.An     ass.PosZ_  ass.PosZ_  ass.PosZ_  ass.PosZ_ 
 f64       ropometric  ropometric  thropomet     b2         b3         b4         b5        
                                 ric          ---        ---        ---        ---       
           ---         ---         ---           f64        f64        f64        f64       
           f64         f64         f64                                                      
╞══════════╪════════════╪════════════╪═══════════╪═══╪═══════════╪═══════════╪═══════════╪═══════════╡
 1.395979  0.170115    0.112083    0.124302     -0.000044  -0.000206  -7.6567e-  0.000057  
                                                                       8                    
└──────────┴────────────┴────────────┴───────────┴───┴───────────┴───────────┴───────────┴───────────┘

Sillywalk can then create an AnyBody model for us using this data:

>>> sillywalk.anybody.write_anyscript(
...     predicted_data,
...     targetfile="predicted_motion.any"
... )

This creates AnyKinEqFourierDriver entries for use in AnyBody models.

Main.HumanModel.Anthropometrics =
{
  BodyMass = 75.61897598069909;
  BodyHeight = 1.8;
};

Main.HumanModel.Anthropometrics.SegmentDimensions =
{
  PelvisWidth = DesignVar(0.17011475699044465);
  PelvisHeight = DesignVar(0.11208281142857143);
  PelvisDepth = DesignVar(0.12430194406161754);

...


AnyFolder PCA_drivers = {
  AnyVar Period =  DesignVar(1/1.3959786246912738);

  AnyFolder JointsAndDrivers = {
    AnyKinEqFourierDriver Trunk_PelvisPosX_Pos_0 = {
      Type = CosSin;
      Freq = 1/..Period;
      CType = {Hard};
      Reaction.Type = {Off};
      MeasureOrganizer = {0};
      AnyKinMeasure &m = Main.HumanModel.BodyModel.Main.HumanModel.BodyModel.Interface.Trunk.PelvisPosX;
      AnyVar a0_offset ??= DesignVar(0.0);
      A = {{  8.623540080091557e-10 + a0_offset, 0.00017990165903964024, 0.011945580192769307, -0.00013312081080007037, 0.00041700269239085025, -7.518299551487054e-05,  }};
      B = {{ 0, 0.0004029987590162868, -0.010429170745175874, 0.00024439360060977196, 0.0003970888221702516, 5.258816436360581e-05,  }};
    };
    ...

Example: Complete Human Model

Sillywalk will by default generate 'anyscript' files with antrhopometics and drivers which can be included in other models. But it is also possible to create a complete standalone model.

sillywalk.anybody.write_anyscript(
    predicted_data,
    targetfile="complete_human_model.any",
    create_human_model=True
)

or using a jinja template for complete control:

sillywalk.anybody.write_anyscript(
    predicted_data,
    targetfile="complete_human_model.any",
    template="MyModel.any.jinja",
    create_human_model=True
)

See the template sillywalk uses as example.

PCAPredictor

PCAPredictor selects numeric columns with sufficient variance and fits a PCA model. It can:

  • Predict all columns from partial constraints on PCA columns using a KKT least‑squares system.
  • Convert between primal parameters and principal components.
  • Persist models to .npz files.

Notes

  • Constraints on columns excluded from PCA are not allowed and raise ValueError.
  • If no constraints are provided, predict returns the column means.
  • If no columns pass variance screening, the model has zero components and predict returns means.

Example

import polars as pl
from sillywalk import PCAPredictor

df = pl.DataFrame({
    "a": [1, 2, 3, 4],
    "b": [2, 2.5, 3, 3.5],
    "c": [10, 10, 10, 10],  # excluded (zero variance)
})
model = PCAPredictor(df)
pred = model.predict({"a": 3.2})
pcs = model.parameters_to_components({k: pred[k] for k in model.pca_columns})
back = model.components_to_parameters(pcs)

License

MIT License. See LICENSE for details.

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

sillywalk-1.2.0.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

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

sillywalk-1.2.0-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

File details

Details for the file sillywalk-1.2.0.tar.gz.

File metadata

  • Download URL: sillywalk-1.2.0.tar.gz
  • Upload date:
  • Size: 16.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sillywalk-1.2.0.tar.gz
Algorithm Hash digest
SHA256 2ce7667b4efdf965cdd729f866c6cc3146b9c01bc3c56f58eead117360eb71b9
MD5 8f03d94e8f0ede64812aa1cae1a939f0
BLAKE2b-256 f4ddfaf164ea381e410f228853b01db44e5f037054030e2566a2638ac8495361

See more details on using hashes here.

Provenance

The following attestation bundles were made for sillywalk-1.2.0.tar.gz:

Publisher: build.yml on AnyBody-Research-Group/sillywalk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sillywalk-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: sillywalk-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 18.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sillywalk-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 de014725175be4001e06e0439bbebc4d7a8e66dae56bf5abb3b458163d47f3c6
MD5 ebcb47eb81e3a36fd8d0c58e80effe23
BLAKE2b-256 89743c052a751a9a3231ebf4f709e40230542731eac35c469a9329cf0e8f4bde

See more details on using hashes here.

Provenance

The following attestation bundles were made for sillywalk-1.2.0-py3-none-any.whl:

Publisher: build.yml on AnyBody-Research-Group/sillywalk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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