Skip to content

Heartbeats

264 heartbeats from a twenty-hour ECG, each 140 samples long, sorted into five types by a cardiologist: normal beats and four ways a beat goes wrong.

Projected with DruidJS: the 140 samples are taken as they are — already z-normalised and R-peak aligned, so plain euclidean distance beat DTW when the two were compared — then new druid.TSNE(Z, { seed: 1, perplexity: 20, epsilon: 5 }). pnpm ecg regenerates it, and --sweep re-runs the search that chose those parameters.

264 glyphs · cell 0.3 px → glyph 0.10 · 52 overlapping pairs

Reading a beat

140 samples of one heartbeat, left to right. Nothing is encoded — this is the signal the projection was computed from, drawn small. Every trace shares one vertical scale, so heights are comparable between glyphs.

Beat types

  • Normal 60 beats
  • R-on-T PVC 60 beats
  • PVC 60 beats
  • Supraventricular 60 beats
  • Unclassifiable 24 beats

The beat nearest each type's average shape. A normal beat has a narrow spike; the ventricular ones are broader and displaced, which is visible in the glyph before you know any of the labels.

Every other glyph in this section is a decision. A flower maps four measurements onto petals and sepals; a star puts each column on a spoke; a stacked column turns percentages into bands. Each of those is a choice about how numbers should look, and each can be argued with.

Here the item already is a picture. A heartbeat is a curve, so the glyph is the curve, drawn small. Nothing is mapped, and there is nothing to explain in a legend beyond the axes. That makes this the cleanest version of the argument in the whole section: the projection is computed from these traces, and what the plot shows you is the traces.

You can see the classes before reading any label — a normal beat is a narrow spike, the ventricular ones are broader and displaced. All the glyphs share one vertical scale, so a tall beat really is a tall beat rather than one that was stretched to fit.

A trace is wide and flat, not round, so this is a case where a square lattice is the better host: a rectangular glyph fills a rectangular cell, where a hexagon would waste its corners. That is the opposite of the flower glyphs, which are round and do better on Gosper’s hexagons — the geometry is on the Gosper page.

The dataset is heavily skewed — 2,919 normal beats against 24 unclassifiable ones. Taking sixty of each type where sixty exist, and all of them where they do not, gives 60/60/60/60/24. That keeps the rare types visible instead of drowning them, at the cost of no longer showing the real prevalence.

Time series get reflexively handed to dynamic time warping, on the reasoning that two beats should count as similar even if one runs slightly early. That reasoning does not apply here: these beats arrive already normalised and aligned on the R peak, so there is no drift left for warping to absorb.

Measured rather than assumed — how often a beat’s ten nearest neighbours share its type:

distanceagreement
euclidean57.0%
dynamic time warping52.9%

Warping makes it worse, and for a good reason: the broad QRS complex of a ventricular beat is a genuine difference in shape, and warping is designed to forgive exactly that kind of difference. Given aligned inputs it removes signal rather than noise. So the projection uses plain euclidean distance on the raw traces.

Yanping Chen, Eamonn Keogh, Bing Hu, Nurjahan Begum, Anthony Bagnall, Abdullah Mueen and Gustavo Batista The UCR Time Series Classification Archive 2015.

ECG5000, derived from a 20-hour ECG in PhysioNet's BIDMC Congestive Heart Failure database. The five original classes survive here; the copies on OpenML are binarised.