Skip to main content

BayesCurveFit: Enhancing Biological Data Analysis with Robust Curve Fitting and FDR Detection

Project description

BayesCurveFit: Enhancing Curve Fitting in Drug Discovery Data Using Bayesian Inference

PyPI version License Python Versions Build Status Publish Status

Overview

BayesCurveFit is a Python package designed to apply Bayesian inference for curve fitting, especially tailored for undersampled and outlier-contaminated data. It supports advanced model fitting and uncertainty estimation for biological data, such as dose-response curves in drug discovery.

Manuscript and Datasets

A preprint can be found at: https://www.biorxiv.org/content/10.1101/2025.02.23.639769v1

Data and supplementary data used in the manuscript can be found at https://zenodo.org/records/14948538

The BayesCurveFit pipeline for processing Kinobeads data is available and can be found in the examples/pipeline directory of the repository.

Technical Details

  • Bayesian Model Averaging: Uses Gaussian Mixture Models with proper law of total variance for covariance estimation
  • MCMC Sampling: Emcee-based sampling with convergence diagnostics (R-hat, effective sample size)
  • Parameter Uncertainty: Full covariance matrix estimation with proper correlation handling
  • Model Comparison: Posterior Error Probability (PEP) for model selection

Installation Guide

Prerequisites

  • Python 3.12 or higher
  • uv package manager (recommended)

Install from PyPI (Recommended)

To install the latest stable release from PyPI:

pip install bayescurvefit

Or with uv (faster):

uv add bayescurvefit

Install from Source

To install the latest development version:

git clone https://github.com/ndu-bioinfo/BayesCurveFit.git
cd BayesCurveFit
uv sync

Development Setup

For development with all dependencies:

git clone https://github.com/ndu-bioinfo/BayesCurveFit.git
cd BayesCurveFit
uv sync --dev
make test  # Run tests

Quick Start

Basic Usage

In this example, we use the log-logistic 4-parameter model to fit dose-response data.

import numpy as np
from bayescurvefit.execution import BayesFitModel

# Define the curve fitting function
def log_logistic_4p(x: np.ndarray, pec50: float, slope: float, front: float, back: float) -> np.ndarray:
    with np.errstate(over="ignore", under="ignore", invalid="ignore"):
        y = (front - back) / (1 + 10 ** (slope * (x + pec50))) + back
        return y

# Input data
x_data = np.array([-9.0, -8.3, -7.6, -6.9, -6.1, -5.4, -4.7, -4.0])
y_data = np.array([1.12, 0.74, 1.03, 1.08, 0.76, 0.61, 0.39, 0.38])
params_range = [(5, 8), (0.01, 10), (0.28, 1.22), (0.28, 1.22)]  # Parameter bounds
param_names = ["pec50", "slope", "front", "back"]

# Run Bayesian curve fitting
run = BayesFitModel(
    x_data=x_data,
    y_data=y_data,
    fit_function=log_logistic_4p,
    params_range=params_range,
    param_names=param_names,
)

Retrieve Results

Get the fitting results with parameter estimates and uncertainties:

# Get results
results = run.get_result()
print(results)

Output:

fit_pec50             5.855744
fit_slope             1.088774
fit_front             1.063355
fit_back              0.376912
std_pec50             0.184996
std_slope             0.399462
std_front             0.061905
std_back              0.050635
est_std               0.089587
null_mean             0.762365
rmse                  0.122646
pep                   0.062768
convergence_warning   False

Key Results:

  • fit_*: Best-fit parameter estimates
  • std_*: Parameter uncertainties (standard deviations)
  • rmse: Root mean square error
  • pep: Posterior Error Probability (model comparison)
  • convergence_warning: MCMC convergence status

Visualization

Plot the fitted curve and parameter distributions:

import matplotlib.pyplot as plt

# Plot fitted curve
fig, ax = plt.subplots()
run.analysis.plot_fitted_curve(ax=ax)
ax.set_xlabel("Concentration [M]")
ax.set_ylabel("Relative Site Response")
plt.show()
Text(0, 0.5, 'Relative Site Response')

png

Parameter Analysis

Visualize parameter correlations and sampling diagnostics:

# Parameter pairwise comparisons
run.analysis.plot_pairwise_comparison(figsize=(10, 10))

# Parameter distributions
run.analysis.plot_param_dist()

png

png

Advanced Usage

Other Curve Types

BayesCurveFit supports various curve types. Here's an example with the Michaelis-Menten model:

def michaelis_menten_func(x, vamx, km):
    return (vamx * x) / (km + x)


x_data = np.array(
    [
        0.5,
        2.0,
        4.0,
        5.0,
        6.0,
        8.0,
        10.0,
    ]
)
y_data = np.array(
    [
        0.3,
        0.6,
        0.8,
        np.nan,
        1.2,
        0.85,
        0.9,
    ]
)

params_range = [(0.01, 5), (0, 5)]
param_names = ["vmax", "km"]

run_mm = BayesFitModel(
    x_data=x_data,
    y_data=y_data,
    fit_function=michaelis_menten_func,
    params_range=params_range,
    param_names=param_names,
)
run_mm.get_result()
fit_vmax               1.075649
fit_km                 1.599924
std_vmax               0.125697
std_km                 0.662758
est_std                0.085374
null_mean              0.77585
rmse                   0.146565
pep                    0.097757
convergence_warning    False
f,ax = plt.subplots()
run_mm.analysis.plot_fitted_curve(ax=ax)
ax.set_xlabel("Substrate Concentration [S] (M)")
ax.set_ylabel("Relative Response (V/V_max)")

png

Parallel Processing

BayesCurveFit supports parallel execution for batch processing and pipeline integration:

from joblib import Parallel, delayed
import pandas as pd
from bayescurvefit.simulation import Simulation
from bayescurvefit.distribution import GaussianMixturePDF
def run_bayescurvefit(df_input, x_data, output_csv, params_range, param_names = None):
    def process_sample(sample):
        y_data = df_input.loc[sample].values
        bayesfit_run = BayesFitModel(
            x_data=x_data,
            y_data=y_data,
            fit_function=michaelis_menten_func,
            params_range=params_range,
            param_names=param_names,
            run_mcmc=True,
        )
        return sample, bayesfit_run.get_result()

    samples = df_input.index.tolist()
    results = Parallel(n_jobs=-1)(
        delayed(process_sample)(sample) for sample in samples
    )
    dict_result = {sample: res for sample, res in results}
    df_output = pd.DataFrame(dict_result).T
    df_output.to_csv(output_csv)
    return df_output
# Generate simulation dataset
sample_size = 6 # Number of observations
params_bounds = [(1, 1.2), [0.5, 1]] # Bounds for fitting parameters in simulation data
pdf_params = [0.1, 0.3, 0.1] # PDF parameters [scale1, scale2, mix_frac of second Gaussian]

x_sample = np.linspace(0.5, 10, sample_size)
sim = Simulation(fit_function=michaelis_menten_func,
                X=x_sample,
                pdf_function=GaussianMixturePDF(pdf_params=pdf_params),
                sim_params_bounds=params_bounds,
                n_samples=10,
                n_replicates=1)
params_range = [(0.01, 5), (0, 5)] #The range of fitting parameters we estimated
param_names = ["vmax", "km"] #optional
df_output = run_bayescurvefit(df_input = sim.df_sim_data_w_error, x_data=sim.X, output_csv="test_output.csv", params_range= params_range, param_names = param_names)
df_output
fit_vmax fit_km std_vmax std_km est_std null_mean rmse pep convergence_warning
m001 1.05 0.53 0.07 0.27 0.09 0.88 0.09 0.08 False
m002 1.07 0.45 0.05 0.13 0.06 0.92 0.07 0.02 False
m003 0.99 0.42 0.03 0.08 0.04 0.85 0.04 0.00 False
m004 1.20 0.83 0.12 0.50 0.11 0.95 0.12 0.15 False
m005 1.11 0.86 0.08 0.30 0.09 0.86 0.09 0.02 False
m006 1.20 1.79 0.19 1.19 0.14 0.90 0.26 0.58 False
m007 1.05 1.49 0.26 1.16 0.20 0.67 0.33 0.92 False
m008 1.59 2.62 0.25 1.14 0.13 0.96 0.17 0.17 False
m009 1.23 1.57 0.09 0.46 0.06 0.85 0.06 0.00 False
m010 1.11 0.41 0.04 0.10 0.05 0.96 0.05 0.00 False

Development

Running Tests

# Install development dependencies
uv sync --dev

# Run all tests
make test

# Run specific test file
uv run pytest src/tests/test_utils.py -v

Building the Package

# Build package
make build

# Install locally
make install

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Make your changes and add tests
  4. Run tests: make test
  5. Commit your changes: git commit -m "Add feature"
  6. Push to the branch: git push origin feature-name
  7. Submit a pull request

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Citation

If you use BayesCurveFit in your research, please cite:

@article{du2025bayescurvefit,
  title={BayesCurveFit: Enhancing Curve Fitting in Drug Discovery Data Using Bayesian Inference},
  author={Du, Niu and others},
  journal={bioRxiv},
  year={2025},
  doi={10.1101/2025.02.23.639769}
}

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

bayescurvefit-0.6.0.tar.gz (261.3 kB view details)

Uploaded Source

Built Distribution

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

bayescurvefit-0.6.0-py3-none-any.whl (28.7 kB view details)

Uploaded Python 3

File details

Details for the file bayescurvefit-0.6.0.tar.gz.

File metadata

  • Download URL: bayescurvefit-0.6.0.tar.gz
  • Upload date:
  • Size: 261.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for bayescurvefit-0.6.0.tar.gz
Algorithm Hash digest
SHA256 be4660e9844a5f9e54a34c23f3aae6eee071f581df1df897bae0744c47b1d8c9
MD5 e98fc4565926228a155caa83ce233572
BLAKE2b-256 101835400f9067ce81f4dbf5aa5d05781c4f6e81d6a3e8cc9aa7eeb1c5f41e5d

See more details on using hashes here.

File details

Details for the file bayescurvefit-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: bayescurvefit-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 28.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for bayescurvefit-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 37ff9e2fba6c34c308ded64fc364e79a37932cda61c96f3ad1ce78db209a07c4
MD5 cef00afe7b7b11c0dbc76aadc4ce2d44
BLAKE2b-256 544c8ea632aff2973075a22153970c27d32ec2abddffc84dea4f99cc424313f4

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