---
title: "Understanding Annotations"
format:
  html:
    toc: true
    toc-depth: 3
    toc-location: left
    number-sections: true
vignette: >
  %\VignetteIndexEntry{Understanding WFDB Annotations}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
knitr:
  opts_chunk:
    collapse: true
    comment: '#>'
---

```{r}
#| label: setup
library(EGM)
```

<!-- 
Introduction will go here. First start with WFDB annotation standard. Need to describe the current standards for how annotations are documented in WFDB. The general structure will be...

- Give an example of the annotation table and the required elements
- Show the overall types of annotations that are standard, e.g. the `.surface_annotations`, grouped by the type of annotations that are available to help it be easy to understand
-->

# Electrocardiogram Annotations

## WFDB Annotation Standards

The WFDB (Waveform Database) software package provides a standardized system for annotating cardiac signals, predominately surface electrocardiogram (**ECG**) data.
Annotations are polymorphic, meaning multiple annotation sets can be applied to a single signal dataset.
The limitation is that these are constrained to physiological events associated with surface ECGs. 

### Annotation Table Structure

All WFDB-compatible annotations in the `{EGM}` package are stored as `annotation_table` objects. 
The columns are type-specific, which allows them to be easily stored.
These tables contain the following required columns:

- **annotator**: Name of the annotation function or creator (e.g., "sqrs", "ecgpuwave", "manual")
- **time**: Time stamp constructed from sample number and sampling frequency (format: HH:MM:SS.mmm)
- **sample**: Integer index of the annotation position in the signal
- **type**: Single character symbol describing the annotation type (see annotation types below)
- **subtype**: Single character providing additional classification
- **channel**: Integer indicating which signal channel the annotation applies to
- **number**: Additional numeric qualifier providing context-specific information

```{r}
# Create annotations for detected R-peaks
# Example
ann <- annotation_table(
  annotator = "qrs",
  sample = c(100, 350, 600, 850),
  type = "N",
  frequency = 250,
  channel = 0
)

# Print
ann
```

## Standard ECG Annotation Types

The WFDB standard defines 41 annotation types for surface electrocardiograms. 
These annotations can be broadly grouped into several categories:

### Beat Annotations

Beat annotations mark individual cardiac cycles and their characteristics:

```{r}
# Load internal annotation data
beat_symbols <- c("N", "L", "R", "a", "V", "F", "J", "A", "S", "E", "j", "/", "Q", "~")
beat_labels <- EGM:::.surface_annotations[EGM:::.surface_annotations$symbol %in% beat_symbols, ]

knitr::kable(
  beat_labels[, c("symbol", "mnemonic", "description")],
  col.names = c("Symbol", "Mnemonic", "Description"),
  row.names = FALSE,
  caption = "Beat Annotation Types"
)
```

**Key beat types:**

- **N (NORMAL)**: Normal sinus beat
- **V (PVC)**: Premature ventricular contraction
- **A (APC)**: Atrial premature contraction
- **L/R (LBBB/RBBB)**: Bundle branch block beats
- **F (FUSION)**: Fusion of ventricular and normal beat
- **/ (PACE)**: Paced beat

### Waveform Boundary Annotations

These mark the beginning, peak, and end of ECG waveforms:

```{r}
wave_symbols <- c("p", "t", "u", "(", ")")
wave_labels <- EGM:::.surface_annotations[EGM:::.surface_annotations$symbol %in% wave_symbols, ]

knitr::kable(
  wave_labels[, c("symbol", "mnemonic", "description")],
  col.names = c("Symbol", "Mnemonic", "Description"),
  row.names = FALSE,
  caption = "Waveform Boundary Annotations"
)
```


### Rhythm and Signal Quality Annotations

```{r}
rhythm_symbols <- c("+", "|", "s", "T", "~", "x")
rhythm_labels <- EGM:::.surface_annotations[EGM:::.surface_annotations$symbol %in% rhythm_symbols, ]

knitr::kable(
  rhythm_labels[, c("symbol", "mnemonic", "description")],
  col.names = c("Symbol", "Mnemonic", "Description"),
  row.names = FALSE,
  caption = "Rhythm and Signal Quality Annotations"
)
```

### Specialized Annotations

```{r}
special_symbols <- c("*", "D", "\"", "=", "!", "[", "]", "@", "r", "^", "B", "e", "n", "f")
special_labels <- EGM:::.surface_annotations[EGM:::.surface_annotations$symbol %in% special_symbols, ]

knitr::kable(
  special_labels[, c("symbol", "mnemonic", "description")],
  col.names = c("Symbol", "Mnemonic", "Description"),
  row.names = FALSE,
  caption = "Specialized Annotations"
)
```

## Common Annotation File Types

When working with WFDB files, annotations are stored with specific file extensions that indicate the annotator used:

- **`.atr`**: Manually reviewed and corrected reference annotations
- **`.ann`**: General annotation file
- **`.ecgpuwave`**: Surface ECG wave boundaries (P, QRS, T waves) generated by the ecgpuwave algorithm
- **`.sqrs`, `.wqrs`, `.gqrs`**: R-peak detections from different QRS detection algorithms

## Accessing All Standard Annotations

The complete list of standard WFDB annotations can be accessed using:

```{r, eval=FALSE}
# View all standard ECG annotation types
wfdb_annotation_labels()

# Filter for specific symbols
wfdb_annotation_labels(symbol = c("N", "V", "A"))

# Decode annotations in an existing annotation table
ann <- annotation_table(
  annotator = "example",
  sample = c(100, 200),
  type = c("N", "V")
)

# Add human-readable descriptions
wfdb_annotation_decode(ann)
```

## Example: Working with ECG Annotations

Here's a practical example of reading, interpreting, and visualizing ECG annotations:

```{r}
# Read an ECG with annotations
record_path <- system.file('extdata', package = 'EGM')
ecg <- read_wfdb(
  record = "muse-sinus",
  record_dir = record_path
)

# Read associated annotations (if they exist)
ann <- read_annotation(
   record = "muse-sinus",
   annotator = "ecgpuwave",
   record_dir = record_path
)

# Decode annotation types
ann_decoded <- wfdb_annotation_decode(ann)
head(ann_decoded)
```

# Working with Multiple Annotators

A key feature of the WFDB system is the ability to apply multiple independent annotation sets to a single recording. This is essential for comparing different automated algorithms, validating manual annotations, or analyzing different physiological features simultaneously.

## Reading Multiple Annotators

The `{EGM}` package supports reading multiple annotators in a single operation:

```{r, eval=FALSE}
# Read a single annotator (traditional approach)
ann_single <- read_annotation(
  record = "300",
  record_dir = "path/to/data",
  annotator = "ecgpuwave"
)

# Read multiple annotators simultaneously
ann_multiple <- read_annotation(
  record = "300",
  record_dir = "path/to/data",
  annotator = c("ecgpuwave", "atr", "wqrs")
)

# Returns a named list of annotation_table objects
names(ann_multiple)
# [1] "i"  "ii" "wqrs"

# Access individual annotators
ann_multiple$ecgpuwave
ann_multiple$atr
```

## Reading Complete Records with Multiple Annotators

You can also read an entire WFDB record with multiple annotations in one call:

```{r}
# Read signal, header, and multiple annotations together
record <- read_wfdb(
  record = "ludb-ecg",
  record_dir = system.file("extdata", package = "EGM"),
  annotator = c("i", "ii")
)

# The egm object now contains multiple annotators
print(record)
```

## Helper Functions for Multi-Annotator Workflows

### List Available Annotators

```{r}
# Get names of all annotators in an egm object
annotators <- list_annotators(record)
# [1] "i"  "ii"
```

### Extract Specific Annotators

```{r}
# Extract a specific annotator from an egm object
ecg_ann <- get_annotation(record, "i")

# Without a name: the table itself when there is one annotator, a named list of
# them when there are several - the same shape `read_annotation()` returns
all_ann <- get_annotation(record)

# This will error if the annotator doesn't exist
# get_annotation(record, "nonexistent")  # Error!
```

### Merge Multiple Annotators

For comparative analysis, you may want to combine multiple annotators into a single table:

```{r}
# Merge all annotators into one table
merged <- merge_annotations(record)

# The result includes an 'annotator' column to identify the source
head(merged)

# Merge only specific annotators
merged_subset <- merge_annotations(record, annotators = c("i"))

# You can also merge from a list of annotation_table objects
ann_list <- read_annotation(
  record = "ludb-ecg",
  record_dir = system.file("extdata", package = "EGM"),
  annotator = c("i", "ii")
)
merged_from_list <- merge_annotations(ann_list)
```

# Intracardiac Electrogram Annotations

<!--
This section is on the intracardiac annotation options. The intracardiac has to have a similar and compatible structure, with the same variable types.

The type includes:

- a = Atrial
- v = Ventricular
- h = His bundle recording
- f = Fascicular potential

The subtypes 
-->

This is a work in progress, where we aim to define and implement a comprehensive set of annotations for intracardiac electrograms that are compatible with the standard WFDB annotation system.



# Examples

## `ecgpuwave`

One of the common annotators used, called [`ecgpuwave`](https://physionet.org/content/ecgpuwave/1.3.4/), demonstrates how the annotation system is leveraged.

The standard labels are used:

- **p (PWAVE)**: P-wave peak
- **t (TWAVE)**: T-wave peak
- **( (WFON)**: Waveform onset
- **) (WFOFF)**: Waveform end

When using waveform boundary annotations like `(` and `)`, the **number** column specifies which waveform:

- 0 = P wave
- 1 = QRS complex
- 2 = T wave

For T-wave annotations (`t`), the **number** column describes morphology:

- 0 = Normal
- 1 = Inverted
- 2 = Positive
- 3 = Negative
- 4 = Biphasic (negative-positive)
- 5 = Biphasic (positive-negative)

# Registration

When reviewing annotations, the `{EGM}` system allows for all annotation types to be included, whether or not they conform with the WFDB annotator standards. 
The `annotation_table` class allows any __annotator__ type to be read in.

However the `{EGM}` package would like to allow for editing of annotations and writing annotations, which requires some level of standardization (particularly when introducing a new annotation labels to intracardiac data). 

To allow for editing on writing annotations (either through an algorithm approach or manual edits), several rules are being developed.




