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.
Files changed (85) hide show
  1. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/workflows/ci.yml +3 -1
  2. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/workflows/publish.yml +61 -8
  3. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.pre-commit-config.yaml +1 -1
  4. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/CHANGELOG.md +25 -0
  5. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/PKG-INFO +37 -13
  6. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/README.md +36 -12
  7. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/bioconda/meta.yaml +3 -3
  8. pylocuszoom-1.0.0/docs/CODEMAP.md +256 -0
  9. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/docs/USER_GUIDE.md +40 -22
  10. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/eqtl_bokeh.html +5 -5
  11. pylocuszoom-1.0.0/examples/eqtl_overlay.png +0 -0
  12. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/eqtl_plotly.html +1 -1
  13. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/finemapping_bokeh.html +5 -5
  14. pylocuszoom-1.0.0/examples/finemapping_plot.png +0 -0
  15. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/finemapping_plotly.html +1 -1
  16. pylocuszoom-1.0.0/examples/stacked_plot.png +0 -0
  17. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/pyproject.toml +2 -2
  18. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/__init__.py +19 -7
  19. pylocuszoom-1.0.0/src/pylocuszoom/config.py +365 -0
  20. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/eqtl.py +3 -7
  21. pylocuszoom-1.0.0/src/pylocuszoom/exceptions.py +33 -0
  22. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/finemapping.py +2 -7
  23. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/forest.py +1 -0
  24. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/gene_track.py +8 -7
  25. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/plotter.py +193 -85
  26. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/schemas.py +1 -6
  27. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/utils.py +2 -4
  28. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/validation.py +51 -0
  29. pylocuszoom-1.0.0/tests/test_config.py +540 -0
  30. pylocuszoom-1.0.0/tests/test_exceptions.py +158 -0
  31. pylocuszoom-1.0.0/tests/test_forest.py +111 -0
  32. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_gene_track.py +61 -0
  33. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_notebook_backends.py +66 -13
  34. pylocuszoom-1.0.0/tests/test_phewas.py +98 -0
  35. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_plotter.py +209 -17
  36. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_validation.py +129 -0
  37. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/uv.lock +1 -1
  38. pylocuszoom-0.8.0/examples/eqtl_overlay.png +0 -0
  39. pylocuszoom-0.8.0/examples/finemapping_plot.png +0 -0
  40. pylocuszoom-0.8.0/examples/stacked_plot.png +0 -0
  41. pylocuszoom-0.8.0/tests/test_forest.py +0 -54
  42. pylocuszoom-0.8.0/tests/test_phewas.py +0 -51
  43. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.gitattributes +0 -0
  44. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  45. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  46. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  47. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/.gitignore +0 -0
  48. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/CONTRIBUTING.md +0 -0
  49. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/LICENSE.md +0 -0
  50. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/docs/ARCHITECTURE.md +0 -0
  51. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/forest_plot.png +0 -0
  52. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/generate_readme_plots.py +0 -0
  53. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/getting_started.ipynb +0 -0
  54. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/phewas_plot.png +0 -0
  55. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/examples/regional_plot.png +0 -0
  56. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/logo.svg +0 -0
  57. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/__init__.py +0 -0
  58. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/base.py +0 -0
  59. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/bokeh_backend.py +0 -0
  60. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/hover.py +0 -0
  61. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/matplotlib_backend.py +0 -0
  62. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/backends/plotly_backend.py +0 -0
  63. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/colors.py +0 -0
  64. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/ensembl.py +0 -0
  65. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/labels.py +0 -0
  66. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/ld.py +0 -0
  67. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/loaders.py +0 -0
  68. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/logging.py +0 -0
  69. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/phewas.py +0 -0
  70. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/py.typed +0 -0
  71. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/recombination.py +0 -0
  72. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/src/pylocuszoom/reference_data/__init__.py +0 -0
  73. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/conftest.py +0 -0
  74. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_backends.py +0 -0
  75. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_colors.py +0 -0
  76. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_ensembl.py +0 -0
  77. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_ensembl_integration.py +0 -0
  78. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_finemapping.py +0 -0
  79. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_hover.py +0 -0
  80. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_labels.py +0 -0
  81. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_ld.py +0 -0
  82. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_loaders.py +0 -0
  83. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_logging.py +0 -0
  84. {pylocuszoom-0.8.0 → pylocuszoom-1.0.0}/tests/test_recombination.py +0 -0
  85. {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
- run: uv run pytest --cov=pylocuszoom --cov-report=term-missing
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 Pull Request
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 new release"
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
- **Next steps:**
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"
@@ -10,7 +10,7 @@ repos:
10
10
  hooks:
11
11
  - id: pytest-cov
12
12
  name: pytest with coverage
13
- entry: uv run python -m pytest -q
13
+ entry: uv run python -m pytest -n auto -q
14
14
  language: system
15
15
  types: [python]
16
16
  pass_filenames: false
@@ -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.8.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
- # Create regional plot
112
+ # Plot with parameters passed directly
113
113
  fig = plotter.plot(
114
- gwas_df, # DataFrame with ps, p_wald, rs columns
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, # Highlight lead SNP
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.bed", # For LD calculation
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
- # Or provide data per-plot
176
+ # Provide data per-plot
176
177
  fig = plotter.plot(
177
178
  gwas_df,
178
- chrom=1, start=1000000, end=2000000,
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, start=1000000, end=2000000,
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, start=1000000, end=2000000,
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 start. |
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
- # Create regional plot
67
+ # Plot with parameters passed directly
68
68
  fig = plotter.plot(
69
- gwas_df, # DataFrame with ps, p_wald, rs columns
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, # Highlight lead SNP
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.bed", # For LD calculation
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
- # Or provide data per-plot
131
+ # Provide data per-plot
131
132
  fig = plotter.plot(
132
133
  gwas_df,
133
- chrom=1, start=1000000, end=2000000,
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, start=1000000, end=2000000,
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, start=1000000, end=2000000,
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 start. |
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.6.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: 1597c2080e2e32019293aab67836d1b944c1cc0b2fe3e7cd94e82d9119175742
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: