/MiniQTc

Primary LanguageSwift

MiniQTc

Minimal Swift package version of the QTc library

This QTc Swift package includes a multitude of formulas for QTc and QTp calculation, both common and obscure. It is intended for use in iOS and macOS programs. It is written in Swift but can be used in Objective C projects. This library is free to use in your own apps and programs. Usages include designing simple QTc calculators to medical research involving calculated QTc and QTp intervals. For more background on this project, see the blog post Hacking the QTc.

Installation

Install this library via the Xcode Swift package manager.

Using the library

Formulas

QTc and QTp formulas are labeled based on the proposed standard nomenclature of Rabkin et al.. The QT interval normally shortens with increasing heart rate. QTc formulas try to correct the QT interval for heart rate and assume the QT = QTc at a heart rate of 60. Ideally in an individual subject, the QTc will not change at different heart rates. QTp formulas predict the "normal" QT based on heart rate. Don't confuse the QTp with the same term QTp used in some recent studies to indicate a corrected QT interval measured to the the peak, rather than the end of the T wave. Over the years many QTc and QTp formulas have been developed. The most commonly used is still Bazett's 1920 QTc formula, despite its flaws.

Use the enum Formula to select QTc or QTp formulas:

public enum Formula {
	// QTc formulas
	case qtcBzt  // Bazett
	case qtcFrd  // Fridericia
	case qtcFrm  // Framingham
	case qtcHdg  // Hodges

Calculators

The easiest way to get a calculator for a specific formula is to generate one using this static factory class:

let calculator = QTc.calculator(formula: .qtcBzt) // generates a Bazett QTc calculator (Swift)

Calculator calculator = [QTc calculatorWithFormula: Formula.qtcBzt]; // Qbjective C

Most of the following examples are given as Swift code. See Apple's reference for more information on calling Swift functions from Objective C.

Here is a QTp calculator:

let calculator = QTc.calculator(formula: .qtpFrd) // Friedericia QTp calculator

QTc and QTp calculators have types of QTcCalculator and QTpCalculator and are subclasses of the base class Calculator. Using a calculator generated in this way to calculate a QTc or QTp requires passing a QtMeasurement struct to the calculator.

QtMeasurement

The QtMeasurement struct is a convenient way to package the measurements required for a QTc or QTp calculation. It is defined as:

public struct QtMeasurement {
	public let qt: Double? // an optional, since QT not needed for QTp
	public let intervalRate: Double  // RR interval or HR
	public let units: Units // may be .msec or .sec
	public let intervalRateType: IntervalRateType // may be .bpm or .interval
	public let sex: Sex = .unspecified // .male or .female, not required by many formulas
	public let age: Int? = nil  // may be nil as not always needed
}

Units can be .msec or .sec, and IntervalRateType either .bpm or .interval (meaning the heart rate is given as beats per minute or an RR interval). Sex is .male, .female, or .unspecified (not all formulas require sex or age). A complete example for calculating a QTc interval using the Bazett formula is as follows.

let qtMeasurement = QtMeasurement(qt: 367.0, intervalRate: 777.0, units: .msec, intervalRateType: .interval)
let qtcBztCalculator = QTc.calculator(formula: .qtcBzt)
let qtc = qtcBztCalculator.calculate(qtMeasurement) // qtc = 416.34711041

Note that if the QtMeasurement units are msec, the calculator returns a result in msec; if the units are secs, the result is in secs.

More ways to calculate (aka the less easy way)

If you are less interested in a universal calculator object that handles QTc and QTp calculations the same way, you can instantiate specific QTc and QTp calculator classes. These classes don't require use of the QtMeasurement struct. You can pass to their calculate methods parameters in secs or msecs directly.

For example:

let qtcBztCalculator = QTc.qtcCalculator(formula: .qtcBzt) // Swift

QTcCalculator qtcBztCalculator = [QTc qtcCalculatorWithFormula: Formula.qtcBzt]; // Objective C

Then use the calculator to calculate the QTc:

let qtcBzt = qtcBztCalculator.calculate(qtInSec: 0.334, rrInSec: 0.785) // Swift

double qtcBzt = [qtcBztCalculator calculateWithQtInSec: 0.334 rrInSec: 0.785]; // Objective C

Calculate functions

When using the QTcCalculator or QTpCalculator classes, each calculate function has 4 different signatures, using QT in sec or msec, RR in sec or msec or heart rate in beats per minute. Functions using msec parameters return QTc in msec, while those using sec parameters return QTc in seconds. All interval/rate parameters are Double in Swift, double in Objective C. For example:

let qtcInMsec = qtcBztCalculator.calculate(qtInMsec: 402, rate 72) // returns QTc in msec
let qtcInSec = qtcBztCalculator.calculate(qtInSec: 0.402, rate 72) // returns QTc in sec

Other calculator variables and functions

You can get other information from the calculator instance, for example:

let qtcCalculator = QTc.qtcCalculator(formula: .qtcBzt)
let longName = qtcCalculator.longName // "Bazett"
let shorName = qtcCalculator.shortName // "QTcBZT"
let reference = qtcCalculator.reference // literature reference in AMA style
let notes = qtcCalculator.notes // facts about this formula
let classification = qtcCalculator.classification // .power
// this is the type of mathematical equation: .power, .linear, .exponential, etc.
let date = qtcCalculator.publicationDate // year of publication
let numberOfSubjects = qtcCalculator.numberOfSubjects // number of subjects studied

Normal values

Just as there are many formulas to correct or predict QT intervals, there are numerous proposals aimed at defining normal QTc intervals. The QTc library contains a number of these abnormal QTc definitions, along with supporting literature references.

The enum Criterion in AbnormalQTc.swift lists previously defined QTc criteria.

public enum Criterion: String {
	case schwartz1985
	case schwartz1993
	case fda2005
	case esc2005
	// etc.

Retrieve a test suite from AbnormalQTc and use it to test if a QTc is normal or not.

// returns a QTcTestSuite? struct
if let testSuite = AbnormalQTc.qtcTestSuite(criterion: .schwartz1985)
	let m = QTcMeasurement(qtc: 455, units: .msec, .sex: .male)
	let severity = testSuite.severity(measurement: m) // == .abnormal
}

Notice the struct QTcMeasurement which is used to pass the QTc along with other parameters, including units, sex, and age (the latter two are optional parameters). The results are given as one of the constants of the struct Severity. These constants are .undefined, .normal, .borderline, .abnormal, .mild, .moderate, .severe, and .error. The method Severity.isAbnormal() -> Bool returns true if a result is .abnormal, .mild, .moderate, .severe, or .error. The three constants .mild, .moderate, and .severe are used in the FDA criterion (.fda2005) for prolonged QTc. See the source code in AbnormalQTc.swift for more information.

Errors

Mathematical errors

Some QTc formulas have the potential for division by zero or performing fractional power operations on negative numbers. Parameters are not checked for these problematic inputs. Division by zero (generally if the RR interval is zero) will result in the value Double.infinity, and zero divided by itself (generally if the QT and RR are both zero) or a fractional root of a negative number (if the RR is negative) will result in Double.nan. Thus if input parameters are not checked for sanity, it is necessary to check results as follows:

let qtc = QTc.qtcCalculator(formula: .qtcBzt).calculate(qtInMsec: qt, rrInMsec: rr)
if qtc == Double.infinity || qtc.isNaN {
	Error("Division by zero or root of negative number!")
	return
} else {
	// carry on
}

Of course your other option is never to send these bad parameters to the formulas:

if qt <= 0 || rr <= 0 {
	Error("QT and RR can't be less than or equal to zero!")
	return
} else {
	let qtc = QTc.qtcCalculator(formula: .qtcBzt).calculate(qtInMsec: qt, rrInMsec: rr)
}

Exceptions

Calculate methods of calculators can throw exceptions in certain situations. For example, if you have a qt of nil in your QtMeasurement struct and pass this to a calculate method of a QTc calculator, a CalculationError.qtMissing exception will be thrown. Similarly if a calculator from a formula requires that a sex parameter is provided and it isn't, a CalculationError.sexRequired exception will be thrown. See the CalculationError enum in QTc.swift for a complete list of possible exceptions. Make sure you code includes exception handling for the calculate functions. For example,

guard let qtc = try? calculator.calculate(qtInMsec: qt, rrInMsec: rr) else {
	assertionFailure("Calculate threw an exception.")
}
print(String(format: "QTc = %.f", qtc))

Conversion functions

The QTc library includes static functions to do common conversions, between seconds, milliseconds and heart rate, e.g.:

let intervalInSec = 0.890
let intervalInMsec = QTc.secToMsec(intervalInSec) // = 890
let rate = QTc.msecToBpm(intervalInMsec) // = 67.41573

These functions don't throw, but as with the calculate functions, division by zero will result in Double.infinity.

Tests

The QTc library includes numerous unit tests to confirm accuracy.

Contributing

If you know of QTc or QTp formulas which are omitted here and should be included, please email me at mannd@epstudiossoftware.com or contact me on Twitter (@manndmd).

Demo program

EP QTc is a demo program that is available for download on the Apple App Store. With it, you can calculate all the QTc and QTp formulas at once, see graphs of intervals, determine statistics on the formulas, investigate each formula individually, and just generally have a bunch of good clean EP QT fun.

References

See the file Formulas.swift for an updated list of references.

License

This MiniQTc library is open source, and licensed under the Apache License, Version 2.0. When used with Apple devices via the iTunes App Store, it is subject to the standard Apple iOS license agreement.

Copyright

Copyright © 2017-2022 EP Studios, Inc.

Acknowledgments

Thanks to Marian Stiehler for help in acquiring the original literature that forms the basis of these QTc and QTp formulas.

Thanks to Dr. Simon Rabkin at the University of British Columbia for corresponding with me regarding the QT interval, and for his and his team's fine work on the nomenclature and categorization of the numerous QT formulas which formed a basis and inspiration for this app. I also thank the multitude of investigators who over the years have attacked the problematic QT interval, using math in an attempt to flatten nature's heart rate versus repolarization curve.

The universal library template was created based on this helpful Medium post by Atai Barkai.

Author

David Mann, MD

Email: mannd@epstudiossoftware.com Website: https://www.epstudiossoftware.com