bindcurve is an lmfit-backed Python package for fitting dose-response curves.
The current API provides:
- dose-response data ingestion and validation through
DoseResponseData - a logistic
IC50Model - direct-binding models for simple, specific, and total Kd fitting
- competitive three-state and four-state binding models
- IC50-to-Kd conversion helpers
- structured fit results and plotting helpers for reports and figures
uv add bindcurveFor local development:
uv python install 3.14
uv sync --group dev
uv run pytestbindcurve supports Python 3.10, 3.11, 3.12, 3.13, and 3.14. Python 3.9 is not supported.
bindcurve expects dose-response observations in long-form data:
compound_id | experiment_id | concentration | replicate_id | response
Only these columns are required:
compound_id | concentration | response
If experiment_id or replicate_id is missing, bindcurve fills defaults.
import pandas as pd
import bindcurve as bc
raw = pd.DataFrame(
{
"compound_id": ["cmpd_a", "cmpd_a", "cmpd_a", "cmpd_a"],
"experiment_id": ["exp1", "exp1", "exp1", "exp1"],
"concentration": [0.01, 0.1, 1.0, 10.0],
"response": [98.0, 85.0, 45.0, 5.0],
}
)
data = bc.DoseResponseData.from_dataframe(
raw,
)
results = bc.fit(
data,
model="ic50",
fixed={"ymin": 0.0, "ymax": 100.0},
)
print(results.fits())
print(results.summary())
print(results.report(unit="uM"))bindcurve uses one canonical fitting pipeline.
For each compound, it:
- splits observations by independent experiment;
- averages technical replicates within each experiment at each concentration using the arithmetic mean;
- fits one curve per independent experiment;
- summarizes fitted parameters across independent experiments.
This avoids treating technical replicates as independent biological repeats.
At the high-level plotting layer, markers and fitted curves are coupled into one logical series by default: they share one legend label and one base color. plot_fits() can optionally draw covariance-based pointwise confidence bands around each experiment-level fitted mean curve. plot_compounds() draws grand-mean observations across experiments plus a separate plotting-only master fit, uses SD/SEM error bars instead of confidence bands to show inter-experiment uncertainty, and leaves asymptotes / curve points to their dedicated plotting helpers.
Wide assay tables can be normalized with from_dataframe(..., format="wide"):
data = bc.DoseResponseData.from_dataframe(
df,
format="wide",
compound_col="compound",
concentration_col="dose",
experiment_col="experiment",
replicate_cols=["rep_1", "rep_2", "rep_3"],
)DoseResponseData can also be serialized back to tabular or JSON representations:
long_df = data.to_dataframe()
wide_df = data.to_dataframe(format="wide")
data.to_csv("dose_response_long.csv")
data.to_csv("dose_response_wide.csv", format="wide")
json_text = data.to_json(format="wide", indent=2)
reloaded = bc.DoseResponseData.from_json(json_text)For a quick compound-level overview of the input dataset:
summary = data.summary()
print(summary[["compound_id", "N_exp", "N_conc_total"]])For simple compound-level data manipulation:
subset = data.keep_only(["cmpd_a", 2])
trimmed = data.remove("cmpd_b")
merged = bc.DoseResponseData.concatenate(data_1, data_2)You can also evaluate any registered model directly without fitting data:
import numpy as np
import bindcurve as bc
model = bc.get_model("comp_3st_specific")
grid = np.logspace(-4, 1, 200)
evaluation = model.evaluate_components(
grid,
ymin=0.0,
ymax=1.0,
RT=0.05,
LsT=0.005,
Kds=0.02,
Kd=1.6,
)
response = evaluation.response
free_receptor = evaluation.components["R_free"]
tracer_bound = evaluation.components["RLstar"]predict(...) returns only the observable response, while
evaluate_components(...) returns a ModelEvaluation object with the response
plus any model-specific component arrays.
bindcurve is unitless.
The user is responsible for providing all concentration-like values on a consistent numerical scale. Fitted values such as IC50 and Kd are returned on that same scale.
Common development commands:
uv sync --group dev
uv run pytest
uv run ruff check
uv buildThe tutorial notebooks use the same dev environment, which includes ipykernel
and Jupyter support.
See architecture.md for the intended design.
bindcurve has a DOI available from Zenodo. Please use the DOI or cite this repository directly.
bindcurve is published under the MIT license.