pylocuszoom 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
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/__init__.py +39 -20
- pylocuszoom/backends/__init__.py +1 -5
- pylocuszoom/backends/base.py +3 -1
- pylocuszoom/backends/bokeh_backend.py +220 -51
- pylocuszoom/backends/matplotlib_backend.py +35 -8
- pylocuszoom/backends/plotly_backend.py +273 -32
- pylocuszoom/colors.py +132 -0
- pylocuszoom/eqtl.py +3 -2
- pylocuszoom/finemapping.py +223 -0
- pylocuszoom/gene_track.py +259 -38
- pylocuszoom/labels.py +32 -33
- pylocuszoom/ld.py +8 -7
- pylocuszoom/plotter.py +615 -162
- pylocuszoom/recombination.py +14 -14
- pylocuszoom/utils.py +3 -1
- {pylocuszoom-0.1.0.dist-info → pylocuszoom-0.3.0.dist-info}/METADATA +36 -27
- pylocuszoom-0.3.0.dist-info/RECORD +21 -0
- pylocuszoom-0.1.0.dist-info/RECORD +0 -20
- {pylocuszoom-0.1.0.dist-info → pylocuszoom-0.3.0.dist-info}/WHEEL +0 -0
- {pylocuszoom-0.1.0.dist-info → pylocuszoom-0.3.0.dist-info}/licenses/LICENSE.md +0 -0
pylocuszoom/recombination.py
CHANGED
|
@@ -22,7 +22,7 @@ from .logging import logger
|
|
|
22
22
|
RECOMB_COLOR = "#7FCDFF" # Light blue
|
|
23
23
|
|
|
24
24
|
# Data sources by species
|
|
25
|
-
|
|
25
|
+
CANINE_RECOMB_URL = (
|
|
26
26
|
"https://github.com/cflerin/dog_recombination/raw/master/dog_genetic_maps.tar.gz"
|
|
27
27
|
)
|
|
28
28
|
|
|
@@ -177,11 +177,11 @@ def get_default_data_dir() -> Path:
|
|
|
177
177
|
return base / "snp-scope-plot" / "recombination_maps"
|
|
178
178
|
|
|
179
179
|
|
|
180
|
-
def
|
|
180
|
+
def download_canine_recombination_maps(
|
|
181
181
|
output_dir: Optional[str] = None,
|
|
182
182
|
force: bool = False,
|
|
183
183
|
) -> Path:
|
|
184
|
-
"""Download
|
|
184
|
+
"""Download canine recombination rate maps from Campbell et al. 2016.
|
|
185
185
|
|
|
186
186
|
Downloads from: https://github.com/cflerin/dog_recombination
|
|
187
187
|
|
|
@@ -213,22 +213,22 @@ def download_dog_recombination_maps(
|
|
|
213
213
|
# Create output directory
|
|
214
214
|
output_path.mkdir(parents=True, exist_ok=True)
|
|
215
215
|
|
|
216
|
-
logger.info("Downloading
|
|
217
|
-
logger.debug(f"Source: {
|
|
216
|
+
logger.info("Downloading canine recombination maps from GitHub...")
|
|
217
|
+
logger.debug(f"Source: {CANINE_RECOMB_URL}")
|
|
218
218
|
|
|
219
219
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
220
220
|
# Download tar.gz file
|
|
221
221
|
tar_path = Path(tmpdir) / "dog_genetic_maps.tar.gz"
|
|
222
222
|
|
|
223
223
|
try:
|
|
224
|
-
urllib.request.urlretrieve(
|
|
224
|
+
urllib.request.urlretrieve(CANINE_RECOMB_URL, tar_path)
|
|
225
225
|
except Exception as e:
|
|
226
226
|
logger.debug(f"urllib download failed: {e}")
|
|
227
227
|
logger.debug("Trying alternative method with requests...")
|
|
228
228
|
try:
|
|
229
229
|
import requests
|
|
230
230
|
|
|
231
|
-
response = requests.get(
|
|
231
|
+
response = requests.get(CANINE_RECOMB_URL, timeout=60)
|
|
232
232
|
response.raise_for_status()
|
|
233
233
|
tar_path.write_bytes(response.content)
|
|
234
234
|
except ImportError:
|
|
@@ -282,14 +282,14 @@ def download_dog_recombination_maps(
|
|
|
282
282
|
|
|
283
283
|
def load_recombination_map(
|
|
284
284
|
chrom: int,
|
|
285
|
-
species: str = "
|
|
285
|
+
species: str = "canine",
|
|
286
286
|
data_dir: Optional[str] = None,
|
|
287
287
|
) -> pd.DataFrame:
|
|
288
288
|
"""Load recombination map for a specific chromosome.
|
|
289
289
|
|
|
290
290
|
Args:
|
|
291
|
-
chrom: Chromosome number (1-38 for
|
|
292
|
-
species: Species name ('
|
|
291
|
+
chrom: Chromosome number (1-38 for canine, 1-18 for feline) or 'X'.
|
|
292
|
+
species: Species name ('canine', 'feline').
|
|
293
293
|
data_dir: Directory containing recombination maps.
|
|
294
294
|
|
|
295
295
|
Returns:
|
|
@@ -326,7 +326,7 @@ def get_recombination_rate_for_region(
|
|
|
326
326
|
chrom: int,
|
|
327
327
|
start: int,
|
|
328
328
|
end: int,
|
|
329
|
-
species: str = "
|
|
329
|
+
species: str = "canine",
|
|
330
330
|
data_dir: Optional[str] = None,
|
|
331
331
|
genome_build: Optional[str] = None,
|
|
332
332
|
) -> pd.DataFrame:
|
|
@@ -336,7 +336,7 @@ def get_recombination_rate_for_region(
|
|
|
336
336
|
chrom: Chromosome number.
|
|
337
337
|
start: Start position (bp).
|
|
338
338
|
end: End position (bp).
|
|
339
|
-
species: Species name ('
|
|
339
|
+
species: Species name ('canine', 'feline').
|
|
340
340
|
data_dir: Directory containing recombination maps.
|
|
341
341
|
genome_build: Target genome build (e.g., "canfam4"). If specified and
|
|
342
342
|
different from source data (CanFam3.1), coordinates are lifted over.
|
|
@@ -345,7 +345,7 @@ def get_recombination_rate_for_region(
|
|
|
345
345
|
DataFrame with pos and rate columns for the region.
|
|
346
346
|
|
|
347
347
|
Note:
|
|
348
|
-
Built-in
|
|
348
|
+
Built-in canine recombination maps are in CanFam3.1 coordinates.
|
|
349
349
|
If genome_build="canfam4", positions are automatically lifted over.
|
|
350
350
|
This requires pyliftover: pip install pyliftover
|
|
351
351
|
"""
|
|
@@ -353,7 +353,7 @@ def get_recombination_rate_for_region(
|
|
|
353
353
|
|
|
354
354
|
# Liftover if needed
|
|
355
355
|
build = _normalize_build(genome_build)
|
|
356
|
-
if species == "
|
|
356
|
+
if species == "canine" and build == "canfam4":
|
|
357
357
|
logger.debug(f"Lifting over recombination map for chr{chrom} to CanFam4")
|
|
358
358
|
df = liftover_recombination_map(
|
|
359
359
|
df, from_build="canfam3", to_build="canfam4", chrom=chrom
|
pylocuszoom/utils.py
CHANGED
|
@@ -29,7 +29,9 @@ def is_spark_dataframe(df: Any) -> bool:
|
|
|
29
29
|
True if PySpark DataFrame, False otherwise.
|
|
30
30
|
"""
|
|
31
31
|
# Check class name to avoid importing pyspark
|
|
32
|
-
return type(df).__name__ == "DataFrame" and type(df).__module__.startswith(
|
|
32
|
+
return type(df).__name__ == "DataFrame" and type(df).__module__.startswith(
|
|
33
|
+
"pyspark"
|
|
34
|
+
)
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def to_pandas(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pylocuszoom
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Regional association plots for GWAS results with LD coloring, gene tracks, and recombination rate overlays
|
|
5
5
|
Project-URL: Homepage, https://github.com/michael-denyer/pylocuszoom
|
|
6
6
|
Project-URL: Documentation, https://github.com/michael-denyer/pylocuszoom#readme
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
19
|
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: adjusttext>=0.8
|
|
22
23
|
Requires-Dist: bokeh>=3.8.2
|
|
23
24
|
Requires-Dist: kaleido>=0.2.0
|
|
24
25
|
Requires-Dist: loguru>=0.7.0
|
|
@@ -28,14 +29,11 @@ Requires-Dist: pandas>=1.4.0
|
|
|
28
29
|
Requires-Dist: plotly>=5.0.0
|
|
29
30
|
Requires-Dist: pyliftover>=0.4
|
|
30
31
|
Provides-Extra: all
|
|
31
|
-
Requires-Dist: adjusttext>=0.8; extra == 'all'
|
|
32
32
|
Requires-Dist: pyspark>=3.0.0; extra == 'all'
|
|
33
33
|
Provides-Extra: dev
|
|
34
34
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
35
35
|
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
36
36
|
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
37
|
-
Provides-Extra: labels
|
|
38
|
-
Requires-Dist: adjusttext>=0.8; extra == 'labels'
|
|
39
37
|
Provides-Extra: spark
|
|
40
38
|
Requires-Dist: pyspark>=3.0.0; extra == 'spark'
|
|
41
39
|
Description-Content-Type: text/markdown
|
|
@@ -71,16 +69,24 @@ Inspired by [LocusZoom](http://locuszoom.org/) and [locuszoomr](https://github.c
|
|
|
71
69
|
- **eQTL overlay**: Expression QTL data as separate panel
|
|
72
70
|
- **PySpark support**: Handles large-scale genomics DataFrames
|
|
73
71
|
|
|
72
|
+

|
|
73
|
+
|
|
74
74
|
## Installation
|
|
75
75
|
|
|
76
|
+
```bash
|
|
77
|
+
pip install pylocuszoom
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or with uv:
|
|
81
|
+
|
|
76
82
|
```bash
|
|
77
83
|
uv add pylocuszoom
|
|
78
84
|
```
|
|
79
85
|
|
|
80
|
-
Or with
|
|
86
|
+
Or with conda (Bioconda):
|
|
81
87
|
|
|
82
88
|
```bash
|
|
83
|
-
|
|
89
|
+
conda install -c bioconda pylocuszoom
|
|
84
90
|
```
|
|
85
91
|
|
|
86
92
|
## Quick Start
|
|
@@ -88,8 +94,8 @@ pip install pylocuszoom
|
|
|
88
94
|
```python
|
|
89
95
|
from pylocuszoom import LocusZoomPlotter
|
|
90
96
|
|
|
91
|
-
# Initialize plotter (loads reference data for
|
|
92
|
-
plotter = LocusZoomPlotter(species="
|
|
97
|
+
# Initialize plotter (loads reference data for canine)
|
|
98
|
+
plotter = LocusZoomPlotter(species="canine")
|
|
93
99
|
|
|
94
100
|
# Create regional plot
|
|
95
101
|
fig = plotter.plot(
|
|
@@ -109,7 +115,7 @@ fig.savefig("regional_plot.png", dpi=150)
|
|
|
109
115
|
from pylocuszoom import LocusZoomPlotter
|
|
110
116
|
|
|
111
117
|
plotter = LocusZoomPlotter(
|
|
112
|
-
species="
|
|
118
|
+
species="canine", # or "feline", or None for custom
|
|
113
119
|
plink_path="/path/to/plink", # Optional, auto-detects if on PATH
|
|
114
120
|
)
|
|
115
121
|
|
|
@@ -134,10 +140,10 @@ fig = plotter.plot(
|
|
|
134
140
|
|
|
135
141
|
## Genome Builds
|
|
136
142
|
|
|
137
|
-
The default genome build for
|
|
143
|
+
The default genome build for canine is CanFam3.1. For CanFam4 data:
|
|
138
144
|
|
|
139
145
|
```python
|
|
140
|
-
plotter = LocusZoomPlotter(species="
|
|
146
|
+
plotter = LocusZoomPlotter(species="canine", genome_build="canfam4")
|
|
141
147
|
```
|
|
142
148
|
|
|
143
149
|
Recombination maps are automatically lifted over from CanFam3.1 to CanFam4 coordinates using the UCSC liftOver chain file.
|
|
@@ -145,8 +151,8 @@ Recombination maps are automatically lifted over from CanFam3.1 to CanFam4 coord
|
|
|
145
151
|
## Using with Other Species
|
|
146
152
|
|
|
147
153
|
```python
|
|
148
|
-
#
|
|
149
|
-
plotter = LocusZoomPlotter(species="
|
|
154
|
+
# Feline (LD and gene tracks, user provides recombination data)
|
|
155
|
+
plotter = LocusZoomPlotter(species="feline")
|
|
150
156
|
|
|
151
157
|
# Custom species (provide all reference data)
|
|
152
158
|
plotter = LocusZoomPlotter(
|
|
@@ -163,27 +169,30 @@ fig = plotter.plot(
|
|
|
163
169
|
)
|
|
164
170
|
```
|
|
165
171
|
|
|
166
|
-
##
|
|
172
|
+
## Backends
|
|
167
173
|
|
|
168
|
-
|
|
174
|
+
pyLocusZoom supports multiple rendering backends:
|
|
169
175
|
|
|
170
176
|
```python
|
|
171
177
|
# Static publication-quality plot (default)
|
|
172
|
-
|
|
173
|
-
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
178
|
+
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="matplotlib")
|
|
174
179
|
fig.savefig("plot.png", dpi=150)
|
|
175
180
|
|
|
176
|
-
# Interactive
|
|
177
|
-
|
|
178
|
-
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
181
|
+
# Interactive Plotly (hover tooltips, pan/zoom)
|
|
182
|
+
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="plotly")
|
|
179
183
|
fig.write_html("plot.html")
|
|
180
184
|
|
|
181
|
-
# Interactive
|
|
182
|
-
|
|
183
|
-
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000)
|
|
185
|
+
# Interactive Bokeh (dashboard-ready)
|
|
186
|
+
fig = plotter.plot(gwas_df, chrom=1, start=1000000, end=2000000, backend="bokeh")
|
|
184
187
|
```
|
|
185
188
|
|
|
186
|
-
|
|
189
|
+
| Backend | Output | Best For |
|
|
190
|
+
|---------|--------|----------|
|
|
191
|
+
| `matplotlib` | Static PNG/PDF/SVG | Publications, presentations |
|
|
192
|
+
| `plotly` | Interactive HTML | Web reports, data exploration |
|
|
193
|
+
| `bokeh` | Interactive HTML | Dashboards, web apps |
|
|
194
|
+
|
|
195
|
+
> **Note:** All backends support gene track, recombination overlay, and LD legend. SNP labels (auto-positioned with adjustText) are matplotlib-only.
|
|
187
196
|
|
|
188
197
|
## Stacked Plots
|
|
189
198
|
|
|
@@ -324,14 +333,14 @@ chr pos rate cM
|
|
|
324
333
|
|
|
325
334
|
## Reference Data
|
|
326
335
|
|
|
327
|
-
|
|
336
|
+
Canine recombination maps are downloaded from [Campbell et al. 2016](https://github.com/cflerin/dog_recombination) on first use.
|
|
328
337
|
|
|
329
338
|
To manually download:
|
|
330
339
|
|
|
331
340
|
```python
|
|
332
|
-
from pylocuszoom import
|
|
341
|
+
from pylocuszoom import download_canine_recombination_maps
|
|
333
342
|
|
|
334
|
-
|
|
343
|
+
download_canine_recombination_maps()
|
|
335
344
|
```
|
|
336
345
|
|
|
337
346
|
## Logging
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
pylocuszoom/__init__.py,sha256=f8h22YYL3JkDP5P_dJftu98qlJkXvaAeyto5kVc8bzU,3785
|
|
2
|
+
pylocuszoom/colors.py,sha256=XXTCmCFfHrOrSiP6b0V8Kis7Z1tyvGEKJpdv59QDVQ8,6610
|
|
3
|
+
pylocuszoom/eqtl.py,sha256=9lZJ8jT1WEj3won6D9B54xdqUvbRvxpOitf97NCUR28,6167
|
|
4
|
+
pylocuszoom/finemapping.py,sha256=PJ4HJYeCaHZecUmADCEGQxKd9HhhjrdIA1H5LQsUmLI,6332
|
|
5
|
+
pylocuszoom/gene_track.py,sha256=CbKqIIl-3VpqIS0NWQ7p-VazhrgbF0-XDkkvoWx_2jI,18665
|
|
6
|
+
pylocuszoom/labels.py,sha256=Ams5WVZFNVT692BRiQ5wZcdbdNEAm5xtgRwmF5u0s_A,3492
|
|
7
|
+
pylocuszoom/ld.py,sha256=64xIulpDVvbMSryWUPoCQ99Odcjwf1wejpwVr_30MLU,6412
|
|
8
|
+
pylocuszoom/logging.py,sha256=nZHEkbnjp8zoyWj_S-Hy9UQvUYLoMoxyiOWRozBT2dg,4987
|
|
9
|
+
pylocuszoom/plotter.py,sha256=7wEN0b3emb0SM7gYn8bSjXBGNt7npw3y3y5AEC-Ha2k,43660
|
|
10
|
+
pylocuszoom/recombination.py,sha256=Q2tAft54nJWHlZt-vZje1r_5RP7D8_VUy5ab_deYXVw,13749
|
|
11
|
+
pylocuszoom/utils.py,sha256=fKNX9WSTbfHR1EpPYijt6ycNjXEjwzunQMHXAvHaK3s,5211
|
|
12
|
+
pylocuszoom/backends/__init__.py,sha256=7dlGvDoqMVK3fCtoMcI9zOP9qO0odQGPwfXhxnLfXfI,1196
|
|
13
|
+
pylocuszoom/backends/base.py,sha256=YEYMtaqPRTJQI-TPqK62-XPN6WvjVwqP6e6ydULK6H0,8523
|
|
14
|
+
pylocuszoom/backends/bokeh_backend.py,sha256=oOXTOhSx-tNgBzgtYfYvGUgNmuUP2vhCbtEBOZ5YZ18,18496
|
|
15
|
+
pylocuszoom/backends/matplotlib_backend.py,sha256=TIKaT7x0X3QKYUB5076XlG6RC0zbi0hcm3LSU7LGnmw,8521
|
|
16
|
+
pylocuszoom/backends/plotly_backend.py,sha256=ucQLmcz6WAdEvII5n3_rdHffZv5-a8FOp8nBGBng-hk,22222
|
|
17
|
+
pylocuszoom/reference_data/__init__.py,sha256=qqHqAUt1jebGlCN3CjqW3Z-_coHVNo5K3a3bb9o83hA,109
|
|
18
|
+
pylocuszoom-0.3.0.dist-info/METADATA,sha256=P-JbXF9KxFzixMAYKQalKO4F4TwOqxDZD0B0uciyy_c,12068
|
|
19
|
+
pylocuszoom-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
20
|
+
pylocuszoom-0.3.0.dist-info/licenses/LICENSE.md,sha256=U2y_hv8RcN5lECA3uK88irU3ODUE1TDAPictcmnP0Q4,698
|
|
21
|
+
pylocuszoom-0.3.0.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
pylocuszoom/__init__.py,sha256=1TUI6bxbsPbNYrJR1-i_cffbh7mqYrXfFuUdF4r4sl8,3174
|
|
2
|
-
pylocuszoom/colors.py,sha256=VnQJEgC6uEBqjlu9mq16ijNn9gRwWHZEAqpu1E-KVug,2638
|
|
3
|
-
pylocuszoom/eqtl.py,sha256=GHOcSs2LRa4YHiKUm-ZNHQz5aR7z-5f8nRFpLvn8ezk,6154
|
|
4
|
-
pylocuszoom/gene_track.py,sha256=2U9JawzPfcjtQo_FhMMTw5juY8QQwRE6mTXy_JdChq4,10281
|
|
5
|
-
pylocuszoom/labels.py,sha256=PhplRqGa1G_4A1JZRdIa5ufnnkfLjpBoln-bs8IL7ok,3585
|
|
6
|
-
pylocuszoom/ld.py,sha256=Jwh_wi34IJMiaXBkNKPTL7S-YVEJXJfckJazSKN3ZBc,6282
|
|
7
|
-
pylocuszoom/logging.py,sha256=nZHEkbnjp8zoyWj_S-Hy9UQvUYLoMoxyiOWRozBT2dg,4987
|
|
8
|
-
pylocuszoom/plotter.py,sha256=B_5O5sXqln2oPUU2Nx8t8IRbqka7AzGfisH1fLorDpg,26991
|
|
9
|
-
pylocuszoom/recombination.py,sha256=qZHghHBnQLfaADo1PfGPGhRXuyaA5YZM8wI9FLnpfEk,13698
|
|
10
|
-
pylocuszoom/utils.py,sha256=1I4r9KOd8yje12L0Y7GiX2Adc7Qrefq5VqdHv83KM7g,5197
|
|
11
|
-
pylocuszoom/backends/__init__.py,sha256=DpPzFMe43FiZv3olsNTmb00il-sqdb6bKlDmFsQrnxY,1320
|
|
12
|
-
pylocuszoom/backends/base.py,sha256=CBubNWuP3qyovOXoseaf3a6CwsDQbujXy-HOTnHJYmE,8457
|
|
13
|
-
pylocuszoom/backends/bokeh_backend.py,sha256=4H_ay8jaWIVZtlyXGiPpm9sNmGMoF11U56ZjUqaL9YQ,12984
|
|
14
|
-
pylocuszoom/backends/matplotlib_backend.py,sha256=tThkVE-49nyKJYR5_qUPu2svk4LVR3HpP2Ac90FT3sw,7907
|
|
15
|
-
pylocuszoom/backends/plotly_backend.py,sha256=3-s3k93KgTzEKQJWcSb_NKGKzuKb3pLR3DEaUjOzJe8,14202
|
|
16
|
-
pylocuszoom/reference_data/__init__.py,sha256=qqHqAUt1jebGlCN3CjqW3Z-_coHVNo5K3a3bb9o83hA,109
|
|
17
|
-
pylocuszoom-0.1.0.dist-info/METADATA,sha256=k7iSnBOEKvDhzafDZjIjtuIOB5KefGy981kFyLXLEiM,11828
|
|
18
|
-
pylocuszoom-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
19
|
-
pylocuszoom-0.1.0.dist-info/licenses/LICENSE.md,sha256=U2y_hv8RcN5lECA3uK88irU3ODUE1TDAPictcmnP0Q4,698
|
|
20
|
-
pylocuszoom-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|