Checklist OAI 2025 Stage I · Task 3
ECG Signal Disorder Detection
Polish title: Wykrywanie Zaburzeń Sygnału EKG
Design at most four meta-features of synthetic single-lead ECG segments so that a small random forest classifies them into normal and four arrhythmia classes.
The task
An ECG is a time series; each sample here is a synthetic single-lead segment of 150 time steps containing one PQRST sequence and its surroundings. Samples belong to five classes: 0 normal, 1 AFib (atrial fibrillation), 2 PAC (premature atrial contractions), 3 PVC (premature ventricular contractions) and 4 ST elevation (ST-elevation myocardial infarction). The training set has 1,400/150/150/150/150 samples per class and the validation set 819/142/191/197/151.
Motivated by embedded devices with limited memory and energy, the contestant analyses the data and prepares a set of at most 4 meta-features (for example statistics of the signal) that give the best balanced accuracy, together with the hyperparameters of the random forest, in a YourSolution class. Preprocessing such as normalisation is allowed.
The notebook explains balanced accuracy with worked examples and describes each anomaly (AFib, PAC, PVC, STEMI) with medical references. The hidden test set has a different number of observations from the training and validation sets.
Abridged and translated by SOTA from the official Polish materials. The official statement has the exact rules, and it wins wherever this summary differs.
In English
This task was published in Polish. SOTA translated its 2 files into English on 16 September 2026. Only the words changed in the notebooks: markdown, code comments, messages and printed output. The code, file names and paths are the original's, so a translated notebook runs with the original data.
- Task notebook Polish original of Task notebook
- Official solution Polish original of Official solution
Read the task notebook in English
ECG Signal Disorder Detection

Image generated using the DALL-E model.
Introduction
The development of artificial intelligence opens up new possibilities in medical diagnostics, especially in the analysis of complex data such as electrocardiographic (ECG) signals. The ECG is one of the most commonly used diagnostic tools in medicine, making it possible to assess the functioning of the heart and detect abnormalities in it.
Traditionally, an ECG signal is obtained from twelve leads; in this task, however, we will focus on a single-lead signal, i.e. we have one variable representing the electrical voltage generated by the heart over time. These data are recorded as a curve that depends on time, so we can speak of a time series here. Characteristic parts can be distinguished in an ECG signal, namely the P, Q, R, S and T waves, and the intervals between two events in the ECG, among which the R-R interval (the time between the occurrence of two consecutive R waves) plays an important role. In addition, we speak of segments, i.e. the length between two specific waves in the ECG, between which the signal should be at its baseline amplitude. A complex, in turn, is made up of several grouped waves. Here we mainly distinguish the QRS complex. A schematic drawing of an ECG with its parts labelled is presented below.

In the ECG of a healthy person, the PQRST sequence can be observed. First, we distinguish the P wave, which represents the contraction of the atria and is a small vertical deflection before the QRS complex. Next, the QRS complex indicates the contraction of the ventricles and is formed by three deflections: the Q wave, the R wave and the S wave. After that, the ST segment can be seen, i.e. the flat segment between the QRS complex and the T wave, which corresponds to the early phase of ventricular repolarisation. Finally, the T wave, which is a rounded, vertical deflection, relates to the repolarisation of the ventricles and their return to the initial state. The PQRST sequence resembles a sinusoid whose maximum is reached at the R wave. In the case of cardiac disorders, the ECG may show various anomalies, such as additional minima or maxima, or a considerably increased standard deviation over the whole measurement. The characteristics of these anomalies depend on the type and cause of the disorder.
In the task below, you have to deal with samples containing single PQRST sequences and their surroundings. Most samples will correspond to data without anomalies, an example of which is shown in the image below:

There will also be measurements corresponding to four types of disorders: AFib, i.e. atrial fibrillation, PAC, i.e. premature atrial contraction, PVC, i.e. premature ventricular contractions, and STEMI, i.e. ST-elevation myocardial infarction.
NOTE: The data below are synthetic and are only an approximation of real ECG data!
The ECG is a typical example of a time series that can be analysed using dedicated machine learning methods, including neural networks, e.g. recurrent networks. However, using neural networks is not always necessary, or even advisable. For some problems, satisfactory results can be obtained with simpler methods, in which appropriate preparation of the data is key. Skilful analysis of the data makes it possible to select a few meta-features – features that concisely describe the samples in the dataset, e.g. the mean, minimum, maximum, standard deviation, etc. They can be used for classification instead of the original features. As a result, we work with low-dimensional input data; for example, we reduce a 150-dimensional vector containing the information from the original time steps to a 4-dimensional vector containing specially prepared features.
Examples of the use of a small number of meta-features are machine learning models that are meant to run on embedded devices or small mobile devices, where constraints such as the requirement of low power consumption, a small amount of available RAM or limited computing power are crucial. In such cases, simpler models must be used that are able to provide adequate classification accuracy while satisfying the required constraints.
Task
Prepare a solution (including training a random forest model) that meets the requirements of our embedded device. Analyse the data and prepare a set of 4 meta-features that give the best balanced accuracy for the ECG signal classification problem. The dataset consists of a training set and a validation set (with labels), on which you can verify your approach. Your solution will be evaluated on a separate (secret) test set, in which the number of observations will differ from the number of observations in the training and validation sets. Each sample is described by 150 values corresponding to consecutive time steps and is assigned to one of the following five classes:
| Class ID | Class name | Description | Samples in the training set | Samples in the validation set |
|---|---|---|---|---|
| 0 | normal | no anomalies | 1400 | 819 |
| 1 | afib | Atrial Fibrillation | 150 | 142 |
| 2 | pac | Premature Atrial Contractions | 150 | 191 |
| 3 | pvc | Premature Ventricular Complex | 150 | 197 |
| 4 | st_elevation | ST-elevation myocardial infarction | 150 | 151 |
The new features should contain the key diagnostic information that differentiates the classes above and that will allow the listed anomalies to be classified effectively.
The classifier for this task is a random forest with no more than 10 decision trees and a maximum depth of 10. Solutions that do not meet these conditions will be disqualified! There are no restrictions on the other parameters of the forest. Preprocessing, i.e. initial processing of the input data (e.g. applying data normalisation), is also allowed.
Scoring Criterion
Your solution will be scored on the secret test set on the basis of balanced classification accuracy (balanced accuracy):
This means that all solutions that achieve up to balanced classification accuracy on the test set will receive points, while those that achieve at least balanced classification accuracy will receive the maximum number of points for the task. All values in the range will be converted into a number of points (between and ) according to the formula above.
Hint: The score on the validation set should be your measure of the quality of the proposed solution.
In problems concerning the detection of diseases, we quite often deal with an imbalanced (unbalanced) dataset. This means that the data are usually dominated by normal examples, corresponding to healthy people, while the samples representing sick people usually form a minority. Imagine a situation in which, out of 100 samples, only 10 concern sick people and the remaining 90 concern healthy people. A model that assigned the class healthy to every sample would then achieve 90% classification accuracy, but only 50% balanced classification accuracy! Of course, such a model would be useless. In such cases, we need a measure that better corresponds to the needs arising from the problem at hand and that reports the effectiveness of the model in a way that is useful from the point of view of its eventual user.
In this task, you must therefore focus on making sure that each of the classes is assigned correctly.
Constraints
- Your solution will be tested on the Contest Platform without internet access and in an environment without a GPU.
- The evaluation of your final solution on the Contest Platform must not take longer than 1 minute without a GPU.
- When preparing the data, remember that:
- the use of machine learning methods other than random forests, whether supervised or unsupervised (e.g. autoencoders, multilayer perceptrons and other neural networks, support vector machines (SVM), and others), is forbidden; dimensionality reduction methods such as principal component analysis (PCA) are, however, allowed;
- when constructing the meta-features, you may only use functions available as standard in Python (
v3.11), as well as Numpy (v2.0.2) and Scipy (v1.14.1); - at most 4 meta-features may be computed,
- Only the random forest (RandomForestClassifier) from the scikit-learn library (
v1.5.2) may be used for classification:- consisting of at most 10 decision trees (
n_estimators); - each tree is to have a maximum depth of 10 (
max_depth); - the other hyperparameters may be modified without restriction;
- consisting of at most 10 decision trees (
Submission Files
This notebook, completed with your solution (see the YourSolution class), in which you prepare a set of 4 meta-features describing the dataset and a set of random forest hyperparameters.
Evaluation
Remember that during checking, the FINAL_EVALUATION_MODE flag will be set to True.
You can score between 0 and 100 points for this task. The number of points you receive will be calculated on the (secret) test set on the Contest Platform on the basis of the formula given above, rounded to an integer. If your solution does not meet the above criteria or does not run correctly, you will receive 0 points for the task.
Supplementary Information
Balanced Classification Accuracy
Let be the number of classes, and let be the number of samples belonging to the -th class, where . Furthermore, let be the class predicted by the model for the -th sample that actually belongs to the -th class. The balanced classification accuracy can then be computed as follows:
where is an indicator function that takes the value 1 if , i.e. when the class predicted for the -th sample is the same as the true class of that sample, and 0 otherwise. The outer sum runs over the successive classes, and the inner sum over the successive samples belonging to the given class.
Example: Let
be the vector representing the true classes of the successive samples, and let
be the vector representing the model's predictions for these samples. We therefore have four classes, and the model had a problem with class number . All the other examples were assigned without error. In total, 10 out of 12 samples were classified correctly, which means that if we were to measure "ordinary" classification accuracy, we would obtain approx. . However, when we look at the balanced classification accuracy, we obtain a score of .
Now suppose that
i.e. the model correctly classifies of the samples from class 1 and of the samples from the other classes. The "ordinary" classification accuracy here is just under , whereas the balanced classification accuracy is .
Anomalies Occurring in the Dataset Under Consideration
AFib
Atrial Fibrillation (AFib) occurs when action potentials are triggered very rapidly and chaotically, and as a result the heart rhythm is irregular. In this disorder, the P waves may not be visible on the ECG, and the QRS complex becomes irregular.

PAC
Premature Atrial Contractions (PACs) are associated with an abnormal P wave followed by a normal QRS complex. Note! The samples in this task include examples in which only the premature P wave is visible within a single sample of the dataset.

PVC
Premature Ventricular Contractions (PVCs) are extra heartbeats that begin in one of the two ventricles of the heart and disrupt its regular rhythm. They are one of the common types of arrhythmia. These contractions occur earlier than would be expected from the preceding R-R intervals.

STEMI
ST-elevation myocardial infarction (STEMI) blocks the flow of blood to the heart muscle and causes the muscle to die. The ST segment occurs immediately after the QRS complex. Normally there is no electrical activity there, which is why it is flat. If, however, the ST segment is elevated, this indicates a blockage of one of the main arteries supplying blood to the heart.

Sources for the medical description of the ECG: 1, 2, 3, 4, 5, 6, 7; the image of the PQRST waves is based on 8.
Starter Code
In this section, we initialise the environment by importing the required libraries and functions. The prepared code will help you to work with the data efficiently and to build the actual solution.
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
# While your solution is being checked, the value of the FINAL_EVALUATION_MODE flag will be changed to True
FINAL_EVALUATION_MODE = False
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
import cloudpickle
import os
import random
from abc import ABC, abstractmethod
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import balanced_accuracy_score
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
# Setting the seed of the pseudo-random number generator to make the results deterministic.
random.seed(42)
np.random.seed(42)
Loading the Data
The code below loads the data.
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
train_val_filename = "train_validation_sets.npz"
if not os.path.exists(train_val_filename):
import gdown
url = "https://drive.google.com/file/d/1pCqgbsKBQP1UnH2kMmBKRS1AuvmSl9jx/view?usp=sharing"
gdown.download(url, train_val_filename, quiet=True, fuzzy=True)
train_valid_bundle = np.load("train_validation_sets.npz", allow_pickle=True)
x_train = train_valid_bundle["X_train"]
y_train = train_valid_bundle["y_train"]
y_train_str = train_valid_bundle["anomaly_train"]
x_valid = train_valid_bundle["X_validation"]
y_valid = train_valid_bundle["y_validation"]
y_valid_str = train_valid_bundle["anomaly_validation"]
Public Solution Interface
This is all we require of your class. In your solution, you may modify your class as you wish by adding attributes and new methods that compute meta-features – anything you need to solve the task.
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
class ISolution(ABC):
random_forest: RandomForestClassifier | None = None
@classmethod
def create_with_training(cls) -> "ISolution":
"""Method that creates a solution with a trained random forest."""
solution = cls()
hyperparameters = cls.get_rf_hyperparameters()
hyperparameters = cls.validate_hyperparameters(hyperparameters)
solution.random_forest = RandomForestClassifier(**hyperparameters)
meta_features = solution.compute_meta_features(x_train)
solution.random_forest.fit(meta_features, y_train)
return solution
@staticmethod
def validate_hyperparameters(hyperparameters: dict[str, int | float | str]) -> dict[str, int | float | str]:
"""
This function checks whether the random forest hyperparameters comply with the requirements of the task. If not, it
corrects them to the default values.
"""
hyperparameters["n_estimators"] = min(hyperparameters.get("n_estimators", 10), 10)
hyperparameters["max_depth"] = min(hyperparameters.get("max_depth", 10), 10)
hyperparameters["random_state"] = 42
return hyperparameters
@abstractmethod
def compute_meta_features(self, x: np.ndarray) -> np.ndarray:
"""
For each example in the set $x$, described by 150 features, this function should return a vector of 4 features that
will represent that example. This function should transform an input array of size (n, 150) into an array of
size (n, 4).
"""
pass
@staticmethod
@abstractmethod
def get_rf_hyperparameters() -> dict[str, int | float | str]:
"""
This function should return a dictionary with the random forest hyperparameters. Remember the constraints on the
number of trees and their depth!
"""
pass
Code with the Scoring Criterion
Code similar to the code below will be used to score the solution on the test set.
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
def balanced_accuracy_to_score(balanced_accuracy: float) -> float:
return min(max((balanced_accuracy - 75.) * (100. / (98. - 75.)), 0.), 100.)
def score_solution(solution: ISolution) -> float:
x, y = x_valid, y_valid
meta_features = solution.compute_meta_features(x)
y_hat = solution.random_forest.predict(meta_features)
balanced_accuracy = 100. * balanced_accuracy_score(y, y_hat)
assert meta_features.shape[-1] <= 4
assert solution.random_forest.n_estimators <= 10
assert solution.random_forest.max_depth <= 10
if not FINAL_EVALUATION_MODE:
print("Evaluation of the model's performance: \n")
print(f"Balanced classification accuracy: {balanced_accuracy: .4f}")
return int(round(balanced_accuracy_to_score(balanced_accuracy)))
Example Solution
Below we present a simplified solution that serves as an example demonstrating the basic functionality of the notebook. It can serve as a starting point for developing your own solution.
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
class ExemplarySolution(ISolution):
def compute_meta_features(self, x: np.ndarray) -> np.ndarray:
return np.array([
np.min(x, axis=1),
np.max(x, axis=1),
np.mean(x, axis=1),
np.std(x, axis=1)
]).T
@staticmethod
def get_rf_hyperparameters() -> dict[str, int | float | str]:
return {
"n_estimators": 3,
"random_state": 42
}
if not FINAL_EVALUATION_MODE:
exemplary_solution = ExemplarySolution.create_with_training()
print(f"Score: {score_solution(exemplary_solution)} pts")
Your Solution
Place your solution in this section. Make changes only here!
class YourSolution(ISolution):
def compute_meta_features(self, x: np.ndarray) -> np.ndarray:
"""
For each example in the set $x$, described by 150 features, this function should return a vector of 4 features that
will represent that example. This function should transform an input array of size (n, 150) into an array of
size (n, 4).
"""
pass
@staticmethod
def get_rf_hyperparameters() -> dict[str, int | float | str]:
"""
This function should return a dictionary with the random forest hyperparameters. Remember the constraints on the
number of trees and their depth!
"""
pass
Evaluation
Running the cell below lets you check how many points your solution would score on the validation data. Before submitting, make sure that the whole notebook runs from start to finish without errors and without any user intervention after selecting the "Run All" option.
During checking, the model will be saved as your_model.pkl and scored on the test set.
######################### DO NOT CHANGE THIS CELL WHEN SUBMITTING ##########################
if FINAL_EVALUATION_MODE:
your_solution = YourSolution.create_with_training()
print(f"Score: {score_solution(your_solution)} pts")
OUTPUT_PATH = "file_output"
FUNCTION_FILENAME = "your_solution"
FUNCTION_OUTPUT_PATH = os.path.join(OUTPUT_PATH, FUNCTION_FILENAME)
if not os.path.exists(OUTPUT_PATH):
os.makedirs(OUTPUT_PATH)
with open("file_output/your_model.pkl", "wb") as model_out:
cloudpickle.dump(your_solution, model_out)
Translated by SOTA. The Polish original is the official version and wins wherever the two differ. In the official solution, the plots and two summary tables were produced by the original run, so their titles and column headings are still in Polish; the code glosses the kept column names. If you organise this olympiad and would like the translation removed, email [email protected] and we will take it down.
At a glance
- You get
train_validation_sets.npz(training and validation signals with labels), included in the task folder and also downloadable from Google Drive.- You submit
- This notebook with YourSolution (meta-feature extraction and random-forest hyperparameters); saved to
your_model.pklduring checking. - Scoring
- Balanced accuracy (in percent) on the hidden test set: 0 points at ≤ 75%, 100 points at ≥ 98%, linear in between; rounded to an integer.
- Rules
- Tested without Internet access and without a GPU; evaluation must take at most 1 minute.
- Only sklearn RandomForestClassifier (v1.5.2) with
n_estimators≤ 10 andmax_depth≤ 10 may be used for classification; violating solutions are disqualified. Other hyperparameters are unrestricted. - No other machine-learning methods (supervised or unsupervised, e.g. autoencoders, MLPs, SVMs); dimensionality-reduction methods such as PCA are allowed.
- Meta-features may use only the Python 3.11 standard library, NumPy 2.0.2 and SciPy 1.14.1; at most 4 meta-features.
- Format
- Stage I (online), 17 February – 22 March 2025; up to 100 points per task (500 in total). Evaluated automatically on the Competition Platform (Platforma Konkursowa) on a hidden test set; points are rounded to an integer, and a notebook that fails the requirements or does not run scores 0.