teppy 0.1.9
TMS-EEG Evoked Potential Analysis Framework
Requires Python: >=3.10

<a id="readme-top"></a>

[![PyPI Version](https://img.shields.io/pypi/v/teppy)](https://pypi.org/project/teppy/)
[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://python.org)
[![License](https://img.shields.io/badge/license-GPLv3-green)](https://github.com/gianlucagag/TEPpy/LICENSE)
[![Open Issues](https://img.shields.io/github/issues/gianlucagag/TEPpy)](https://github.com/gianlucagag/TEPpy/issues)

<!-- PROJECT LOGO -->
<br />
<div align="center">
  <a href="https://github.com/gianlucagag/TEPpy">
    <img src="https://raw.githubusercontent.com/gianlucagag/TEPpy/docs/images/logo.png" 
         alt="TEPpy Logo" width="220">
  </a>

  <h1 align="center">TEPpy</h1>
  
  <p align="center">
    TMS-EEG Evoked Potential Analysis Framework
    <br />
    <a href="https://github.com/gianlucagag/TEPpy/tree/main/docs/doc.md"><strong>Explore the docs »</strong></a>
    <br />
    <br />
    <a href="https://github.com/gianlucagag/TEPpy/issues/new?labels=bug&template=bug-report.md">
      <img src="https://img.shields.io/badge/REPORT-BUG-red" alt="Report Bug">
    </a>
    &nbsp;
    <a href="https://github.com/gianlucagag/TEPpy/issues/new?labels=enhancement&template=feature-request.md">
      <img src="https://img.shields.io/badge/REQUEST-FEATURE-green" alt="Request Feature">
    </a>
  </p>
</div>

<!-- TABLE OF CONTENTS -->
<details>
  <summary>Table of Contents</summary>
  <ol>
    <li>
      <a href="#about-the-project">About The Project</a>
      <ul>
        <li><a href="#key-features">Key Features</a></li>
      </ul>
    </li>
    <li>
      <a href="#getting-started">Getting Started</a>
      <ul>
        <li><a href="#dependencies">Dependencies</a></li>
        <li><a href="#installation">Installation</a></li>
      </ul>
    </li>
    <li><a href="#usage">Usage</a></li>
    <li><a href="#citation">Citation</a></li>
    <li><a href="#contact">Contact</a></li>
    <li><a href="#contributing">Contributing</a></li>
    <li><a href="#license">License</a></li>
  </ol>
</details>

<!-- ABOUT THE PROJECT -->
## About The Project

TEPpy is a Python package for analyzing Transcranial Magnetic Stimulation combined with Electroencephalography (TMS-EEG) data. It provides tools for characterizing TMS-EEG evoked potential (TEP) temporal and spectral features, with automated peak detection and time-frequency analysis methods.

### Key Features

- Convert MNE-Python Epochs objects to TEP objects
- Automatic detection of TEP peaks
- Automatic selection of most reponsive channels
- Time-frequency analysis using Stockwell transform
- Extraction of temporal and spectral features
- Visualization of temporal and spectral features
- Customizable analysis parameters for research flexibility

### Analysis Workflow

![TEPpy Analysis Workflow](docs/images/workflow.png)
*Typical TEPpy analysis workflow from MNE Epochs to extracted features*

<!-- GETTING STARTED -->
## Getting Started

### Dependencies

- numpy>=2.0.1
- scipy>=1.14.0
- mne>=1.9.0
- matplotlib>=3.9.1
- stockwell>=1.2
  
### Installation

Install from PyPI:

```bash
pip install teppy
```
### Core Concepts

#### TEP Class
Converts MNE Epochs objects into analyzable TEP data structures:
- Performs automatic peak detection (P1, P2, P3)
- Extracts amplitude, latency, and peak-to-peak features
- Provides channel selection methods
  
`TEP(epochs, exclude_channels=None, p1_range=[0.01, 0.04], low_limit=35, sign_corr='convexity')`

Key methods:

`compute_timefreq()`: Perform time-frequency analysis

`get_ntop_ch()`: Get top channel names

`plot_summary()`: Generate summary plot

Feature accessors: `get_amplitudes()`, `get_latencies()`, etc.

#### TimeFreq Class
Stores and analyzes time-frequency decomposition results:
- Computes power and inter-trial coherence
- Estimates natural frequency
- Provides visualization tools

Key methods:
  
`plot_natfreq1()`: Comprehensive natural frequency visualization

`plot_natfreq2()`: Compact visualization

`get_natfreq()`: Compute natural frequency

Data accessors: `get_power()`, `get_itc()`, `get_complex()`

<!-- USAGE -->
## Usage

### 1. Loading Data
```python
import mne
from teppy import TEP

# Load EEG epochs
epochs = mne.read_epochs('your_data.fif', verbose=False)

# Define channels to exclude
exclude_channels = ['Fp1', 'Fpz', 'Fp2', 'F7', 'F8', 'FT7', 'FT8', 'T7',
                    'T8', 'TP7', 'TP8', 'TP9', 'TP10', 'P7', 'P8', 'Iz']
```

### 2. Creating TEP Object
```python
tep = TEP(
    epochs,
    exclude_channels=exclude_channels,
    p1_range=[0.01, 0.04],  # P1 detection window (s)
    low_limit=35,            # Minimum peak separation (Hz)
    sign_corr='convexity'    # Signal correction method
)
```

### 3. Extracting Features
```python
# Get top 4 channels
top_channels = tep.get_ntop_ch(ntop=4)

# Get comprehensive feature summary
feature_summary = tep.info_ntop(ntop=4)
```

### 4. Generating Summary Plot
```python
fig = tep.plot_summary(
    ntop=4,
    tlim=(-0.1, 0.3),      # Time limits (s) of the plot
    cmap_topo='Reds',    # Topomap colormap
)
fig.savefig('tep_summary.png', dpi=300)
```
<div align="center">
  <div style="display:flex; flex-wrap:wrap; justify-content:center">
    <div style="margin:10px; text-align:center">
      <img src="docs/images/plot_summary.png" width="50%">
    </div>
  </div>
</div>

### 5. Time-Frequency Analysis
```python
# Compute time-frequency decomposition
timefreq = tep.compute_timefreq(
    ntop=4,                 # Use top 4 channels
    method='stock1',        # MNE's Stockwell implementation
    fmin=4, fmax=45,        # Frequency range
    baseline_correction=True,
    baseline_range=(-0.5, -0.1)
)

# Plot natural frequency map
fig = timefreq.plot_natfreq1(
    natfreq_window=[0.02, 0.15],  # Natural frequency window
    plot_tlim=(-0.1, 0.3), # Time limits (s) of the plot
    plot_ms=True, # plot the time axis in ms
    cmap='RdBu_r'
)
fig.savefig('timefreq_analysis.png', dpi=300)
```
<div align="center">
  <div style="display:flex; flex-wrap:wrap; justify-content:center">
    <div style="margin:10px; text-align:center">
      <img src="docs/images/plot_natfreq1.png" width="50%">
    </div>
  </div>
</div>


### Advanced Features

#### Custom Channel Selection
```python
# Select specific channels
timefreq_custom = tep.compute_timefreq(
    pick_ch=['Cz', 'Pz', 'Fz'],  # Custom channel selection
    method='stock2',              # Alternative Stockwell implementation
    gamma=1.5,                    # Gamma parameter for stock2
    return_complex=True            # Return complex coefficients
)
```

#### Batch Processing
```python
results = []
subjects = ['sub-01', 'sub-02', 'sub-03']

for subject in subjects:
    epochs = mne.read_epochs(f'{subject}_epo.fif')
    tep = TEP(epochs, exclude_channels=exclude_channels)
    tf = tep.compute_timefreq(ntop=4)
    
    results.append({
        'subject': subject,
        'nat_freq': tf.get_natfreq(natfreq_window=[0.02, 0.15]),
        'top_channels': tep.get_ntop_ch(ntop=4),
        'p1p2_amplitude': tep.get_peaktopeak(ntop=4).mean(0)[0] # [0]: Get P1-P2 amplitude 
    })
```

#### Accessing Underlying Data
```python
# Get raw power data
power_data = timefreq.get_power(average_channels=True)

# Get complex coefficients
complex_data = timefreq.get_complex()

# Get evoked spectrum
evoked_spectrum = timefreq.get_evoked_spectrum(
    sum_window=[0.02, 0.12]
)
```

<!-- CITATION -->
## Citation

<!-- CONTACT -->
## Contact

<!-- CONTRIBUTING -->
## Contributing

Contributions are welcome! Please fork the repository and submit pull requests. Report issues on the GitHub issue tracker.

<!-- LICENSE -->
## License