plot-io 0.12.0__py3-none-win_amd64.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.
- plot_io/__init__.py +73 -0
- plot_io/_methods.py +1691 -0
- plot_io/_native/__init__.py +199 -0
- plot_io/_native/__main__.py +197 -0
- plot_io/_native/_api.py +457 -0
- plot_io/_native/bin/windows-x86_64.dll +0 -0
- plot_io/_native/kernels.py +335 -0
- plot_io/_native/manifest.json +49 -0
- plot_io/axis.py +1347 -0
- plot_io/boxes.py +1156 -0
- plot_io/canvas.py +389 -0
- plot_io/color/__init__.py +3 -0
- plot_io/color/data.py +1646 -0
- plot_io/color/maps.py +922 -0
- plot_io/color/values.py +295 -0
- plot_io/color/variants.py +1246 -0
- plot_io/colorbar.py +1426 -0
- plot_io/columns.py +614 -0
- plot_io/decorations.py +920 -0
- plot_io/defaults.py +809 -0
- plot_io/density.py +113 -0
- plot_io/ext.py +68 -0
- plot_io/facets.py +240 -0
- plot_io/figure.py +788 -0
- plot_io/formatters.py +97 -0
- plot_io/genome/__init__.py +17 -0
- plot_io/genome/adapters.py +356 -0
- plot_io/genome/combine.py +123 -0
- plot_io/genome/contacts.py +508 -0
- plot_io/genome/intervals.py +750 -0
- plot_io/genome/lanes.py +191 -0
- plot_io/genome/layout.py +184 -0
- plot_io/genome/links.py +364 -0
- plot_io/genome/pack.py +27 -0
- plot_io/genome/readers.py +145 -0
- plot_io/genome/regions.py +228 -0
- plot_io/genome/scale.py +349 -0
- plot_io/genome/series.py +344 -0
- plot_io/genome/signal.py +621 -0
- plot_io/genome/sites.py +782 -0
- plot_io/genome/sources.py +459 -0
- plot_io/genome/testing.py +161 -0
- plot_io/geometry.py +289 -0
- plot_io/groups.py +308 -0
- plot_io/guides.py +503 -0
- plot_io/helpers/__init__.py +17 -0
- plot_io/helpers/clustermap.py +202 -0
- plot_io/helpers/dendrogram.py +172 -0
- plot_io/helpers/grids.py +304 -0
- plot_io/helpers/qq.py +334 -0
- plot_io/helpers/raincloud.py +249 -0
- plot_io/helpers/tree.py +214 -0
- plot_io/helpers/volcano.py +505 -0
- plot_io/hints.py +125 -0
- plot_io/kernels.py +740 -0
- plot_io/labels.py +566 -0
- plot_io/layers/__init__.py +100 -0
- plot_io/layers/base.py +630 -0
- plot_io/layers/context.py +87 -0
- plot_io/layers/core.py +446 -0
- plot_io/layers/geoms.py +1362 -0
- plot_io/layers/grammar.py +586 -0
- plot_io/layers/paint.py +614 -0
- plot_io/layers/pipeline.py +170 -0
- plot_io/layers/positions.py +431 -0
- plot_io/layers/stats.py +1088 -0
- plot_io/layout.py +1381 -0
- plot_io/legend.py +605 -0
- plot_io/levels.py +509 -0
- plot_io/limits.py +124 -0
- plot_io/lines.py +42 -0
- plot_io/mapped.py +522 -0
- plot_io/markers.py +110 -0
- plot_io/markup.py +128 -0
- plot_io/numbers.py +354 -0
- plot_io/occupancy.py +295 -0
- plot_io/plot.py +597 -0
- plot_io/presets/__init__.py +51 -0
- plot_io/presets/_util.py +153 -0
- plot_io/presets/area.py +145 -0
- plot_io/presets/bar.py +261 -0
- plot_io/presets/box.py +400 -0
- plot_io/presets/catpoints.py +177 -0
- plot_io/presets/contour.py +353 -0
- plot_io/presets/curves.py +30 -0
- plot_io/presets/dots.py +355 -0
- plot_io/presets/ecdf.py +97 -0
- plot_io/presets/errorbar.py +106 -0
- plot_io/presets/errors.py +296 -0
- plot_io/presets/fill.py +74 -0
- plot_io/presets/heatmap.py +322 -0
- plot_io/presets/hexbin.py +198 -0
- plot_io/presets/hist.py +391 -0
- plot_io/presets/hist2d.py +87 -0
- plot_io/presets/kde.py +162 -0
- plot_io/presets/kde2d.py +147 -0
- plot_io/presets/line.py +143 -0
- plot_io/presets/pie.py +502 -0
- plot_io/presets/regression.py +115 -0
- plot_io/presets/scatter.py +194 -0
- plot_io/presets/stem.py +100 -0
- plot_io/presets/violin.py +323 -0
- plot_io/py.typed +0 -0
- plot_io/registry.py +82 -0
- plot_io/render.py +480 -0
- plot_io/rows.py +491 -0
- plot_io/scales.py +103 -0
- plot_io/seams.py +138 -0
- plot_io/settings.py +807 -0
- plot_io/shapes.py +66 -0
- plot_io/sharing.py +232 -0
- plot_io/sizemaps.py +97 -0
- plot_io/spec.py +240 -0
- plot_io/states.py +216 -0
- plot_io/styles/__init__.py +307 -0
- plot_io/styles/library.py +397 -0
- plot_io/tables.py +138 -0
- plot_io/text.py +412 -0
- plot_io/ticks.py +400 -0
- plot_io/times.py +287 -0
- plot_io/titles.py +225 -0
- plot_io/transform.py +114 -0
- plot_io/types.py +13604 -0
- plot_io/units.py +768 -0
- plot_io/validators.py +1029 -0
- plot_io/values.py +190 -0
- plot_io-0.12.0.dist-info/METADATA +215 -0
- plot_io-0.12.0.dist-info/RECORD +130 -0
- plot_io-0.12.0.dist-info/WHEEL +4 -0
- plot_io-0.12.0.dist-info/licenses/LICENSE +28 -0
plot_io/genome/series.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""The base of the genome series: the region read and the axes width the data are binned
|
|
2
|
+
and packed for, and the ``bins`` setting.
|
|
3
|
+
|
|
4
|
+
Each series reads its data when the figure is drawn, for the region drawn (the series'
|
|
5
|
+
``region``, else its x axis'), from a reader (see :mod:`plot_io.genome.sources`) or from
|
|
6
|
+
plain data. x values are positions in bp; they ask their x axis for the genome scale
|
|
7
|
+
(:mod:`plot_io.genome.scale`), which draws them less its ``origin``.
|
|
8
|
+
|
|
9
|
+
On an x axis over several regions (:class:`~plot_io.genome.scale.Segments`), a series
|
|
10
|
+
reads and lays out each region as if it were drawn alone, on axes as wide as the region's
|
|
11
|
+
segment: ``layers`` is made once per segment (the series' ``region()`` and ``axes_px()``
|
|
12
|
+
those of the segment) and the calls are merged into one, each layer drawn through its
|
|
13
|
+
segment's x map and cut at its edges (:class:`SegmentScope`). ``track_height`` and
|
|
14
|
+
``lane_rows`` are the largest of the segments'. A series that places its data across
|
|
15
|
+
the regions itself returns ``False`` from ``segmented()``.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import contextlib
|
|
19
|
+
import functools
|
|
20
|
+
import math
|
|
21
|
+
from array import array
|
|
22
|
+
from collections.abc import Generator, Mapping
|
|
23
|
+
from typing import Any, NamedTuple
|
|
24
|
+
|
|
25
|
+
from vec_io import Path
|
|
26
|
+
|
|
27
|
+
from ..settings import Setting, number
|
|
28
|
+
from ..layers.base import Series
|
|
29
|
+
from ..layers.core import Call
|
|
30
|
+
from .regions import Region, parse_bp, parse_region, pending, whole
|
|
31
|
+
|
|
32
|
+
__all__ = ["GenomeSeries", "Bins", "check_bins", "axes_width", "region_setting",
|
|
33
|
+
"SegmentScope", "in_segment", "axis_region", "known_sizes"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Bins(NamedTuple):
|
|
37
|
+
"""A ``bins`` value: ``kind`` ``"count"`` (``value`` bins over the region), ``"bp"``
|
|
38
|
+
(bins of ``value`` bp) or ``"px"`` (bins ``value`` px wide on the axes)."""
|
|
39
|
+
|
|
40
|
+
kind: str
|
|
41
|
+
value: float
|
|
42
|
+
|
|
43
|
+
def __repr__(self) -> str:
|
|
44
|
+
return f"Bins({self.kind}={self.value:g})"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def check_bins(v: Any) -> Bins:
|
|
48
|
+
"""A bin count (``200``), a bin size in bp (``"10kb"``, ``"5000bp"``, ``"5000d"``) or a
|
|
49
|
+
bin width on the axes (``"0.5px"``)."""
|
|
50
|
+
if isinstance(v, Bins):
|
|
51
|
+
return v
|
|
52
|
+
if isinstance(v, str):
|
|
53
|
+
t = v.strip()
|
|
54
|
+
if t.lower().endswith("px"):
|
|
55
|
+
try:
|
|
56
|
+
w = float(t[:-2])
|
|
57
|
+
except ValueError:
|
|
58
|
+
w = math.nan
|
|
59
|
+
if not (math.isfinite(w) and w > 0):
|
|
60
|
+
raise ValueError(f"expected a positive bin width such as \"0.5px\", got {v!r}")
|
|
61
|
+
return Bins("px", w)
|
|
62
|
+
size = t[:-1] if t.endswith("d") and not t.lower().endswith("bp") else t
|
|
63
|
+
return Bins("bp", float(parse_bp(size, "bin size", positive=True)))
|
|
64
|
+
return Bins("count", float(number(positive=True, integer=True)(v)))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _region(v: Any) -> Any:
|
|
68
|
+
return parse_region(v, whole=True)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def region_setting() -> Setting:
|
|
72
|
+
return Setting(_region,
|
|
73
|
+
doc="The region read: `\"chr2:143,120,001-143,780,000\"` (1-based, "
|
|
74
|
+
"inclusive), `(chr, start, end)` (0-based, half-open), a mapping, "
|
|
75
|
+
"or a chromosome name (the whole chromosome, as the x axis' "
|
|
76
|
+
"`chr_sizes` gives it); unset: the x axis' `region`.")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def known_sizes(owner: Any, axis: Any = None) -> dict[str, int]:
|
|
80
|
+
"""The chromosome sizes known to the figure of ``owner`` (a plot or a figure): the
|
|
81
|
+
largest each reader of its genome series gives (``chr_sizes``), then ``axis``'
|
|
82
|
+
``chr_sizes``, which win."""
|
|
83
|
+
fig = owner if hasattr(owner, "plots") else getattr(owner, "figure", None)
|
|
84
|
+
plots = fig.plots if fig is not None else [owner]
|
|
85
|
+
sizes: dict[str, int] = {}
|
|
86
|
+
for p in plots:
|
|
87
|
+
for s in getattr(p, "series", ()):
|
|
88
|
+
got = getattr(getattr(s, "reader", None), "chr_sizes", None)
|
|
89
|
+
if isinstance(got, Mapping):
|
|
90
|
+
for k, n in got.items():
|
|
91
|
+
sizes[str(k)] = max(sizes.get(str(k), 0), int(n))
|
|
92
|
+
given = axis.resolve("chr_sizes") if axis is not None else None
|
|
93
|
+
if given:
|
|
94
|
+
sizes.update(given)
|
|
95
|
+
return sizes
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def axis_region(axis: Any) -> Any:
|
|
99
|
+
"""The ``region`` of a genome axis, a whole chromosome (``"chr1"``) made the region
|
|
100
|
+
of its size (:func:`known_sizes`)."""
|
|
101
|
+
r = axis.resolve("region")
|
|
102
|
+
if not pending(r):
|
|
103
|
+
return r
|
|
104
|
+
return whole(r, lambda: known_sizes(axis._owner, axis))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def axes_width(plot: Any) -> float:
|
|
108
|
+
"""The axes width (px) genome data are binned and packed for: in a render, the width
|
|
109
|
+
of the plot's axes as laid out when its calls were made (the layout makes them again
|
|
110
|
+
when it places the axes at another width); outside a render, the figure's ``width``."""
|
|
111
|
+
from ..spec import NeedsBox, current
|
|
112
|
+
main = getattr(plot, "twin_of", None)
|
|
113
|
+
plot = main[0] if main else plot
|
|
114
|
+
fig = getattr(plot, "figure", None)
|
|
115
|
+
r = current()
|
|
116
|
+
if r is not None and fig is not None and r.root is fig:
|
|
117
|
+
w = r.widths.get(id(plot))
|
|
118
|
+
if w is None:
|
|
119
|
+
boxes = r.boxes.get(id(plot))
|
|
120
|
+
if not boxes:
|
|
121
|
+
raise NeedsBox("the axes width genome data are binned for is known once the "
|
|
122
|
+
"layout sizes the axes")
|
|
123
|
+
w = r.widths[id(plot)] = boxes["axes_width"]
|
|
124
|
+
return w
|
|
125
|
+
if fig is None or "width" not in fig.SETTINGS:
|
|
126
|
+
return 480.0
|
|
127
|
+
return float(fig.resolve("width"))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@contextlib.contextmanager
|
|
131
|
+
def in_segment(series: Any, seg: Any) -> Generator[None]:
|
|
132
|
+
"""``series``, and the other genome series of its plot (a series may read another's
|
|
133
|
+
data), reading and drawing the region of segment ``seg``."""
|
|
134
|
+
plot = getattr(series, "plot", None)
|
|
135
|
+
every = [s for s in (plot.series if plot is not None else ()) if
|
|
136
|
+
isinstance(s, GenomeSeries)] or [series]
|
|
137
|
+
if series not in every:
|
|
138
|
+
every.append(series)
|
|
139
|
+
before = [s.__dict__.get("_segment") for s in every]
|
|
140
|
+
for s in every:
|
|
141
|
+
s.__dict__["_segment"] = seg
|
|
142
|
+
try:
|
|
143
|
+
yield
|
|
144
|
+
finally:
|
|
145
|
+
for s, b in zip(every, before):
|
|
146
|
+
s.__dict__["_segment"] = b
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class SegmentScope:
|
|
150
|
+
"""A layer of a series drawn in a segment of an axis over several regions:
|
|
151
|
+
:meth:`scope` gives the drawing context whose x map draws the segment's positions, the
|
|
152
|
+
series reading the segment's region, and the canvas cut at the segment's edges."""
|
|
153
|
+
|
|
154
|
+
__slots__ = ("series", "segment")
|
|
155
|
+
|
|
156
|
+
def __init__(self, series: Any, segment: Any) -> None:
|
|
157
|
+
self.series, self.segment = series, segment
|
|
158
|
+
|
|
159
|
+
def context(self, ctx: Any) -> Any:
|
|
160
|
+
"""``ctx`` (a call's context) with the segment's x map; one per plot context and
|
|
161
|
+
segment (its own ``root``: values a layer keeps per context are per segment)."""
|
|
162
|
+
seg = self.segment
|
|
163
|
+
made = ctx.root.__dict__.setdefault("_segments", {})
|
|
164
|
+
key = (id(ctx), seg.number)
|
|
165
|
+
got = made.get(key)
|
|
166
|
+
if got is None:
|
|
167
|
+
import copy
|
|
168
|
+
c = copy.copy(ctx)
|
|
169
|
+
for k in ("_segments", "_lazy", "_seams"):
|
|
170
|
+
c.__dict__.pop(k, None)
|
|
171
|
+
c.xmap = ctx.xmap.affine(seg.region.start - seg.u0 / seg.k, seg.k, "x")
|
|
172
|
+
c.root = c
|
|
173
|
+
got = made[key] = (ctx, c) # ctx kept alive while its id is a key
|
|
174
|
+
return got[1]
|
|
175
|
+
|
|
176
|
+
@contextlib.contextmanager
|
|
177
|
+
def scope(self, ctx: Any, clip: bool = False) -> Generator[Any]:
|
|
178
|
+
seg = self.segment
|
|
179
|
+
c = self.context(ctx)
|
|
180
|
+
with in_segment(self.series, seg), contextlib.ExitStack() as stack:
|
|
181
|
+
if clip:
|
|
182
|
+
canvas = ctx.canvas
|
|
183
|
+
stack.enter_context(canvas.state())
|
|
184
|
+
a, b = sorted((ctx.root.xmap(seg.u0), ctx.root.xmap(seg.u1)))
|
|
185
|
+
y0, y1 = ctx.rect[1], ctx.rect[3]
|
|
186
|
+
canvas.clip(Path().rect(a, y0 - 1e5, b - a, y1 - y0 + 2e5))
|
|
187
|
+
yield c
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _merged(calls: list[Any], series: Any, segs: Any) -> Any:
|
|
191
|
+
"""One call of the calls a series made for each segment: their layers, tables and
|
|
192
|
+
color and size sources, the extents of all (legend entries, colors taken and the rest of
|
|
193
|
+
the first)."""
|
|
194
|
+
out = calls[0]
|
|
195
|
+
for c, seg in zip(calls, segs):
|
|
196
|
+
scope = SegmentScope(series, seg)
|
|
197
|
+
for la in c.layers:
|
|
198
|
+
la.options["segment"] = scope
|
|
199
|
+
for t in c.tables:
|
|
200
|
+
t.segment = seg
|
|
201
|
+
for c in calls[1:]:
|
|
202
|
+
out.layers += c.layers
|
|
203
|
+
out.tables += c.tables
|
|
204
|
+
out.color_sources += c.color_sources
|
|
205
|
+
out.size_sources += c.size_sources
|
|
206
|
+
out.after += c.after
|
|
207
|
+
for role, v in c.extra.items():
|
|
208
|
+
have = out.extra.get(role)
|
|
209
|
+
out.extra[role] = v if have is None else array("d", list(have) + list(v))
|
|
210
|
+
for role, v in c.sticky_values.items():
|
|
211
|
+
out.sticky_values[role] = out.sticky_values.get(role, []) + list(v)
|
|
212
|
+
for role, v in c.ticks.items():
|
|
213
|
+
out.ticks[role] = out.ticks.get(role, []) + list(v)
|
|
214
|
+
for role, f in c.extent_of.items():
|
|
215
|
+
g = out.extent_of.get(role)
|
|
216
|
+
out.extent_of[role] = f if g is None else _union(g, f)
|
|
217
|
+
for name in ("zero", "exact", "invert", "whole", "hide"):
|
|
218
|
+
getattr(out, name).update(getattr(c, name))
|
|
219
|
+
return out
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _union(f: Any, g: Any) -> Any:
|
|
223
|
+
def extent(scale: Any) -> tuple[int, float, float]:
|
|
224
|
+
n1, a1, b1 = f(scale)
|
|
225
|
+
n2, a2, b2 = g(scale)
|
|
226
|
+
if not n1:
|
|
227
|
+
return n2, a2, b2
|
|
228
|
+
if not n2:
|
|
229
|
+
return n1, a1, b1
|
|
230
|
+
return n1 + n2, min(a1, a2), max(b1, b2)
|
|
231
|
+
return extent
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _per_segment(name: str, method: Any) -> Any:
|
|
235
|
+
"""``method`` of a genome series run for each segment of its x axis when it has
|
|
236
|
+
several: ``layers`` merged (:func:`_merged`), other methods the largest result."""
|
|
237
|
+
@functools.wraps(method)
|
|
238
|
+
def run(self: Any, *args: Any) -> Any:
|
|
239
|
+
if self.__dict__.get("_segment") is not None or self.plot is None \
|
|
240
|
+
or not self.segmented():
|
|
241
|
+
return method(self, *args)
|
|
242
|
+
from .scale import segments_of
|
|
243
|
+
width = args[0] if name == "track_height" else None
|
|
244
|
+
segs = segments_of(self.plot, width)
|
|
245
|
+
if segs is None:
|
|
246
|
+
return method(self, *args)
|
|
247
|
+
got = []
|
|
248
|
+
for seg in segs:
|
|
249
|
+
with in_segment(self, seg):
|
|
250
|
+
got.append(method(self, *((seg.px,) if name == "track_height" else args)))
|
|
251
|
+
if name == "layers":
|
|
252
|
+
return _merged(got, self, segs)
|
|
253
|
+
known = [g for g in got if g is not None]
|
|
254
|
+
return max(known) if known else None
|
|
255
|
+
return run
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class GenomeSeries(Series):
|
|
259
|
+
"""Base of the genome series: the region and the width the data are binned for."""
|
|
260
|
+
|
|
261
|
+
#: The data argument (a reader or plain data).
|
|
262
|
+
reader: Any = None
|
|
263
|
+
#: The methods made per segment (see :func:`_per_segment`).
|
|
264
|
+
_PER_SEGMENT = ("layers", "track_height", "lane_rows")
|
|
265
|
+
|
|
266
|
+
def __init_subclass__(cls, **kw: Any) -> None:
|
|
267
|
+
super().__init_subclass__(**kw)
|
|
268
|
+
for name in GenomeSeries._PER_SEGMENT:
|
|
269
|
+
m = cls.__dict__.get(name)
|
|
270
|
+
if m is not None and not getattr(m, "_per_segment", False):
|
|
271
|
+
w = _per_segment(name, m)
|
|
272
|
+
w._per_segment = True
|
|
273
|
+
setattr(cls, name, w)
|
|
274
|
+
|
|
275
|
+
def segmented(self) -> bool:
|
|
276
|
+
"""Whether the series is made once per region of an axis over several (see the
|
|
277
|
+
module); ``False``: it places its data across the regions itself."""
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
def segment(self) -> Any:
|
|
281
|
+
"""The segment being read or drawn (:class:`~plot_io.genome.scale.Segment`), or
|
|
282
|
+
``None``."""
|
|
283
|
+
return self.__dict__.get("_segment")
|
|
284
|
+
|
|
285
|
+
def cached(self, key: Any, compute: Any, reads: tuple[str, ...] = (),
|
|
286
|
+
kind: Any = None) -> Any:
|
|
287
|
+
seg = self.__dict__.get("_segment")
|
|
288
|
+
if seg is not None and kind is not None: # one result per kind and segment
|
|
289
|
+
kind = (kind, seg.number)
|
|
290
|
+
return super().cached(key, compute, reads, kind)
|
|
291
|
+
|
|
292
|
+
def _given_region(self) -> Region | None:
|
|
293
|
+
seg = self.__dict__.get("_segment")
|
|
294
|
+
if seg is not None:
|
|
295
|
+
return seg.region
|
|
296
|
+
r = self.resolve("region")
|
|
297
|
+
if r is None and self.plot is not None:
|
|
298
|
+
return axis_region(self.plot.x_axis)
|
|
299
|
+
if pending(r):
|
|
300
|
+
plot = self.plot
|
|
301
|
+
return whole(r, lambda: known_sizes(plot if plot is not None else self,
|
|
302
|
+
None if plot is None else plot.x_axis))
|
|
303
|
+
return r
|
|
304
|
+
|
|
305
|
+
def region(self) -> Region:
|
|
306
|
+
r = self._given_region()
|
|
307
|
+
if r is None:
|
|
308
|
+
raise ValueError(f"{self!r} has no region: give region= to the x axis "
|
|
309
|
+
f"(plt.Tracks(region), figure.x_axis) or the series")
|
|
310
|
+
return r
|
|
311
|
+
|
|
312
|
+
def axes_px(self) -> float:
|
|
313
|
+
"""The width (px) the data are binned for (see :func:`axes_width`; in a segment, the
|
|
314
|
+
segment's)."""
|
|
315
|
+
seg = self.__dict__.get("_segment")
|
|
316
|
+
return axes_width(self.plot) if seg is None else seg.px
|
|
317
|
+
|
|
318
|
+
def genome_call(self) -> Call:
|
|
319
|
+
"""A call asking its x axis for the genome scale, over the series' region (``None``:
|
|
320
|
+
neither the series nor its x axis has one)."""
|
|
321
|
+
call = Call(self)
|
|
322
|
+
call.scales["x"] = "genome"
|
|
323
|
+
call.region = self._given_region()
|
|
324
|
+
return call
|
|
325
|
+
|
|
326
|
+
def _what(self) -> str:
|
|
327
|
+
label = self.plot.y_axis.resolve("label") if self.plot is not None else None
|
|
328
|
+
name = self.resolve("name") if "name" in self.SETTINGS else None
|
|
329
|
+
tag = label or name
|
|
330
|
+
return f"{self.kind}" + (f" {str(tag)!r}" if tag else "")
|
|
331
|
+
|
|
332
|
+
def _guard(self, read: Any) -> Any:
|
|
333
|
+
"""``read()``, an error naming the track."""
|
|
334
|
+
try:
|
|
335
|
+
return read()
|
|
336
|
+
except Exception as e:
|
|
337
|
+
if self._what() in str(e):
|
|
338
|
+
raise
|
|
339
|
+
raise type(e)(f"{self._what()}: {e}") from e
|
|
340
|
+
|
|
341
|
+
def track_height(self, width: float) -> float | None:
|
|
342
|
+
"""The height (px) the series asks of its track's value lane when the track's
|
|
343
|
+
``height`` is ``"auto"``; ``None``: the track's ``auto_height``."""
|
|
344
|
+
return None
|