Main Content

Scattering Spectra and Wavelet Phase Harmonics with Phonocardiogram Data

R2026b
Since R2026b

This example shows how to classify human phonocardiogram (PCG) recordings using wavelet scattering spectra and wavelet phase harmonics coupled with a LogitBoost classifier.

Phonocardiograms are acoustic recordings of sounds produced by the systolic and diastolic phases of the heart. Auscultation of the heart continues to play an important diagnostic role in assessing cardiac health. Unfortunately, many areas of the world lack sufficient numbers of medical personnel trained in heart auscultation. Accordingly, it is necessary to develop reliable automated ways of interpreting phonocardiogram data.

This example uses wavelet scattering spectra and phase harmonics as feature extractors for PCG classification. Wavelet scattering spectra and wavelet phase harmonics are a method to capture correlation in the data across time and scales by realigning different scales where correlation would naturally be absent due to separation in frequency. Wavelet phase harmonics are introduced in [4] , while scattering spectra are introduced in [5]. Both scattering spectra and phase harmonics are complex-valued in general. Accordingly, in order to use a number of learning algorithms, some transformation or reshaping of the features is required. See the documentation for additional detail on wavelet scattering spectra and phase harmonics.

In a similar example, the PCG waveforms are classified using the scattering transform, Wavelet Time Scattering Classification of Phonocardiogram Data. Using the wavelet scattering spectra coefficients in place of the full scattering transform results in an approximate 4-fold decrease in the number of features. Utilizing the phase harmonic method results in an approximate 9-fold decrease in the number of coefficients. Neither of these reductions results in any significant loss in performance on the held-out test set. However, there are some advantages and disadvantages to each approach summarized in Comparison with Full Scattering Transform.

Data Description

This example uses phonocardiogram (PCG) data obtained from persons with normal and abnormal cardiac function. The data set consists of 3829 recordings, 2575 from persons with normal cardiac function and 1254 records from persons with abnormal cardiac function. Each recording is 10,000 samples long and is sampled at 2 kHz. This represents five seconds of phonocardiogram data. The data set is constructed from the training and validation data used in the PhysioNet Computing in Cardiology Challenge 2016 [1][3].

Download Data

The first step is to download the data from the GitHub repository. To download the data, click Code and select Download ZIP. Save the file physionet_phonocardiogram-main.zip in a folder where you have write permission. The instructions for this example assume you have downloaded the file to your temporary directory, (tempdir in MATLAB®). Modify the subsequent instructions for unzipping and loading the data if you choose to download the data in folder different from tempdir.

The file physionet_phonocardiogram-main.zip contains

  • PCG_Data.zip

  • README.md

and PCG_Data.zip contains

  • heartSoundData.mat

  • extrafiles.mat

  • Modified_physionet_data.txt

  • License.txt.

heartSoundData.mat holds the data and class labels used in this example. The .txt file, Modified_physionet_data.txt, is required by PhysioNet's copying policy and provides the source attributions for the data as well as a description of how each signal in heartSoundData.mat corresponds to a file in the original PhysioNet data. extrafiles.mat also contains source file attributions and is explained in the Modified_physionet_data.txt file. The only file required to run the example is heartSoundData.mat.

Load Data

If you followed the download instructions in the previous section, enter the following commands to unzip the two archive files:

if exist(fullfile(tempdir,"physionet_phonocardiogram-main.zip"),"file")
    unzip(fullfile(tempdir,"physionet_phonocardiogram-main.zip"),tempdir)
    unzip(fullfile(tempdir,"physionet_phonocardiogram-main","PCG_Data.zip"), ...
        fullfile(tempdir,"PCG_Data"))
end

After you unzip the PCG_Data.zip file, load the data into MATLAB.

load(fullfile(tempdir,"PCG_Data","heartSoundData.mat"))

heartSoundData is a structure array with two fields: Data and Classes. Data is a 10000-by-3829 matrix where each column is an PCG recording. Classes is a 3829-by-1 categorical array of diagnostic labels, one for each column of Data. Because this is a binary classification problem, the classes are "normal" and "abnormal". As previously stated, there are 2575 normal records and 1254 abnormal records. Equivalently, 67.25% of the examples in the data are from persons with normal cardiac function while 32.75% are from persons with abnormal cardiac function. You can verify this by entering:

countlabels(heartSoundData.Classes)
ans = 2×3 table
     Label      Count    Percent
    ________    _____    _______
    normal      2575      67.25 
    abnormal    1254      32.75 

Create Training and Test Sets

Split the data into a training and test set. Allocate 70% of the data for training and the remaining 30% for test.

rng default
idxTrainTest = splitlabels(heartSoundData.Classes,0.7);
trainData = heartSoundData.Data(:,idxTrainTest{1});
testData = heartSoundData.Data(:,idxTrainTest{2});
trainLabels = heartSoundData.Classes(idxTrainTest{1});
testLabels = heartSoundData.Classes(idxTrainTest{2});

You can check the count and percentage of each class in the training and test sets.

countlabels(trainLabels)
ans = 2×3 table
     Label      Count    Percent
    ________    _____    _______
    normal      1802     67.239 
    abnormal     878     32.761 

countlabels(testLabels)
ans = 2×3 table
     Label      Count    Percent
    ________    _____    _______
    normal       773     67.276 
    abnormal     376     32.724 

Note that the training and test sets have been partitioned so that the proportion of "normal" and "abnormal" records in the training and test sets are the same as their proportions in the overall data.

Scattering Spectra

Compute the scattering spectra for the entire training set of 2680 signals. Because these features will be used in training a boosted logistic regression model that only supports real-valued data, use outputmode="realimag", to interleave the real and imaginary parts for complex-valued features. Normalize the input signals by their standard deviations prior to obtaining the scattering spectra.

tsn = waveletScattering(SignalLength=1e4,InvarianceScale=7e3,...
    FilterDownsampling="bandlimited", ...
    QualityFactors=[1 1],OptimizePath=true, ...
    OversamplingFactor=0,boundary="reflection");
[scatspectraTrain,cfsTable] = scatteringSpectra(tsn,trainData, ...
    OutputMode="realimag", ...
    InputNormalization="std");
scatspectraTest=scatteringSpectra(tsn,testData,...
    OutputMode="realimag", ...
    InputNormalization="std"); 

With the given configuration of the scattering network and the scattering spectra computation, each signal yields 674 scattering spectra coefficients. This reduces the number of samples (or features) from the original signal by a factor of approximately 15. Note that in Wavelet Time Scattering Classification of Phonocardiogram Data, the use of the full scattering transform results in 279 scattering paths with 5 coefficients per path. This results in a total of 1395 coefficients per signal. In Using Phase Harmonics, we present a variation of the scattering spectra that further reduces the number of coefficients.

cfsTable is a MATLAB table which provides all the metadata necessary to understand and extract coefficients from the scattering spectra computation.

Training LogitBoost Ensemble

In this example, a LogitBoost ensemble classifier is trained and used for inference.

Create a template decision tree by setting the maximum number of decision splits per tree, MaxNumSplits, to 31. This results in a decision tree with roughly 5 levels. This represents a moderately shallow decision tree, which helps to avoid overfitting. Randomly select 80% of the available features at each split. This is a feature subsampling strategy that introduces diversity among the weak learners and can improve generalization, especially with relatively high-dimensional data. Finally, ensure identical results across runs by setting Reproducible=true.

rng default
T = templateTree(MaxNumSplits=2^5-1, ...
    NumVariablesToSample=round(0.8 * size(scatspectraTrain, 1)), ...
    Reproducible=true);

Specify the LogitBoost model to train 500 sequential weak learners (trees). This example pairs this number of weak learners with a small learn rate to help prevent overfitting. Use a learn rate of 0.05. A value like 0.05 ensures that each tree only moves the ensemble prediction a small amount, which requires more cycles but typically yields better generalization than a larger rate. Sample the features without replacement and set the resampling fraction to 80% of the training data per cycle.

Because the data is highly imbalanced, specify a misclassification cost matrix. The ratio of normal phonocardiograms to abnormal ones is approximately 2:1. In other words, approximately 2/3 of the data are normal phonocardiograms and 1/3 are abnormal. Accordingly, specify the misclassification matrix as (0210) with ClassNames = ["abnormal" "normal"].The given penalty structure deliberately trades normal-class recall for abnormal-class recall. In applications like medical screening, this is usually the right trade-off because it is typically better to have false positives than to miss genuine abnormalities. This does mean however, that recall for the abnormal class will likely be significantly higher than precision. You can always balance this for the cost considerations of your application by changing the values in the Cost matrix.

lboostmdlSS = fitcensemble(scatspectraTrain', trainLabels, ...
    Method="LogitBoost", ...
    NumLearningCycles=500, ...
    Learners=T, ...
    LearnRate=0.05, ...
    ClassNames=["abnormal","normal"], ...
    Cost=[0, 2; 1, 0], ...
    Resample="on", ...
    Replace="off", ...
    Fresample=0.8);

Test LogitBoost Ensemble

Test the boosted logistic regression classifier on the held-out test set.

predTestLabelsScatSpectra = categorical(predict(lboostmdlSS,scatspectraTest'));
testAccuracy = sum(predTestLabelsScatSpectra==testLabels)/numel(testLabels)*100;
fprintf('Test Accuracy is %2.2f percent\n',testAccuracy);
Test Accuracy is 91.12 percent

The test accuracy is approximately 91.1 percent. Note that this is very similar to the test accuracy in Wavelet Time Scattering Classification of Phonocardiogram Data. Plot the confusion chart with precision and recall values.

testCVScatSpectra = confusionchart(testLabels, ...
predTestLabelsScatSpectra, ...
RowSummary="row-normalized",ColumnSummary="column-normalized");
title("Wavelet Scattering Spectra with LogitBoost")

Figure contains an object of type ConfusionMatrixChart. The chart of type ConfusionMatrixChart has title Wavelet Scattering Spectra with LogitBoost.

Compute the F1 scores and display the macro-averaged precision, recall, and F1 scores.

PRTableScatSpectra = helperF1heartSounds(testCVScatSpectra.NormalizedValues);
disp(PRTableScatSpectra)
                     Precision    Recall    F1_Score
                     _________    ______    ________
    Abnormal          81.136      94.947       87.5 
    Normal             97.32      89.263     93.117 
    Macro Average     89.228      92.105     90.309 

Using Phase Harmonics

Next, use the phase harmonic method. Setting method="phaseharmonic", replaces the modulus-modulus coefficients of wavelet scattering spectra with wavelet phase harmonics. This means that there are many common features between method="scatteringspectra" and method="phaseharmonic". See the documentation for details and guidance on which method to choose. Depending on the network configuration, using method="phaseharmonic" can result in a significant reduction in the number of coefficients. In this example, the number of coefficients per signal is reduced from 10,000 to 674 for Method="scatteringspectra" and down to 290 using Method="phaseharmonic". However, there is usually an increase in computation time. Use the same wavelet scattering network and input normalization used in the scattering spectra computation.

[scatPHTrain,phTable] = scatteringSpectra(tsn,trainData,Method="phaseharmonic",...
    OutputMode="realimag", ...
    InputNormalization="std");
scatPHTest = scatteringSpectra(tsn,testData,Method="phaseharmonic", ...
    OutputMode="realimag", ...
    InputNormalization="std");

Use the same LogitBoost ensemble described in Training LogitBoost Ensemble.

lboostmdlPH = fitcensemble(scatPHTrain', trainLabels, ...
    Method="LogitBoost", ...
    NumLearningCycles=500, ...
    Learners=T, ...
    LearnRate=0.05, ...
    ClassNames=["abnormal","normal"], ...
    Cost=[0, 2; 1, 0], ...
    Resample="on", ...
    Replace="off", ...
    Fresample=0.8);

Test LogitBoost Ensemble model with Phase Harmonics

Compute the test features and predict the class of the test data.

predTestLabelsPH = categorical(predict(lboostmdlPH,scatPHTest'));
testAccuracy = sum(predTestLabelsPH==testLabels)/numel(testLabels)*100;
fprintf('Test Accuracy is %2.2f percent\n',testAccuracy);
Test Accuracy is 91.56 percent

Obtain the confusion chart with precision and recall values, compute the F1 scores and display the results.

testCVScatPH = confusionchart(testLabels, ...
predTestLabelsPH, ...
RowSummary="row-normalized",ColumnSummary="column-normalized");
title("Wavelet Scattering Phase Harmonics with LogitBoost")

Figure contains an object of type ConfusionMatrixChart. The chart of type ConfusionMatrixChart has title Wavelet Scattering Phase Harmonics with LogitBoost.

Compute the F1 scores and display the macro-averaged precision, recall, and F1 scores.

PRTablePH = helperF1heartSounds(testCVScatPH.NormalizedValues);
disp(PRTablePH)
                     Precision    Recall    F1_Score
                     _________    ______    ________
    Abnormal          81.922      95.213     88.069 
    Normal            97.472       89.78     93.468 
    Macro Average     89.697      92.496     90.768 

Of the 1149 test records, approximately 91.5% are correctly classified as "Normal" or "Abnormal". Of the 773 normal PCG recordings in the test set, 694 are correctly classified. Of the 376 abnormal recordings in the test set, 358 are correctly classified.

Summary

Both models perform quite well on the held-out test set. The macro-averaged F1 scores are nearly identical. However, the use of phase harmonics has resulted in a reduction in the number of features by a factor of 2.3. Accordingly, you have nearly identical precision, recall, and F1 metrics with less than 1/2 the features.

Both models detect roughly 96% of the actual abnormal cases (high recall), which is exactly what the asymmetric cost matrix was designed to encourage. The tradeoff is lower precision (approximately 81%), meaning roughly 1 in 5 samples flagged as abnormal are actually normal (false positives). Again, this can be adjusted by changing the penalties in the cost matrix.

Of course with the normal class, the pattern is reversed. Precision is very high (97-98%), so when the model designates a waveform as "normal," it is almost always right. But recall is lower (88-89%), meaning about 11% of truly normal cases are misclassified as abnormal. Again, this is the direct consequence of the cost matrix pushing the decision boundary toward flagging more abnormal cases. Due to the stochastic nature of the training, rerunning the example may change the results slightly, but the general outcomes will remain the same.

The macro-averaged F1 scores of 90% plus indicate good balanced performance across the classes, suggesting the ensemble's regularization strategy (slow learning rate, feature/observation subsampling, and shallow trees) is generalizing well.

Scattering Spectra or Phase Harmonics?

The practical takeaway is that both feature sets perform equally well. The differences are well within the range of normal statistical variation. If you ran cross-validation or repeated hold-out splits, you would likely see fluctuations of this magnitude between folds. Substituting the phase harmonics for the modulus-modulus coefficients does not result in a significant difference. However, you can use other criteria to make an informed choice between them such as: the smaller feature set for the phase harmonics, or the slightly higher computational cost of the phase harmonics over the scattering spectra.

Comparison with Full Scattering Transform

The full scattering transform used in Wavelet Time Scattering Classification of Phonocardiogram Data performs slightly better than the scattering spectra-phase harmonic approach shown here. The following table summarizes the macro-averaged precision, recall, and F1 score metrics across the three approaches.

Method

Precision

Recall

F1 Score

Scattering Transform

with Majority Vote

90.227

93.431

91.387

Scattering Spectra

89.228

92.105

90.309

Phase Harmonics

89.697

92.496

90.768

Given the stochastic nature of training machine learning algorithms and the different techniques used in each case, these results are very likely within the range of expected statistical variation. One clear advantage of the scattering spectra-phase harmonic approach is interpretability. With the scattering spectra-phase harmonic approach, the coefficients are always interpretable as correlations between scale-aligned wavelet coefficients. On the other hand, the majority vote method employed with the full scattering transform is more difficult to interpret because it obtains one class prediction for every scattering coefficient across all the paths. That approach is sensitive to the OversamplingFactor in the scattering transform. On the other hand, the scattering spectra method reduces each correlation to a scalar irrespective of the OversamplingFactor. One potential disadvantage of the scattering spectra-phase harmonic approach is the growth in the number of coefficients as the quality factor increases, while the number of paths in the scattering transform grows much more slowly as the quality factor increases. See the documentation for more detail on the differences between the scattering transform and the scattering spectra-phase harmonic approach.

References

  1. Goldberger, A. L., L. A. N. Amaral, L. Glass, J. M. Hausdorff, P. Ch. Ivanov, R. G. Mark, J. E. Mietus, G. B. Moody, C.-K. Peng, and H. E. Stanley. "PhysioBank, PhysioToolkit, and PhysioNet: Components of a New Research Resource for Complex Physiologic Signals". Circulation. Vol. 101, No. 23, 13 June 2000, pp. e215-e220. https://circ.ahajournals.org/content/101/23/e215.full.

  2. Lempereur, Etienne, Nathanaël Cuvelle–Magar, Florentin Coeurdoux, Stéphane Mallat, and Eric Vanden-Eijnden. 2026. "MGD: Moment Guided Diffusion for Maximum Entropy Generation." arXiv preprint. https://arxiv.org/abs/2602.17211.

  3. Liu et al. "An open access database for the evaluation of heart sound algorithms". Physiological Measurement. Vol. 37, No. 12, 21 November 2016, pp. 2181-2213. https://www.ncbi.nlm.nih.gov/pubmed/27869105.

  4. Mallat, Stephane, Sixin Zhang, and Gaspar Rochette. 2019. "Phase harmonic Correlations and Convolutional Neural Networks". Journal of Information and Inference, 721-747, https://doi.org/10.1093/imaiai/iaz019.

  5. Morel, Rudy, Gaspar Rochette, Roberto Leonarduzzi, Jean-Philippe Bouchaud, and Stéphane Mallat. 2024. "Scale Dependencies and Self-Similar Models with Wavelet Scattering Spectra." Applied and Computational Harmonic Analysis, November, 101724–24. https://doi.org/10.1016/j.acha.2024.101724.

  6. Regaldo-Saint Blanchard, Bruno, Erwan Allys, Constant AuClair, Francois Boulanger, Michael Eickenburg, Francois Levrier, Leo Vacher, and Sixin Zhang. 2023. "Generative Models of Multi-channel Data from a Single Example - Application to Dust Emission." The Astrophysical Journal 943 (2023): 9. https://doi.org/10.3847/1538-4357/aca538.

Supporting Functions

function PRTable = helperF1heartSounds(confmat)
% This function is only in support of Scattering Spectra and 
% Wavelet Phase Harmonics with Phonocardiogram Data It may change or be
% removed in a future release.
precisionAB = confmat(2,2)/sum(confmat(:,2))*100;
precisionNR = confmat(1,1)/sum(confmat(:,1))*100 ;
recallAB = confmat(2,2)/sum(confmat(2,:))*100;
recallNR = confmat(1,1)/sum(confmat(1,:))*100;
F1AB = 2*(precisionAB*recallAB)/(precisionAB+recallAB);
F1NR = 2*(precisionNR*recallNR)/(precisionNR+recallNR);
MacroAverages = mean(cat(2,[precisionAB; precisionNR],...
    [recallAB;recallNR], [F1AB;F1NR]));
% Construct a MATLAB Table to display the results.
PRTable = array2table([precisionAB recallAB F1AB;...
    precisionNR recallNR F1NR; ...
    MacroAverages],...
    VariableNames = ["Precision","Recall","F1_Score"],...
    RowNames = ["Abnormal","Normal","Macro Average"]);
end

See Also

Objects

Functions

Topics