Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 81 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,87 @@
A Python package for post-processing FreeSurfer outputs.


## Combining FreeSurfer data with XCP-D results

To perform exciting cross-modality comparisons with BOLD and structural data,
you can combine tabular outputs from `freesurfer-post` and XCP-D.
Here we will combine ReHo estimates with the surface stats for the Schaefer 100 parcellation.

### Finding matching files

In XCP-D the Schaefer atlases are included in the [`4S` parcellations](https://github.com/pennlinc/atlaspack).
In `4S` parcellations, the Schaefer parcellations are combined with 56 subcortical regions.
Therefore to get the Schaefer 100 parcellation we need to look for the `seg-4S156Parcels` files from XCP-D.
In this example we'll use `sub-01_task-emotion_dir-LR_run-1_space-fsLR_seg-4S156Parcels_stat-reho_bold.tsv`.

In `freesurfer-post` the parcellations come directly from the FreeSurfer annot files in the CBIG repo.
Following those naming conventions, we want to use `atlas-Schaefer2018100Parcels7Networks`.
Here we'll use `sub-01_atlas-Schaefer2018100Parcels7Networks_surfacestats.tsv`.

### Python example

Here we combine the two tsvs using Python

```python
import pandas as pd

# Load the tsvs into dataframes.
fspost_data = pd.read_csv(
"sub-01_atlas-Schaefer2018100Parcels7Networks_surfacestats.tsv",
sep = "\t",
)
xcpd_reho = pd.read_csv(
"sub-01_task-emotion_dir-LR_run-1_space-fsLR_seg-4S156Parcels_stat-reho_bold.tsv",
sep = "\t",
)

# Convert xcpd_reho from wide to long format
xcpd_reho_long = pd.melt(
xcpd_reho,
var_name="StructName",
value_name="reho",
)
# Prepend the string "7Networks_" to match the annot StructName
xcpd_reho_long["StructName"] = "7Networks_" + xcpd_reho_long["StructName"]

# Merge fspost_data with xcpd_reho_long, keeping all rows from both datasets
merged_data = pd.merge(fspost_data, xcpd_reho_long, on="StructName")
```

### R Example

To do the same thing in R

```r
library(tidyverse)

# Load the tsvs into dataframes.
fspost_data <- read.csv(
"sub-01_atlas-Schaefer2018100Parcels7Networks_surfacestats.tsv",
sep = "\t"
)
xcpd_reho <- read.csv(
"sub-01_task-emotion_dir-LR_run-1_space-fsLR_seg-4S156Parcels_stat-reho_bold.tsv",
sep = "\t"
)

# Convert xcpd_reho from wide to long format
xcpd_reho_long <- xcpd_reho %>%
pivot_longer(cols = everything(),
names_to = "StructName",
values_to = "reho") %>%
mutate(StructName = paste0("7Networks_", StructName))

# Merge fspost_data with xcpd_reho_long, keeping all rows from both datasets
merged_data <- merge(fspost_data, xcpd_reho_long, by = "StructName")
```

### Summary

In both cases you will end up with a `merged_data` dataframe with 100 rows,
containing a `reho` column and all the surface properties.


## Using with BABS

Assuming you ran an fmriprep with `--anat-only` you can use just the
Expand Down Expand Up @@ -67,76 +148,6 @@ freesurfer-post /path/to/subjects_dir /path/to/output --subject-id sub-01 --sess
```


## Development

### Setup Development Environment

```bash
# Clone repository
git clone https://github.com/yourusername/freesurfer-post.git
cd freesurfer-post

# Install in development mode with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Code formatting and linting
ruff check . # Lint code
ruff check . --fix # Lint and auto-fix issues
ruff format . # Format code

# Type checking
mypy freesurfer_post/
```

### Code Quality

This project uses several tools to maintain code quality:

- **Ruff**: Fast linting and formatting (replaces flake8, isort, and more)
- **Black**: Code formatting (as backup/alternative to ruff format)
- **MyPy**: Static type checking
- **Pytest**: Testing framework

### Running Tests

```bash
# Run all tests
pytest

# Run with coverage
pytest --cov=freesurfer_post

# Run specific test file
pytest tests/test_core.py
```

### Pre-commit Hooks (Optional)

You can set up pre-commit hooks to automatically run linting before commits:

```bash
pip install pre-commit
# Create .pre-commit-config.yaml (see example below)
pre-commit install
```

Example `.pre-commit-config.yaml`:
```yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/psf/black
rev: 23.11.0
hooks:
- id: black
```

## License

Expand Down