Skip to main content

mpspline

Python implementation of mass-preserving equal-area quadratic splines for harmonizing soil profiles to standard depth intervals.

Port of the mpspline2 R package by Lauren O'Brien, based on the method of Bishop et al. (1999), as generalized in Malone et al. (2009).

Installation

pip install mpspline

Quick Start

from mpspline import mpspline

# Single profile
result = mpspline({
    'horizons': [
        {'hzname': 'A', 'upper': 0, 'lower': 20, 'clay': 24.5},
        {'hzname': 'B', 'upper': 20, 'lower': 50, 'clay': 35.2},
    ]
}, output_type='wide')
print(result)  # {'clay_0_5': 21.4, 'clay_5_15': 27.8, ...}

Usage

Pass a dict for a single profile or a list for multiple profiles.

1. Single Profile (Wide Format)

from mpspline import mpspline

# Single profile (returns dict)
result = mpspline({
    'cokey': 12345,
    'horizons': [
        {'hzname': 'A', 'upper': 0, 'lower': 20, 'clay': 24.5},
        {'hzname': 'B', 'upper': 20, 'lower': 50, 'clay': 35.2},
    ]
}, output_type='wide')
print(result)
# {'cokey': 12345, 'clay_0_5': 22.6, 'clay_5_15': 24.1, 'clay_15_30': 29.9, ...}

2. Multiple Profiles (Long Format - Default)

profiles = [
    {
        'cokey': 1001,
        'horizons': [
            {'hzname': 'A', 'upper': 0, 'lower': 15, 'clay': 18.5},
            {'hzname': 'Bt1', 'upper': 15, 'lower': 45, 'clay': 35.0},
            {'hzname': 'Bt2', 'upper': 45, 'lower': 80, 'clay': 32.5},
        ]
    },
    {
        'cokey': 1002,
        'horizons': [
            {'hzname': 'Ap', 'upper': 0, 'lower': 20, 'clay': 15.2},
            {'hzname': 'Bw', 'upper': 20, 'lower': 50, 'clay': 28.4},
            {'hzname': 'C', 'upper': 50, 'lower': 100, 'clay': 25.0},
        ]
    },
]

# Returns DataFrame with columns: [cokey, var_name, upper, lower, value]
df_long = mpspline(profiles, var_name=['clay'])
print(df_long[['cokey', 'var_name', 'upper', 'lower', 'value']].head())

Output:

 cokey var_name  upper  lower      value
  1001     clay      0      5  16.168706
  1001     clay      5     15  19.762784
  1001     clay     15     30  31.118765
  1001     clay     30     60  36.090372
  1001     clay     60    100  31.251860

3. Multiple Profiles (Wide Format)

# Returns DataFrame with flattened columns: [cokey, clay_0_5, clay_5_15, ...]
df_wide = mpspline(profiles, var_name=['clay'], output_type='wide')
print(df_wide[['cokey', 'clay_0_5', 'clay_5_15', 'clay_15_30']].head())

Output:

 cokey  clay_0_5  clay_5_15  clay_15_30
  1001 16.168706  19.762784   31.118765
  1002 12.784555  14.721798   22.437913

4. Advanced Modes (1cm Resolution)

# Get high-resolution predictions at every 1cm interval
df_1cm = mpspline(profiles, var_name=['clay'], mode='1cm')
print(df_1cm[['cokey', 'var_name', 'depth', 'value']].head())

Output:

 cokey var_name  depth      value
  1001     clay      0  15.935577
  1001     clay      1  15.974431
  1001     clay      2  16.090996
  1001     clay      3  16.285271
  1001     clay      4  16.557255

Parameters

Parameter Type Default Description
obj dict or list Required Profile(s) with a 'horizons' key
var_name str or list None Properties to harmonize (all numeric if None)
target_depths list GlobalSoilMap Depth intervals as (top, bottom). Default: [(0,5), (5,15), (15,30), (30,60), (60,100), (100,200)]
lam float 0.1 Smoothing parameter (0.01=smooth, 1.0=fit data closely)
vlow float 0.0 Minimum constraint for values
vhigh float 1000.0 Maximum constraint for values
parallel bool False Use multiprocessing for multiple profiles
batch_size int 100 Profiles per batch
strict bool False Raise exceptions on validation errors (False = skip)

Horizon Input Format

Required fields:

  • Depth: upper and lower (or top and bottom) in cm
  • Properties: numeric fields to harmonize

Optional:

  • hzname: horizon name

Example:

{
    'hzname': 'Ap',
    'upper': 0,
    'lower': 20,
    'clay': 24.5,
    'sand': 42.3,
}

Examples

# Custom depth intervals
result = mpspline(profile, target_depths=[(0, 10), (10, 30), (30, 60)])

# Adjust smoothing (0.01=smooth, 1.0=closer to data)
result = mpspline(profile, lam=0.01)

# Constrain values to realistic ranges
result = mpspline(profile, var_name=['clay'], vlow=0, vhigh=100)

# Parallel processing for many profiles
df = mpspline(profiles, parallel=True, batch_size=500)

# Only process specific properties
df = mpspline(profiles, var_name=['clay', 'sand'])

# Error handling: skip bad profiles or raise errors
df = mpspline(profiles, strict=False)  # Skip problematic profiles
try:
    df = mpspline(profiles, strict=True)  # Raise on errors
except ValueError as e:
    print(f"Validation error: {e}")

Troubleshooting

Horizons not ordered by depth: Horizons must be sequential with no gaps or overlaps.

# Wrong
{'upper': 0, 'lower': 30}, {'upper': 20, 'lower': 50}

# Right
{'upper': 0, 'lower': 20}, {'upper': 20, 'lower': 50}

Values outside expected ranges: Use vlow and vhigh to constrain predictions:

result = mpspline(profile, vlow=0, vhigh=100)

Processing is slow: Enable parallel processing:

df = mpspline(profiles, parallel=True, batch_size=500)

How it works

The algorithm fits a mass-preserving equal-area quadratic spline to horizon data. This converts discrete observations at varying depths to standard depth intervals while preserving the average value across each depth range.

See the algorithm documentation for details.

References

License

GPL 3.0

Release files for mpspline 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mpspline 0.2.0
File Size Uploaded
mpspline-0.2.0.tar.gz 39.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mpspline 0.2.0
File Interpreter ABI Platform
mpspline-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 73.2 kB

Release files / mpspline-0.2.0.tar.gz

Download URL mpspline-0.2.0.tar.gz
Size 39.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7aae9b8199be8b052619c7c9e055375eb8af4af914ebf9b6cd08663cd2db9a12
BLAKE2b-256 checksum
How to use checksums
81a3ec5e3705eb0146bfcb775edaaf58bcdfaeba241fd91ef709ed1181a7fa5e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 14, 2026.

Transparency log

Release files / mpspline-0.2.0-py3-none-any.whl

Download URL mpspline-0.2.0-py3-none-any.whl
Size 34.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
371f450c346a57210272daf03e95e25eccf251c2adce66a4bc959e56005a0966
BLAKE2b-256 checksum
How to use checksums
29baac4d0f987c5037d09d7bbb7a78882b9c2fae05f2d468ef79be87ad758fb2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page