pylocuszoom 0.8.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/workflows/ci.yml +3 -1
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/workflows/publish.yml +61 -8
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.pre-commit-config.yaml +1 -1
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/CHANGELOG.md +25 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/PKG-INFO +37 -13
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/README.md +36 -12
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/bioconda/meta.yaml +3 -3
- pylocuszoom-1.0.0/docs/CODEMAP.md +256 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/docs/USER_GUIDE.md +40 -22
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/eqtl_bokeh.html +5 -5
- pylocuszoom-1.0.0/examples/eqtl_overlay.png +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/eqtl_plotly.html +1 -1
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/finemapping_bokeh.html +5 -5
- pylocuszoom-1.0.0/examples/finemapping_plot.png +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/finemapping_plotly.html +1 -1
- pylocuszoom-1.0.0/examples/stacked_plot.png +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/pyproject.toml +2 -2
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/__init__.py +19 -7
- pylocuszoom-1.0.0/src/pylocuszoom/config.py +365 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/eqtl.py +3 -7
- pylocuszoom-1.0.0/src/pylocuszoom/exceptions.py +33 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/finemapping.py +2 -7
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/forest.py +1 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/gene_track.py +8 -7
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/plotter.py +193 -85
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/schemas.py +1 -6
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/utils.py +2 -4
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/validation.py +51 -0
- pylocuszoom-1.0.0/tests/test_config.py +540 -0
- pylocuszoom-1.0.0/tests/test_exceptions.py +158 -0
- pylocuszoom-1.0.0/tests/test_forest.py +111 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_gene_track.py +61 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_notebook_backends.py +66 -13
- pylocuszoom-1.0.0/tests/test_phewas.py +98 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_plotter.py +209 -17
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_validation.py +129 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/uv.lock +1 -1
- pylocuszoom-0.8.0/examples/eqtl_overlay.png +0 -0
- pylocuszoom-0.8.0/examples/finemapping_plot.png +0 -0
- pylocuszoom-0.8.0/examples/stacked_plot.png +0 -0
- pylocuszoom-0.8.0/tests/test_forest.py +0 -54
- pylocuszoom-0.8.0/tests/test_phewas.py +0 -51
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.gitattributes +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.gitignore +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/CONTRIBUTING.md +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/LICENSE.md +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/docs/ARCHITECTURE.md +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/forest_plot.png +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/generate_readme_plots.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/getting_started.ipynb +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/phewas_plot.png +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/regional_plot.png +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/logo.svg +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/__init__.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/base.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/bokeh_backend.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/hover.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/matplotlib_backend.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/plotly_backend.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/colors.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/ensembl.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/labels.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/ld.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/loaders.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/logging.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/phewas.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/py.typed +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/recombination.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/reference_data/__init__.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/conftest.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_backends.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_colors.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_ensembl.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_ensembl_integration.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_finemapping.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_hover.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_labels.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_ld.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_loaders.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_logging.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_recombination.py +0 -0
- {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_utils.py +0 -0
|
@@ -44,7 +44,9 @@ jobs:
|
|
|
44
44
|
run: uv sync --extra dev --extra all
|
|
45
45
|
|
|
46
46
|
- name: Run tests
|
|
47
|
-
|
|
47
|
+
# Verbose shows pytest-randomly seed for reproduction
|
|
48
|
+
# To reproduce: pytest --randomly-seed=<seed>
|
|
49
|
+
run: uv run pytest -v
|
|
48
50
|
|
|
49
51
|
build:
|
|
50
52
|
runs-on: ubuntu-latest
|
|
@@ -58,6 +58,9 @@ jobs:
|
|
|
58
58
|
update-bioconda:
|
|
59
59
|
needs: publish
|
|
60
60
|
runs-on: ubuntu-latest
|
|
61
|
+
permissions:
|
|
62
|
+
contents: write
|
|
63
|
+
pull-requests: write
|
|
61
64
|
steps:
|
|
62
65
|
- uses: actions/checkout@v4
|
|
63
66
|
with:
|
|
@@ -90,21 +93,71 @@ jobs:
|
|
|
90
93
|
|
|
91
94
|
cat bioconda/meta.yaml
|
|
92
95
|
|
|
93
|
-
- name: Create
|
|
96
|
+
- name: Create local PR for bioconda/meta.yaml
|
|
94
97
|
uses: peter-evans/create-pull-request@v6
|
|
95
98
|
with:
|
|
96
99
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
97
|
-
commit-message: "chore: update bioconda recipe for
|
|
100
|
+
commit-message: "chore: update bioconda recipe for v${{ needs.publish.outputs.version }}"
|
|
98
101
|
branch: bioconda-update
|
|
99
|
-
title: "Update bioconda recipe"
|
|
102
|
+
title: "Update bioconda recipe for v${{ needs.publish.outputs.version }}"
|
|
100
103
|
body: |
|
|
101
104
|
Automated update of bioconda/meta.yaml after PyPI release.
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
1. Review this PR
|
|
105
|
-
2. Merge to main
|
|
106
|
-
3. Copy `bioconda/meta.yaml` to your fork of bioconda-recipes
|
|
107
|
-
4. Submit PR to bioconda-recipes
|
|
106
|
+
This PR updates the local recipe. The bioconda-recipes PR is created automatically below.
|
|
108
107
|
labels: |
|
|
109
108
|
bioconda
|
|
110
109
|
automated
|
|
110
|
+
|
|
111
|
+
submit-bioconda-pr:
|
|
112
|
+
needs: [publish, update-bioconda]
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
steps:
|
|
115
|
+
- name: Checkout bioconda-recipes fork
|
|
116
|
+
uses: actions/checkout@v4
|
|
117
|
+
with:
|
|
118
|
+
repository: michael-denyer/bioconda-recipes
|
|
119
|
+
token: ${{ secrets.BIOCONDA_PAT }}
|
|
120
|
+
path: bioconda-recipes
|
|
121
|
+
|
|
122
|
+
- name: Checkout pyLocusZoom
|
|
123
|
+
uses: actions/checkout@v4
|
|
124
|
+
with:
|
|
125
|
+
ref: main
|
|
126
|
+
path: pylocuszoom
|
|
127
|
+
|
|
128
|
+
- name: Update recipe and create PR
|
|
129
|
+
env:
|
|
130
|
+
GH_TOKEN: ${{ secrets.BIOCONDA_PAT }}
|
|
131
|
+
PKG_VERSION: ${{ needs.publish.outputs.version }}
|
|
132
|
+
run: |
|
|
133
|
+
cd bioconda-recipes
|
|
134
|
+
|
|
135
|
+
# Sync fork with upstream
|
|
136
|
+
git remote add upstream https://github.com/bioconda/bioconda-recipes.git
|
|
137
|
+
git fetch upstream
|
|
138
|
+
git checkout master
|
|
139
|
+
git reset --hard upstream/master
|
|
140
|
+
git push origin master --force
|
|
141
|
+
|
|
142
|
+
# Create branch for this version
|
|
143
|
+
BRANCH="pylocuszoom-$PKG_VERSION"
|
|
144
|
+
git checkout -b "$BRANCH"
|
|
145
|
+
|
|
146
|
+
# Copy updated recipe
|
|
147
|
+
mkdir -p recipes/pylocuszoom
|
|
148
|
+
cp ../pylocuszoom/bioconda/meta.yaml recipes/pylocuszoom/meta.yaml
|
|
149
|
+
|
|
150
|
+
# Commit and push
|
|
151
|
+
git add recipes/pylocuszoom/meta.yaml
|
|
152
|
+
git commit -m "Update pylocuszoom to $PKG_VERSION"
|
|
153
|
+
git push -u origin "$BRANCH" --force
|
|
154
|
+
|
|
155
|
+
# Create PR to bioconda/bioconda-recipes
|
|
156
|
+
gh pr create \
|
|
157
|
+
--repo bioconda/bioconda-recipes \
|
|
158
|
+
--title "Update pylocuszoom to $PKG_VERSION" \
|
|
159
|
+
--body "Automated update of pylocuszoom to version $PKG_VERSION.
|
|
160
|
+
|
|
161
|
+
Changes: https://github.com/michael-denyer/pyLocusZoom/releases/tag/v$PKG_VERSION" \
|
|
162
|
+
--head "michael-denyer:$BRANCH" \
|
|
163
|
+
--base master || echo "PR may already exist"
|
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.0] - 2026-01-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Unified exception hierarchy with `PyLocusZoomError` base class
|
|
14
|
+
- Custom exceptions: `ValidationError`, `DownloadError`, `LiftoverError`, `DataError`, `PLINKError`, `ConfigurationError`
|
|
15
|
+
- Internal Pydantic validation for plot parameters (validates kwargs at call time)
|
|
16
|
+
- Error path tests for download failures and validation edge cases
|
|
17
|
+
- CI ordering validation for forest plots (`ci_lower <= effect <= ci_upper`)
|
|
18
|
+
- P-value validation warnings for NaN and out-of-range values
|
|
19
|
+
- Vectorized eQTL/PheWAS scatter calls for better performance
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- All validation errors now raise `ValidationError` (also a `ValueError` for backward compatibility)
|
|
23
|
+
- Test randomization enabled via pytest-randomly (visible in CI output)
|
|
24
|
+
- Config classes (`PlotConfig`, `StackedPlotConfig`) are now internal implementation details, not part of public API
|
|
25
|
+
- Capped pytest-xdist workers at 8 to prevent terminal issues
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Recombination overlay now uses correct twin axis for matplotlib (no longer distorts GWAS y-limits)
|
|
29
|
+
- Mb formatting now applied to gene track axis for interactive backends (Plotly/Bokeh)
|
|
30
|
+
- Gene track row assignment algorithm now correctly prevents overlapping genes in same row
|
|
31
|
+
- Handle all-NaN p-values in stacked plot lead SNP detection
|
|
32
|
+
- Replaced broad `except Exception` blocks with specific exception types (only 1 justified fallback remains)
|
|
33
|
+
- Download error handling now catches specific HTTP/network errors
|
|
34
|
+
|
|
10
35
|
## [0.8.0] - 2026-01-28
|
|
11
36
|
|
|
12
37
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pylocuszoom
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Publication-ready regional association plots with LD coloring, gene tracks, and recombination overlays
|
|
5
5
|
Project-URL: Homepage, https://github.com/michael-denyer/pylocuszoom
|
|
6
6
|
Project-URL: Documentation, https://github.com/michael-denyer/pylocuszoom#readme
|
|
@@ -109,15 +109,14 @@ from pylocuszoom import LocusZoomPlotter
|
|
|
109
109
|
# Initialize plotter (loads reference data for canine)
|
|
110
110
|
plotter = LocusZoomPlotter(species="canine")
|
|
111
111
|
|
|
112
|
-
#
|
|
112
|
+
# Plot with parameters passed directly
|
|
113
113
|
fig = plotter.plot(
|
|
114
|
-
gwas_df,
|
|
114
|
+
gwas_df, # DataFrame with ps, p_wald, rs columns
|
|
115
115
|
chrom=1,
|
|
116
116
|
start=1000000,
|
|
117
117
|
end=2000000,
|
|
118
|
-
lead_pos=1500000,
|
|
118
|
+
lead_pos=1500000, # Highlight lead SNP
|
|
119
119
|
)
|
|
120
|
-
|
|
121
120
|
fig.savefig("regional_plot.png", dpi=150)
|
|
122
121
|
```
|
|
123
122
|
|
|
@@ -137,9 +136,7 @@ fig = plotter.plot(
|
|
|
137
136
|
start=1000000,
|
|
138
137
|
end=2000000,
|
|
139
138
|
lead_pos=1500000,
|
|
140
|
-
ld_reference_file="genotypes
|
|
141
|
-
genes_df=genes_df, # Gene annotations
|
|
142
|
-
exons_df=exons_df, # Exon annotations
|
|
139
|
+
ld_reference_file="genotypes", # PLINK fileset (without extension)
|
|
143
140
|
show_recombination=True, # Overlay recombination rate
|
|
144
141
|
snp_labels=True, # Label top SNPs
|
|
145
142
|
label_top_n=5, # How many to label
|
|
@@ -147,6 +144,8 @@ fig = plotter.plot(
|
|
|
147
144
|
p_col="p_wald", # Column name for p-value
|
|
148
145
|
rs_col="rs", # Column name for SNP ID
|
|
149
146
|
figsize=(12, 8),
|
|
147
|
+
genes_df=genes_df, # Gene annotations
|
|
148
|
+
exons_df=exons_df, # Exon annotations
|
|
150
149
|
)
|
|
151
150
|
```
|
|
152
151
|
|
|
@@ -163,6 +162,8 @@ Recombination maps are automatically lifted over from CanFam3.1 to CanFam4 coord
|
|
|
163
162
|
## Using with Other Species
|
|
164
163
|
|
|
165
164
|
```python
|
|
165
|
+
from pylocuszoom import LocusZoomPlotter
|
|
166
|
+
|
|
166
167
|
# Feline (LD and gene tracks, user provides recombination data)
|
|
167
168
|
plotter = LocusZoomPlotter(species="feline")
|
|
168
169
|
|
|
@@ -172,10 +173,12 @@ plotter = LocusZoomPlotter(
|
|
|
172
173
|
recomb_data_dir="/path/to/recomb_maps/",
|
|
173
174
|
)
|
|
174
175
|
|
|
175
|
-
#
|
|
176
|
+
# Provide data per-plot
|
|
176
177
|
fig = plotter.plot(
|
|
177
178
|
gwas_df,
|
|
178
|
-
chrom=1,
|
|
179
|
+
chrom=1,
|
|
180
|
+
start=1000000,
|
|
181
|
+
end=2000000,
|
|
179
182
|
recomb_df=my_recomb_dataframe,
|
|
180
183
|
genes_df=my_genes_df,
|
|
181
184
|
)
|
|
@@ -186,6 +189,8 @@ fig = plotter.plot(
|
|
|
186
189
|
pyLocusZoom can automatically fetch gene annotations from Ensembl for any species:
|
|
187
190
|
|
|
188
191
|
```python
|
|
192
|
+
from pylocuszoom import LocusZoomPlotter
|
|
193
|
+
|
|
189
194
|
# Enable automatic gene fetching
|
|
190
195
|
plotter = LocusZoomPlotter(species="human", auto_genes=True)
|
|
191
196
|
|
|
@@ -201,6 +206,8 @@ Data is cached locally for fast subsequent plots. Maximum region size is 5Mb (En
|
|
|
201
206
|
pyLocusZoom supports multiple rendering backends (set at initialization):
|
|
202
207
|
|
|
203
208
|
```python
|
|
209
|
+
from pylocuszoom import LocusZoomPlotter
|
|
210
|
+
|
|
204
211
|
# Static publication-quality plot (default)
|
|
205
212
|
plotter = LocusZoomPlotter(species="canine", backend="matplotlib")
|
|
206
213
|
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
@@ -229,6 +236,10 @@ fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
|
229
236
|
Compare multiple GWAS results vertically with shared x-axis:
|
|
230
237
|
|
|
231
238
|
```python
|
|
239
|
+
from pylocuszoom import LocusZoomPlotter
|
|
240
|
+
|
|
241
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
242
|
+
|
|
232
243
|
fig = plotter.plot_stacked(
|
|
233
244
|
[gwas_height, gwas_bmi, gwas_whr],
|
|
234
245
|
chrom=1,
|
|
@@ -247,15 +258,21 @@ fig = plotter.plot_stacked(
|
|
|
247
258
|
Add expression QTL data as a separate panel:
|
|
248
259
|
|
|
249
260
|
```python
|
|
261
|
+
from pylocuszoom import LocusZoomPlotter
|
|
262
|
+
|
|
250
263
|
eqtl_df = pd.DataFrame({
|
|
251
264
|
"pos": [1000500, 1001200, 1002000],
|
|
252
265
|
"p_value": [1e-6, 1e-4, 0.01],
|
|
253
266
|
"gene": ["BRCA1", "BRCA1", "BRCA1"],
|
|
254
267
|
})
|
|
255
268
|
|
|
269
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
270
|
+
|
|
256
271
|
fig = plotter.plot_stacked(
|
|
257
272
|
[gwas_df],
|
|
258
|
-
chrom=1,
|
|
273
|
+
chrom=1,
|
|
274
|
+
start=1000000,
|
|
275
|
+
end=2000000,
|
|
259
276
|
eqtl_df=eqtl_df,
|
|
260
277
|
eqtl_gene="BRCA1",
|
|
261
278
|
genes_df=genes_df,
|
|
@@ -270,15 +287,21 @@ fig = plotter.plot_stacked(
|
|
|
270
287
|
Visualize SuSiE or other fine-mapping results with credible set coloring:
|
|
271
288
|
|
|
272
289
|
```python
|
|
290
|
+
from pylocuszoom import LocusZoomPlotter
|
|
291
|
+
|
|
273
292
|
finemapping_df = pd.DataFrame({
|
|
274
293
|
"pos": [1000500, 1001200, 1002000, 1003500],
|
|
275
294
|
"pip": [0.85, 0.12, 0.02, 0.45], # Posterior inclusion probability
|
|
276
295
|
"cs": [1, 1, 0, 2], # Credible set assignment (0 = not in CS)
|
|
277
296
|
})
|
|
278
297
|
|
|
298
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
299
|
+
|
|
279
300
|
fig = plotter.plot_stacked(
|
|
280
301
|
[gwas_df],
|
|
281
|
-
chrom=1,
|
|
302
|
+
chrom=1,
|
|
303
|
+
start=1000000,
|
|
304
|
+
end=2000000,
|
|
282
305
|
finemapping_df=finemapping_df,
|
|
283
306
|
finemapping_cs_col="cs",
|
|
284
307
|
genes_df=genes_df,
|
|
@@ -414,7 +437,7 @@ gwas_df = pd.DataFrame({
|
|
|
414
437
|
|--------|------|----------|-------------|
|
|
415
438
|
| `chr` | str or int | Yes | Chromosome identifier. Accepts "1", "chr1", or 1. The "chr" prefix is stripped for matching. |
|
|
416
439
|
| `start` | int | Yes | Gene start position (bp, 1-based). Transcript start for strand-aware genes. |
|
|
417
|
-
| `end` | int | Yes | Gene end position (bp, 1-based). Must be
|
|
440
|
+
| `end` | int | Yes | Gene end position (bp, 1-based). Must be >= start. |
|
|
418
441
|
| `gene_name` | str | Yes | Gene symbol displayed in track (e.g., "BRCA1", "TP53"). Keep short for readability. |
|
|
419
442
|
|
|
420
443
|
Example:
|
|
@@ -516,6 +539,7 @@ Optional:
|
|
|
516
539
|
## Documentation
|
|
517
540
|
|
|
518
541
|
- [User Guide](docs/USER_GUIDE.md) - Comprehensive documentation with API reference
|
|
542
|
+
- [Code Map](docs/CODEMAP.md) - Architecture diagram with source code links
|
|
519
543
|
- [Architecture](docs/ARCHITECTURE.md) - Design decisions and component overview
|
|
520
544
|
- [Example Notebook](examples/getting_started.ipynb) - Interactive tutorial
|
|
521
545
|
- [CHANGELOG](CHANGELOG.md) - Version history
|
|
@@ -64,15 +64,14 @@ from pylocuszoom import LocusZoomPlotter
|
|
|
64
64
|
# Initialize plotter (loads reference data for canine)
|
|
65
65
|
plotter = LocusZoomPlotter(species="canine")
|
|
66
66
|
|
|
67
|
-
#
|
|
67
|
+
# Plot with parameters passed directly
|
|
68
68
|
fig = plotter.plot(
|
|
69
|
-
gwas_df,
|
|
69
|
+
gwas_df, # DataFrame with ps, p_wald, rs columns
|
|
70
70
|
chrom=1,
|
|
71
71
|
start=1000000,
|
|
72
72
|
end=2000000,
|
|
73
|
-
lead_pos=1500000,
|
|
73
|
+
lead_pos=1500000, # Highlight lead SNP
|
|
74
74
|
)
|
|
75
|
-
|
|
76
75
|
fig.savefig("regional_plot.png", dpi=150)
|
|
77
76
|
```
|
|
78
77
|
|
|
@@ -92,9 +91,7 @@ fig = plotter.plot(
|
|
|
92
91
|
start=1000000,
|
|
93
92
|
end=2000000,
|
|
94
93
|
lead_pos=1500000,
|
|
95
|
-
ld_reference_file="genotypes
|
|
96
|
-
genes_df=genes_df, # Gene annotations
|
|
97
|
-
exons_df=exons_df, # Exon annotations
|
|
94
|
+
ld_reference_file="genotypes", # PLINK fileset (without extension)
|
|
98
95
|
show_recombination=True, # Overlay recombination rate
|
|
99
96
|
snp_labels=True, # Label top SNPs
|
|
100
97
|
label_top_n=5, # How many to label
|
|
@@ -102,6 +99,8 @@ fig = plotter.plot(
|
|
|
102
99
|
p_col="p_wald", # Column name for p-value
|
|
103
100
|
rs_col="rs", # Column name for SNP ID
|
|
104
101
|
figsize=(12, 8),
|
|
102
|
+
genes_df=genes_df, # Gene annotations
|
|
103
|
+
exons_df=exons_df, # Exon annotations
|
|
105
104
|
)
|
|
106
105
|
```
|
|
107
106
|
|
|
@@ -118,6 +117,8 @@ Recombination maps are automatically lifted over from CanFam3.1 to CanFam4 coord
|
|
|
118
117
|
## Using with Other Species
|
|
119
118
|
|
|
120
119
|
```python
|
|
120
|
+
from pylocuszoom import LocusZoomPlotter
|
|
121
|
+
|
|
121
122
|
# Feline (LD and gene tracks, user provides recombination data)
|
|
122
123
|
plotter = LocusZoomPlotter(species="feline")
|
|
123
124
|
|
|
@@ -127,10 +128,12 @@ plotter = LocusZoomPlotter(
|
|
|
127
128
|
recomb_data_dir="/path/to/recomb_maps/",
|
|
128
129
|
)
|
|
129
130
|
|
|
130
|
-
#
|
|
131
|
+
# Provide data per-plot
|
|
131
132
|
fig = plotter.plot(
|
|
132
133
|
gwas_df,
|
|
133
|
-
chrom=1,
|
|
134
|
+
chrom=1,
|
|
135
|
+
start=1000000,
|
|
136
|
+
end=2000000,
|
|
134
137
|
recomb_df=my_recomb_dataframe,
|
|
135
138
|
genes_df=my_genes_df,
|
|
136
139
|
)
|
|
@@ -141,6 +144,8 @@ fig = plotter.plot(
|
|
|
141
144
|
pyLocusZoom can automatically fetch gene annotations from Ensembl for any species:
|
|
142
145
|
|
|
143
146
|
```python
|
|
147
|
+
from pylocuszoom import LocusZoomPlotter
|
|
148
|
+
|
|
144
149
|
# Enable automatic gene fetching
|
|
145
150
|
plotter = LocusZoomPlotter(species="human", auto_genes=True)
|
|
146
151
|
|
|
@@ -156,6 +161,8 @@ Data is cached locally for fast subsequent plots. Maximum region size is 5Mb (En
|
|
|
156
161
|
pyLocusZoom supports multiple rendering backends (set at initialization):
|
|
157
162
|
|
|
158
163
|
```python
|
|
164
|
+
from pylocuszoom import LocusZoomPlotter
|
|
165
|
+
|
|
159
166
|
# Static publication-quality plot (default)
|
|
160
167
|
plotter = LocusZoomPlotter(species="canine", backend="matplotlib")
|
|
161
168
|
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
@@ -184,6 +191,10 @@ fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
|
184
191
|
Compare multiple GWAS results vertically with shared x-axis:
|
|
185
192
|
|
|
186
193
|
```python
|
|
194
|
+
from pylocuszoom import LocusZoomPlotter
|
|
195
|
+
|
|
196
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
197
|
+
|
|
187
198
|
fig = plotter.plot_stacked(
|
|
188
199
|
[gwas_height, gwas_bmi, gwas_whr],
|
|
189
200
|
chrom=1,
|
|
@@ -202,15 +213,21 @@ fig = plotter.plot_stacked(
|
|
|
202
213
|
Add expression QTL data as a separate panel:
|
|
203
214
|
|
|
204
215
|
```python
|
|
216
|
+
from pylocuszoom import LocusZoomPlotter
|
|
217
|
+
|
|
205
218
|
eqtl_df = pd.DataFrame({
|
|
206
219
|
"pos": [1000500, 1001200, 1002000],
|
|
207
220
|
"p_value": [1e-6, 1e-4, 0.01],
|
|
208
221
|
"gene": ["BRCA1", "BRCA1", "BRCA1"],
|
|
209
222
|
})
|
|
210
223
|
|
|
224
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
225
|
+
|
|
211
226
|
fig = plotter.plot_stacked(
|
|
212
227
|
[gwas_df],
|
|
213
|
-
chrom=1,
|
|
228
|
+
chrom=1,
|
|
229
|
+
start=1000000,
|
|
230
|
+
end=2000000,
|
|
214
231
|
eqtl_df=eqtl_df,
|
|
215
232
|
eqtl_gene="BRCA1",
|
|
216
233
|
genes_df=genes_df,
|
|
@@ -225,15 +242,21 @@ fig = plotter.plot_stacked(
|
|
|
225
242
|
Visualize SuSiE or other fine-mapping results with credible set coloring:
|
|
226
243
|
|
|
227
244
|
```python
|
|
245
|
+
from pylocuszoom import LocusZoomPlotter
|
|
246
|
+
|
|
228
247
|
finemapping_df = pd.DataFrame({
|
|
229
248
|
"pos": [1000500, 1001200, 1002000, 1003500],
|
|
230
249
|
"pip": [0.85, 0.12, 0.02, 0.45], # Posterior inclusion probability
|
|
231
250
|
"cs": [1, 1, 0, 2], # Credible set assignment (0 = not in CS)
|
|
232
251
|
})
|
|
233
252
|
|
|
253
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
254
|
+
|
|
234
255
|
fig = plotter.plot_stacked(
|
|
235
256
|
[gwas_df],
|
|
236
|
-
chrom=1,
|
|
257
|
+
chrom=1,
|
|
258
|
+
start=1000000,
|
|
259
|
+
end=2000000,
|
|
237
260
|
finemapping_df=finemapping_df,
|
|
238
261
|
finemapping_cs_col="cs",
|
|
239
262
|
genes_df=genes_df,
|
|
@@ -369,7 +392,7 @@ gwas_df = pd.DataFrame({
|
|
|
369
392
|
|--------|------|----------|-------------|
|
|
370
393
|
| `chr` | str or int | Yes | Chromosome identifier. Accepts "1", "chr1", or 1. The "chr" prefix is stripped for matching. |
|
|
371
394
|
| `start` | int | Yes | Gene start position (bp, 1-based). Transcript start for strand-aware genes. |
|
|
372
|
-
| `end` | int | Yes | Gene end position (bp, 1-based). Must be
|
|
395
|
+
| `end` | int | Yes | Gene end position (bp, 1-based). Must be >= start. |
|
|
373
396
|
| `gene_name` | str | Yes | Gene symbol displayed in track (e.g., "BRCA1", "TP53"). Keep short for readability. |
|
|
374
397
|
|
|
375
398
|
Example:
|
|
@@ -471,6 +494,7 @@ Optional:
|
|
|
471
494
|
## Documentation
|
|
472
495
|
|
|
473
496
|
- [User Guide](docs/USER_GUIDE.md) - Comprehensive documentation with API reference
|
|
497
|
+
- [Code Map](docs/CODEMAP.md) - Architecture diagram with source code links
|
|
474
498
|
- [Architecture](docs/ARCHITECTURE.md) - Design decisions and component overview
|
|
475
499
|
- [Example Notebook](examples/getting_started.ipynb) - Interactive tutorial
|
|
476
500
|
- [CHANGELOG](CHANGELOG.md) - Version history
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% set name = "pylocuszoom" %}
|
|
2
|
-
{% set version = "0.
|
|
2
|
+
{% set version = "0.8.0" %}
|
|
3
3
|
|
|
4
4
|
package:
|
|
5
5
|
name: {{ name|lower }}
|
|
@@ -7,7 +7,7 @@ package:
|
|
|
7
7
|
|
|
8
8
|
source:
|
|
9
9
|
url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz
|
|
10
|
-
sha256:
|
|
10
|
+
sha256: cfca452d1dd34c0f6f8c837ef6b45a274bdd9d7e9ab248d67220b4aa830fed06
|
|
11
11
|
|
|
12
12
|
build:
|
|
13
13
|
noarch: python
|
|
@@ -46,7 +46,7 @@ about:
|
|
|
46
46
|
home: https://github.com/michael-denyer/pylocuszoom
|
|
47
47
|
license: GPL-3.0-or-later
|
|
48
48
|
license_family: GPL3
|
|
49
|
-
license_file: LICENSE
|
|
49
|
+
license_file: LICENSE.md
|
|
50
50
|
summary: Publication-ready GWAS visualization library with regional association plots, gene tracks, eQTL, PheWAS, fine-mapping, and forest plots
|
|
51
51
|
description: |
|
|
52
52
|
pyLocusZoom creates publication-quality genetic association visualizations:
|