Cycle-by-cycle respiratory metrics from ADInstruments LabChart text exports.
resp_metrics builds on
labchart_parser.
Set time display to "Start from Block" before exporting, and make sure "Block header" is ticked.
Respiratory cycles are read from user-placed INSPI and EXPI comments. No
automatic breath detection is performed; this is deliberate so signals are
visually inspected before analysis.
pip install git+https://github.com/Neures-1158/resp_metrics.gitFor development: pip install -e ".[dev]" (adds pytest, ruff, build, and twine).
from resp_metrics import compute_from_labchart
result = compute_from_labchart(
"examples/data/labchart_file_vs.example.txt",
block=1,
flow_col="Flow",
flow_unit="L/s",
volume_col=None,
pressure_col="Pressure",
mechanically_ventilated=False,
)
result["cycles"].head()
result["ventilatory"].head()Lower-level functions are available for custom pipelines:
from labchart_parser import LabChartFile
from resp_metrics import cycles_from_comments, ventilatory_from_cycles
lc = LabChartFile.from_file("data/recording.txt")
cycles = cycles_from_comments(lc.comments, block=1)
metrics = ventilatory_from_cycles(
lc.get_block_df(1),
cycles,
flow_col="Flow",
flow_unit="L/s",
)See examples/example_notebook.ipynb.
- Spontaneous breathing: inspiration is negative flow.
- Mechanical ventilation: inspiration is positive flow.
- Standard outputs:
BF,VT,VT_Ti,VE,Ti,Te,Ttot,Ti_Ttot,IE,PIF,PEF,PTP,dPmoandPmo_mean. - Mechanical ventilation also returns
PEEP,Ppeak,Pplat,dP,Cstat,R, andMAPwhen signals support them.
Pass pes_col (and optionally pga_col, pdi_col, pdi_max) to add
per-cycle indices of inspiratory effort. They are merged into the
ventilatory table and also returned as a standalone effort table.
| Metric | Definition | Unit |
|---|---|---|
VT_Ti |
VT / Ti, mean inspiratory flow |
L·s⁻¹ |
Ti_Ttot |
Ti / Ttot, inspiratory duty cycle |
— |
Pes_ee |
end-expiratory Pes, absolute (indirect marker of operating lung volume) | cmH2O |
dPes |
Pes_baseline − min(Pes) over inspiration |
cmH2O |
dPga |
max(Pga) − Pga_baseline |
cmH2O |
dPga_corr |
same, referenced to the Pga nadir | cmH2O |
dPdi |
max(Pdi) − Pdi_baseline |
cmH2O |
WOB |
∫ (Pes_baseline − Pes) × (−Flow) dt, work of breathing |
J |
PTPes |
∫ (Pes_baseline − Pes) dt |
cmH2O·s·breath⁻¹ |
PTPga |
∫ (Pga − Pga_baseline) dt |
cmH2O·s·breath⁻¹ |
PTPga_corr |
same, integrated from the Pga nadir | cmH2O·s·breath⁻¹ |
PTPdi |
∫ (Pdi − Pdi_baseline) dt |
cmH2O·s·breath⁻¹ |
PTPdi_PTPes |
PTPdi / PTPes, diaphragmatic share of the effort |
— |
TTIdi |
(mean inspiratory Pdi / Pdi_max) × (Ti / Ttot) |
— |
The baseline of each channel is the median over the 0.2 s preceding
inspiration onset, i.e. the resting end-expiratory level. Pdi is read from
pdi_col when given, otherwise derived as Pga − Pes. Definitions follow the
ATS/ERS Statement on Respiratory Muscle Testing
(Am J Respir Crit Care Med 2002;165:518-624) and the
ERS statement on respiratory muscle testing at rest and during exercise
(Laveneziana et al., Eur Respir J 2019;53:1801214).
from resp_metrics import compute_from_labchart
res = compute_from_labchart(
"examples/data/labchart_file_pressures.example.txt",
block=1,
flow_col="flow",
flow_unit="L/s",
volume_col=None,
pressure_col="Pmo",
pes_col="Pes",
pga_col="Pga",
pdi_col="Pdi",
pdi_max=97.0, # cmH2O, measured during a maximal manoeuvre
)
res["effort"].head()Pplat,Cstat, andRrequire a low-flow inspiratory plateau.- If
Pplatis unavailable,dP = Ppeak - PEEPis a fallback and overestimates true driving pressure. dPmo(swing) andPmo_mean(mean) come frompressure_col. Under inspiratory threshold loading that channel carries the applied load, which is why they sit with the ventilatory pattern rather than the effort indices.dPmois referenced to the pre-inspiratory baseline and is immune to a DC offset;Pmo_meanis absolute, matching the average inspiratory mouth pressure (PM) reported by Bird et al., so comparing it withPTP/Tireveals an offset on the channel.WOBrequires esophageal pressure (pes_col) and flow; airway pressure is not substituted, as it would not represent patient effort.PTPesis not corrected for chest wall elastic recoil, which would require chest wall elastance. It is a practical within-subject index of global inspiratory effort, not an absolute measure.- Gastric metrics are reported twice. When expiratory abdominal muscles are
recruited, Pga is still elevated at the INSPI marker and falls as they relax
at the start of inspiration, so the end-expiratory baseline sits above the
relaxed level and
dPga/PTPgaare underestimated — an artifact ATS/ERS 2002 explicitly flags.dPga_corrandPTPga_corrreference the Pga nadir instead (search windowpga_nadir_frac, default the first third of inspiration) and match the uncorrected values when no such recruitment is present.PesandPdiare left uncorrected: their swings are an order of magnitude larger so the same offset is negligible, andPdiis derived fromPgaandPesand cannot take an independent reference. Pes_eeis reported as an absolute value, so unlike every other effort column it carries any DC offset of the channel. ERS 2019 uses end-expiratory Poes to reveal intrinsic PEEP when hyperinflation is suspected, but it is a surrogate only — the reference method for end-expiratory lung volume is the inspiratory capacity manoeuvre. Read changes across conditions rather than a single absolute level.TTIdineedsPdi_maxfrom a maximal manoeuvre; it cannot be derived from tidal breathing and staysNaNwhen not supplied. The diaphragm fatigue threshold is aTTIdiof 0.15–0.18 (ATS/ERS).- The final cycle in each block is excluded because the next inspiration onset is unknown.
pytestMaintained under NEURES. Lead: Damien Bachasson, PhD (GitHub | ORCID | Lab). Issues and PRs welcome.
MIT licensed. See LICENSE.

