Statistically rigorous comparison of two protein conformational ensembles — networks, dynamics and kinetics — with proper multiple-testing correction.
Comparing two ensembles — wild-type against a mutant, apo against holo, one design against another — is something almost every MD study does and almost everyone hand-rolls in a notebook: subtract two averages, eyeball the difference, and rarely correct for having just run hundreds of per-residue comparisons at once. confdelta makes that comparison a first-class, tested operation: per residue it reports an effect size with a confidence interval and a p-value corrected across all residues, and it is honest about when a design is too small to conclude anything.
Single-trajectory analysis (residue interaction networks, DCCM, PCA, free-energy landscapes, Markov state models) is here too, but as the layer the comparison is built on — the comparison is the point.
pip install confdeltaPython 3.10–3.13, no conda required. Markov state models need one extra: pip install "confdelta[msm]".
confdelta compare \
-a wild_type.pdb wild_type.xtc --a-name wild_type \
-b mutant.pdb mutant.xtc --b-name mutant \
-o results/The run writes results/07_comprehensive_report/per_residue_statistics.csv — one row per residue, effect size and confidence interval first, corrected q-value after — and prints the largest effects. The output has this shape (the layout is real; the residues and numbers below are placeholders, not results from any particular system):
residue g ci_low ci_high q sig
A_82 +2.41 +1.36 +3.46 0.0108 *
A_84 +1.62 +0.93 +2.31 0.0108 *
A_50 -1.54 -2.06 -1.02 0.0144 *
A_25 -0.97 -2.16 +0.23 0.2684
g is Hedges' g (effect size); sig marks residues significant after Benjamini–Hochberg
correction across every residue tested. The DCCM difference heatmap and the descriptive
network/allosteric tables are written alongside.
If you supply replicate ensembles per condition, the replicate is the unit of inference. If you supply a single run per condition, a block bootstrap over frames supplies the uncertainty and the output says plainly that one run cannot separate the condition effect from run-to-run variation. And if the design cannot reach significance at all — three replicates per condition have a p-value floor of 0.10 — confdelta tells you that and points you to the effect sizes, instead of reporting a misleading "nothing significant".
The compare command takes one run per condition, so it uses block-bootstrap (single-run)
inference. For replicate-level inference, build EnsembleGroups from several runs and
use the Python API shown below.
# Analyse a single ensemble on its own
confdelta single -t system.pdb -x trajectory.xtc -n my_run -o results/
# Generate a config file with every option, then run with it
confdelta example-config -o study.json
confdelta compare -a wt.pdb wt.xtc -b mut.pdb mut.xtc --config study.jsonEvery option not shown on the command line lives in the config file; the full reference is in docs/CONFIGURATION.md, generated from the code and checked against it by the test suite.
The comparison is designed to be imported. An ensemble is built from any source — a trajectory, a multi-model PDB, a set of predicted structures, or an in-memory coordinate array — and the comparison code does not care which:
from confdelta import Ensemble, EnsembleGroup, compare_ensemble_groups
wt = EnsembleGroup([Ensemble.from_trajectory("wt.pdb", f"wt_{i}.xtc") for i in range(3)], label="wild_type")
mut = EnsembleGroup([Ensemble.from_trajectory("v82a.pdb", f"v82a_{i}.xtc") for i in range(3)], label="V82A")
report = compare_ensemble_groups(wt, mut, correction="fdr_bh", alpha=0.05)
for f in report.significant_features():
print(f"{f.feature}: g={f.effect_size:+.2f} "
f"[{f.effect_ci.low:+.2f}, {f.effect_ci.high:+.2f}] q={f.qvalue:.3g}")report is typed (ComparisonReport / FeatureComparison): report.underpowered,
report.mode ("replicate" or "bootstrap"), report.ranked_by_effect(), and the
per-residue effect sizes, confidence intervals and q-values are all attributes, not files.
Other sources: Ensemble.from_structures([...]), .from_pdb_models(path),
.from_coordinates(array, topology).
- Compare two ensembles, with statistics — per-residue effect sizes, confidence intervals and FDR/Bonferroni-corrected q-values; replicate-level or block-bootstrap inference; honest reporting of underpowered designs. This is the core.
- Descriptive difference views — DCCM difference matrices, centrality and modularity deltas, energy-surface differences, allosteric pathway and hotspot changes. Reported without inference, alongside the statistics.
- Single-ensemble analysis — residue interaction networks and community detection,
dynamic cross-correlation, PCA and free-energy landscapes, and (with the
msmextra) Markov state models. The substrate the comparison is built on.
confdelta is 0.2.0 and pre-1.0: the statistical comparison returns typed objects, but the
descriptive comparators still return the loosely structured objects inherited from its
predecessor. Features so far: per-residue contact number, RMSF and RMSD to a
reference; user-defined geometric collective variables (distances, angles and
dihedrals, including between residue groups); and a custom-observable escape hatch for
any per-frame quantity. Expect the API to firm up before 1.0. It continues MD-Compare;
see CHANGELOG.md for the lineage and AUDIT.md for an account of
what was rebuilt and why.
- docs/INSTALL.md — installation and extras
- docs/CONFIGURATION.md — every configuration option
- docs/geometric_cvs.md — custom distances, angles and dihedrals
- docs/custom_features.md — RMSD-to-reference and bring-your-own features
- docs/examples/README.md — worked examples
MIT — see LICENSE.