Introduction
This example uses a dataset derived from the PhysioNet QT Database — a 2,299-point excerpt of the sele0606 ECG Holter recording (the same segment used throughout the GrammarViz tutorials, available as ecg0606_1.csv).
The SAX-based motif and discord machinery lives in the jmotif-sax library (net.seninp:jmotif-sax:2.0.1 on Maven Central, and the SAX layer underneath SAX-VSM). Motifs are found with the EMMA algorithm, discords with HOT-SAX:
import net.seninp.jmotif.sax.NumerosityReductionStrategy;
import net.seninp.jmotif.sax.motif.EMMAImplementation;
import net.seninp.jmotif.sax.motif.MotifRecord;
import net.seninp.jmotif.sax.discord.DiscordRecord;
import net.seninp.jmotif.sax.discord.DiscordRecords;
import net.seninp.jmotif.sax.discord.HOTSAXImplementation;
double[] series = /* read the CSV into an array */;
// the most frequent ~heartbeat-length pattern (motif length 100,
// similarity range 0.5, PAA 6, alphabet 4, z-norm threshold 0.01)
MotifRecord motif = EMMAImplementation.series2EMMAMotifs(series, 100, 0.5, 6, 4, 0.01);
System.out.println("motif at " + motif.getLocation() + ", seen " + motif.getFrequency()
+ " times: " + motif.getOccurrences());
// the two most unusual subsequences (window 100, PAA 3, alphabet 3)
DiscordRecords discords = HOTSAXImplementation.series2Discords(series, 2, 100, 3, 3,
NumerosityReductionStrategy.NONE, 0.01);
for (DiscordRecord d : discords) {
System.out.println("discord at " + d.getPosition() + ", NN distance " + d.getNNDistance());
}
Applied to the heartbeat segment, this prints:
motif at 1690, seen 5 times: [807, 951, 1095, 1837, 2124]
discord at 430, NN distance 5.279080006170648
discord at 318, NN distance 4.175756357304875
The motif occurrences are ordinary heartbeats — the recurring shape of the recording. The strongest discord at position 430 marks the most unusual subsequence: the anomalous third heartbeat, first identified in the HOT SAX paper.
Note that EMMA reports each occurrence once per matching subsequence; with a sliding window, neighboring offsets can describe the same underlying pattern, so tight clusters of offsets are usually collapsed to their first member.
For variable-length motif and discord discovery — where pattern lengths are not fixed in advance — see the grammar-inference approach in GrammarViz, the sibling project of SAX-VSM.
Historical note: this walkthrough originally targeted the retired jMotif Google Code project and its
SAXFactory.seriesToDiscordsAndMotifsAPI; the example above uses the current jmotif-sax API and was verified against version 2.0.1.