ggh4x-python 0.3.1.9000__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.
- ggh4x/__init__.py +140 -0
- ggh4x/_aimed_text_grob.py +432 -0
- ggh4x/_borrowed_ggplot2.py +273 -0
- ggh4x/_cli.py +84 -0
- ggh4x/_datasets.py +106 -0
- ggh4x/_download.py +111 -0
- ggh4x/_facet_helpers.py +313 -0
- ggh4x/_facet_utils.py +649 -0
- ggh4x/_gap_grobs.py +606 -0
- ggh4x/_registry.py +10 -0
- ggh4x/_rlang.py +93 -0
- ggh4x/_utils.py +150 -0
- ggh4x/_vctrs.py +233 -0
- ggh4x/conveniences.py +601 -0
- ggh4x/coord_axes_inside.py +380 -0
- ggh4x/element_part_rect.py +545 -0
- ggh4x/facet_grid2.py +1018 -0
- ggh4x/facet_manual.py +901 -0
- ggh4x/facet_nested.py +776 -0
- ggh4x/facet_nested_wrap.py +193 -0
- ggh4x/facet_wrap2.py +896 -0
- ggh4x/geom_box.py +536 -0
- ggh4x/geom_outline_point.py +444 -0
- ggh4x/geom_pointpath.py +259 -0
- ggh4x/geom_polygonraster.py +252 -0
- ggh4x/geom_rectrug.py +489 -0
- ggh4x/geom_text_aimed.py +279 -0
- ggh4x/guide_stringlegend.py +354 -0
- ggh4x/help_secondary.py +549 -0
- ggh4x/multiscale/__init__.py +51 -0
- ggh4x/multiscale/_multiscale_add.py +207 -0
- ggh4x/multiscale/scale_listed.py +167 -0
- ggh4x/multiscale/scale_manual.py +478 -0
- ggh4x/multiscale/scale_multi.py +393 -0
- ggh4x/panel_scales/__init__.py +58 -0
- ggh4x/panel_scales/at_panel.py +115 -0
- ggh4x/panel_scales/facetted_pos_scales.py +647 -0
- ggh4x/panel_scales/force_panelsize.py +411 -0
- ggh4x/panel_scales/scale_facet.py +222 -0
- ggh4x/position_disjoint_ranges.py +229 -0
- ggh4x/position_lineartrans.py +242 -0
- ggh4x/py.typed +0 -0
- ggh4x/resources/faithful.csv +273 -0
- ggh4x/resources/iris.csv +151 -0
- ggh4x/resources/mtcars.csv +33 -0
- ggh4x/resources/pressure.csv +20 -0
- ggh4x/resources/volcano.csv +87 -0
- ggh4x/save.py +255 -0
- ggh4x/stat_difference.py +388 -0
- ggh4x/stat_funxy.py +436 -0
- ggh4x/stat_rle.py +290 -0
- ggh4x/stat_rollingkernel.py +369 -0
- ggh4x/stat_theodensity.py +681 -0
- ggh4x/strip_nested.py +448 -0
- ggh4x/strip_split.py +687 -0
- ggh4x/strip_tag.py +636 -0
- ggh4x/strip_themed.py +232 -0
- ggh4x/strip_vanilla.py +1464 -0
- ggh4x/themes.py +31 -0
- ggh4x/themes_ggh4x.py +67 -0
- ggh4x_python-0.3.1.9000.dist-info/METADATA +40 -0
- ggh4x_python-0.3.1.9000.dist-info/RECORD +64 -0
- ggh4x_python-0.3.1.9000.dist-info/WHEEL +4 -0
- ggh4x_python-0.3.1.9000.dist-info/licenses/LICENSE +3 -0
ggh4x/strip_nested.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"""Nested strips for ggh4x facets (port of ggh4x ``strip_nested.R``).
|
|
2
|
+
|
|
3
|
+
This module ports :class:`StripNested` and the :func:`strip_nested`
|
|
4
|
+
constructor. Nested strips merge adjacent strips on the same layer that share a
|
|
5
|
+
label, so an outer faceting variable spans the panels of its inner variables.
|
|
6
|
+
It is the default strip for ``facet_nested()`` / ``facet_nested_wrap()``.
|
|
7
|
+
|
|
8
|
+
``StripNested`` extends :class:`ggh4x.strip_themed.StripThemed`. It overrides
|
|
9
|
+
two methods:
|
|
10
|
+
|
|
11
|
+
* :meth:`StripNested.assemble_strip` -- run-length-encoding (RLE) merge of
|
|
12
|
+
adjacent equal labels per layer. A single-layer strip (one faceting variable)
|
|
13
|
+
has nothing to merge and is delegated to the base
|
|
14
|
+
:meth:`Strip.assemble_strip` via :func:`ggplot2_py.ggproto.ggproto_parent`.
|
|
15
|
+
* :meth:`StripNested.finish_strip` -- builds *one* multi-cell gtable per
|
|
16
|
+
panel-group (the merged strip) when the redefined layout carries a ``layer``
|
|
17
|
+
column; otherwise (the monolayer fast path produced a plain layout) delegates
|
|
18
|
+
to the self-less :func:`Strip.finish_strip`.
|
|
19
|
+
|
|
20
|
+
R source: ``ggh4x/R/strip_nested.R``.
|
|
21
|
+
|
|
22
|
+
Notes
|
|
23
|
+
-----
|
|
24
|
+
* **Column-major / ROW-COL ordering is load-bearing.** The labels and layout
|
|
25
|
+
are re-ordered by ``order(ROW, COL)`` (x strips) or ``order(COL, ROW)`` (y
|
|
26
|
+
strips); the RLE separator variable (``ROW`` for x, ``COL`` for y) is pasted
|
|
27
|
+
onto each label to block merges across rows/columns. When ``bleed`` is
|
|
28
|
+
``False`` each layer's pasted key is further pasted with all *preceding*
|
|
29
|
+
layers so a lower-layer strip cannot merge across a higher-layer boundary.
|
|
30
|
+
* **Run encoding.** ``rle(x)$lengths`` per layer -> ``ends = cumsum(lengths)``,
|
|
31
|
+
``starts = ends - lengths + 1`` (1-based, per-layer concatenated). The merged
|
|
32
|
+
cell takes the label at its run-start row and its layer column
|
|
33
|
+
(``labels[cbind(starts, index)]``).
|
|
34
|
+
* **Method-binding asymmetry.** ``assemble_strip`` / ``finish_strip`` here take
|
|
35
|
+
``self`` (instance methods). The monolayer delegate uses
|
|
36
|
+
``ggproto_parent(Strip, self).assemble_strip(...)`` (parent dispatch on a
|
|
37
|
+
*self-bearing* function), while the no-``layer``-column delegate calls the
|
|
38
|
+
*self-less* :func:`Strip.finish_strip` directly (a plain function). These two
|
|
39
|
+
idioms are intentionally different and must not be unified.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from typing import Any, Dict, List, Sequence
|
|
45
|
+
|
|
46
|
+
import numpy as np
|
|
47
|
+
import pandas as pd
|
|
48
|
+
|
|
49
|
+
from ggplot2_py.ggproto import ggproto, ggproto_parent
|
|
50
|
+
from grid_py import unit_c, unit_rep
|
|
51
|
+
from gtable_py import Gtable, gtable_add_grob
|
|
52
|
+
|
|
53
|
+
from ggh4x._rlang import arg_match0
|
|
54
|
+
from ggh4x.strip_themed import StripThemed
|
|
55
|
+
from ggh4x.strip_vanilla import (
|
|
56
|
+
Strip,
|
|
57
|
+
_is_zero_grob,
|
|
58
|
+
validate_element_list,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
__all__ = ["StripNested", "strip_nested"]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# Run-length helper
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
def _rle_lengths(x: Sequence[Any]) -> List[int]:
|
|
68
|
+
"""Port of R ``rle(x)$lengths`` -- run lengths of consecutive equal values.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
x : sequence
|
|
73
|
+
Values to run-length encode.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
list of int
|
|
78
|
+
The length of each maximal run of consecutive equal elements.
|
|
79
|
+
"""
|
|
80
|
+
lengths: List[int] = []
|
|
81
|
+
prev = object()
|
|
82
|
+
for v in x:
|
|
83
|
+
if v == prev and lengths:
|
|
84
|
+
lengths[-1] += 1
|
|
85
|
+
else:
|
|
86
|
+
lengths.append(1)
|
|
87
|
+
prev = v
|
|
88
|
+
return lengths
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _recycle(seq: Sequence[Any], length_out: int) -> List[Any]:
|
|
92
|
+
"""Port of R ``rep(seq, length.out=n)`` for a plain list.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
seq : sequence
|
|
97
|
+
Source values.
|
|
98
|
+
length_out : int
|
|
99
|
+
Target length.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
list
|
|
104
|
+
*seq* cyclically recycled (or truncated) to *length_out*.
|
|
105
|
+
"""
|
|
106
|
+
n = len(seq)
|
|
107
|
+
if length_out <= 0 or n == 0:
|
|
108
|
+
return []
|
|
109
|
+
return [seq[i % n] for i in range(length_out)]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _reverse_unit(u: Any) -> Any:
|
|
113
|
+
"""Reverse a unit vector (R ``rev(unit_vector)``).
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
u : grid_py.Unit
|
|
118
|
+
Source unit vector.
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
grid_py.Unit
|
|
123
|
+
The unit vector with its elements in reverse order.
|
|
124
|
+
"""
|
|
125
|
+
n = len(u)
|
|
126
|
+
if n <= 1:
|
|
127
|
+
return u
|
|
128
|
+
return unit_c(*[u[i] for i in range(n - 1, -1, -1)])
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class StripNested(StripThemed):
|
|
132
|
+
"""Strip that merges adjacent same-label strips into spanning strips.
|
|
133
|
+
|
|
134
|
+
Subclass of :class:`ggh4x.strip_themed.StripThemed`. See the module
|
|
135
|
+
docstring for the RLE-merge algorithm.
|
|
136
|
+
|
|
137
|
+
Attributes
|
|
138
|
+
----------
|
|
139
|
+
params : dict
|
|
140
|
+
Adds ``bleed`` (bool) to the base ``clip`` / ``size`` params.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
_class_name = "StripNested"
|
|
144
|
+
|
|
145
|
+
params: Dict[str, Any] = {"bleed": False}
|
|
146
|
+
|
|
147
|
+
def assemble_strip(
|
|
148
|
+
self,
|
|
149
|
+
labels: np.ndarray,
|
|
150
|
+
position: str,
|
|
151
|
+
elements: Dict[str, Any],
|
|
152
|
+
params: Dict[str, Any],
|
|
153
|
+
layout: pd.DataFrame,
|
|
154
|
+
) -> pd.DataFrame:
|
|
155
|
+
"""RLE-merge adjacent equal labels per layer, then draw and finish.
|
|
156
|
+
|
|
157
|
+
Port of R ``StripNested$assemble_strip`` (``strip_nested.R:110-168``).
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
labels : numpy.ndarray
|
|
162
|
+
2-D object array of label strings (rows = panels, cols = layers).
|
|
163
|
+
position : str
|
|
164
|
+
Strip side (``"top"`` / ``"bottom"`` / ``"left"`` / ``"right"``).
|
|
165
|
+
elements : dict
|
|
166
|
+
Resolved element bundle from :meth:`setup_elements`.
|
|
167
|
+
params : dict
|
|
168
|
+
Strip params (``size``, ``clip``, ``bleed``).
|
|
169
|
+
layout : pandas.DataFrame
|
|
170
|
+
The sliced layout for this side (carries ``PANEL`` / ``ROW`` /
|
|
171
|
+
``COL``).
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
pandas.DataFrame
|
|
176
|
+
Placement frame with a ``layer`` column and per-group merged
|
|
177
|
+
gtables (from :meth:`finish_strip`).
|
|
178
|
+
"""
|
|
179
|
+
nlayers = labels.shape[1]
|
|
180
|
+
# Monolayer fast path: nothing to merge -> base assembly.
|
|
181
|
+
if nlayers == 1:
|
|
182
|
+
return ggproto_parent(Strip, self).assemble_strip(
|
|
183
|
+
labels, position, elements, params, layout
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
aes = "x" if position in ("top", "bottom") else "y"
|
|
187
|
+
bleed = self.params["bleed"]
|
|
188
|
+
|
|
189
|
+
# Right strip reverses label columns (inside-out) -- note the base
|
|
190
|
+
# build_strip already reversed; R reverses again here so it operates on
|
|
191
|
+
# outermost-first columns.
|
|
192
|
+
if position == "right":
|
|
193
|
+
labels = labels[:, ::-1]
|
|
194
|
+
|
|
195
|
+
if aes == "x":
|
|
196
|
+
sepvar = "ROW"
|
|
197
|
+
order_keys = ["ROW", "COL"]
|
|
198
|
+
else:
|
|
199
|
+
sepvar = "COL"
|
|
200
|
+
order_keys = ["COL", "ROW"]
|
|
201
|
+
|
|
202
|
+
# R order(a, b) -> stable lexicographic order.
|
|
203
|
+
order = np.lexsort(
|
|
204
|
+
tuple(np.asarray(layout[k].to_numpy()) for k in reversed(order_keys))
|
|
205
|
+
)
|
|
206
|
+
labels = labels[order, :]
|
|
207
|
+
layout = layout.iloc[order].reset_index(drop=True)
|
|
208
|
+
|
|
209
|
+
sep = [str(v) for v in layout[sepvar].tolist()]
|
|
210
|
+
|
|
211
|
+
nrow = labels.shape[0]
|
|
212
|
+
# Per-layer pasted key columns (R: tmp[] <- paste0(col, layout[[sepvar]])).
|
|
213
|
+
tmp_cols: List[List[str]] = []
|
|
214
|
+
for j in range(nlayers):
|
|
215
|
+
tmp_cols.append([str(labels[i, j]) + sep[i] for i in range(nrow)])
|
|
216
|
+
|
|
217
|
+
if not bleed:
|
|
218
|
+
# Paste each layer (from the 2nd) with all preceding pasted layers
|
|
219
|
+
# to prevent lower-layer bleeding across higher-layer boundaries.
|
|
220
|
+
cumulative = list(tmp_cols[0])
|
|
221
|
+
for j in range(1, nlayers):
|
|
222
|
+
cumulative = [cumulative[i] + tmp_cols[j][i] for i in range(nrow)]
|
|
223
|
+
tmp_cols[j] = list(cumulative)
|
|
224
|
+
|
|
225
|
+
# Run lengths per layer.
|
|
226
|
+
lens = [_rle_lengths(col) for col in tmp_cols]
|
|
227
|
+
flat_lens: List[int] = [length for col_lens in lens for length in col_lens]
|
|
228
|
+
|
|
229
|
+
ends: List[int] = []
|
|
230
|
+
for col_lens in lens:
|
|
231
|
+
acc = 0
|
|
232
|
+
for length in col_lens:
|
|
233
|
+
acc += length
|
|
234
|
+
ends.append(acc)
|
|
235
|
+
starts = [e - flat_lens[k] + 1 for k, e in enumerate(ends)]
|
|
236
|
+
|
|
237
|
+
panel = [int(p) for p in layout["PANEL"].tolist()]
|
|
238
|
+
# layer id per run, repeated by the number of runs in each layer.
|
|
239
|
+
layer = [j + 1 for j, col_lens in enumerate(lens) for _ in col_lens]
|
|
240
|
+
|
|
241
|
+
new_layout = pd.DataFrame(
|
|
242
|
+
{
|
|
243
|
+
"t": [panel[s - 1] for s in starts],
|
|
244
|
+
"b": [panel[e - 1] for e in ends],
|
|
245
|
+
"l": [panel[s - 1] for s in starts],
|
|
246
|
+
"r": [panel[e - 1] for e in ends],
|
|
247
|
+
"layer": layer,
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
index = list(new_layout["layer"])
|
|
251
|
+
# labels[cbind(starts, index)] -- matrix index (1-based) -> the run-start
|
|
252
|
+
# row at its layer column.
|
|
253
|
+
merged = [labels[starts[k] - 1, index[k] - 1] for k in range(len(starts))]
|
|
254
|
+
|
|
255
|
+
elems = self.init_strip(elements, position, index)
|
|
256
|
+
strips = self.draw_labels(merged, elems, position, index, params["size"])
|
|
257
|
+
|
|
258
|
+
width = _recycle_unit_attr(strips.width, nlayers)
|
|
259
|
+
height = _recycle_unit_attr(strips.height, nlayers)
|
|
260
|
+
|
|
261
|
+
return self.finish_strip(
|
|
262
|
+
list(strips),
|
|
263
|
+
width,
|
|
264
|
+
height,
|
|
265
|
+
position,
|
|
266
|
+
new_layout,
|
|
267
|
+
(new_layout.shape[0], nlayers),
|
|
268
|
+
params["clip"],
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
def finish_strip(
|
|
272
|
+
self,
|
|
273
|
+
strip: Sequence[Any],
|
|
274
|
+
width: Any,
|
|
275
|
+
height: Any,
|
|
276
|
+
position: str,
|
|
277
|
+
layout: pd.DataFrame,
|
|
278
|
+
dim: Any,
|
|
279
|
+
clip: str = "inherit",
|
|
280
|
+
) -> pd.DataFrame:
|
|
281
|
+
"""Build one multi-cell gtable per merged group (or delegate).
|
|
282
|
+
|
|
283
|
+
Port of R ``StripNested$finish_strip`` (``strip_nested.R:170-200``).
|
|
284
|
+
When the *layout* lacks a ``layer`` column (the monolayer fast path
|
|
285
|
+
produced a plain placement frame) this delegates to the *self-less*
|
|
286
|
+
:func:`Strip.finish_strip`. Otherwise each merged strip is placed into a
|
|
287
|
+
bare :class:`gtable_py.Gtable` at its layer index (``t=index, l=1`` for
|
|
288
|
+
horizontal; ``t=1, l=index`` for vertical); for ``"bottom"`` / ``"right"``
|
|
289
|
+
the index and the width/height vectors are reversed so the outermost
|
|
290
|
+
layer sits furthest from the panel.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
strip : sequence
|
|
295
|
+
The merged label grobs (one per run).
|
|
296
|
+
width, height : grid_py.Unit
|
|
297
|
+
Per-layer width / height unit vectors (length ``nlayers``).
|
|
298
|
+
position : str
|
|
299
|
+
Strip side.
|
|
300
|
+
layout : pandas.DataFrame
|
|
301
|
+
The redefined layout (carries ``layer`` when merged).
|
|
302
|
+
dim : tuple of int
|
|
303
|
+
``(nrow, nlayers)``.
|
|
304
|
+
clip : str, default ``"inherit"``
|
|
305
|
+
Clip setting.
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
pandas.DataFrame
|
|
310
|
+
The *layout* with an attached object ``grobs`` column.
|
|
311
|
+
"""
|
|
312
|
+
# No 'layer' column -> delegate to the self-less base finish_strip.
|
|
313
|
+
if "layer" not in layout.columns:
|
|
314
|
+
return Strip.finish_strip(
|
|
315
|
+
strip, width, height, position, layout, dim, clip
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
strip = list(strip)
|
|
319
|
+
empty_strips = len(strip) == 0 or all(_is_zero_grob(g) for g in strip)
|
|
320
|
+
|
|
321
|
+
out_grobs: List[Any] = strip
|
|
322
|
+
if not empty_strips:
|
|
323
|
+
index = [int(v) for v in layout["layer"]]
|
|
324
|
+
ncols = int(dim[1])
|
|
325
|
+
if position in ("bottom", "right"):
|
|
326
|
+
index = [ncols - i + 1 for i in index]
|
|
327
|
+
width = _reverse_unit(width)
|
|
328
|
+
height = _reverse_unit(height)
|
|
329
|
+
out_grobs = []
|
|
330
|
+
if position in ("top", "bottom"):
|
|
331
|
+
# gt = gtable(widths = width[1], heights = height)
|
|
332
|
+
for g, i in zip(strip, index):
|
|
333
|
+
gt = Gtable(widths=_unit_slice(width, 0), heights=height)
|
|
334
|
+
gt = gtable_add_grob(gt, g, t=i, l=1, clip=clip)
|
|
335
|
+
out_grobs.append(gt)
|
|
336
|
+
else:
|
|
337
|
+
for g, i in zip(strip, index):
|
|
338
|
+
gt = Gtable(widths=width, heights=_unit_slice(height, 0))
|
|
339
|
+
gt = gtable_add_grob(gt, g, t=1, l=i, clip=clip)
|
|
340
|
+
out_grobs.append(gt)
|
|
341
|
+
|
|
342
|
+
result = layout.copy()
|
|
343
|
+
result["grobs"] = pd.Series(out_grobs, index=result.index, dtype=object)
|
|
344
|
+
return result
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
# Unit helpers
|
|
349
|
+
# ---------------------------------------------------------------------------
|
|
350
|
+
def _recycle_unit_attr(u: Any, length_out: int) -> Any:
|
|
351
|
+
"""Recycle the ``width``/``height`` unit attribute to ``nlayers``.
|
|
352
|
+
|
|
353
|
+
Port of R ``rep(attr(strips, 'width'), length.out = nlayers)``.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
u : grid_py.Unit or None
|
|
358
|
+
The unit vector attribute from ``draw_labels``.
|
|
359
|
+
length_out : int
|
|
360
|
+
Target length (number of layers).
|
|
361
|
+
|
|
362
|
+
Returns
|
|
363
|
+
-------
|
|
364
|
+
grid_py.Unit or None
|
|
365
|
+
The recycled unit vector (``None`` passes through).
|
|
366
|
+
"""
|
|
367
|
+
if u is None:
|
|
368
|
+
return None
|
|
369
|
+
return unit_rep(u, length_out=length_out)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _unit_slice(u: Any, i: int) -> Any:
|
|
373
|
+
"""Return the single-element unit at position *i* (R ``u[i+1]``).
|
|
374
|
+
|
|
375
|
+
Parameters
|
|
376
|
+
----------
|
|
377
|
+
u : grid_py.Unit
|
|
378
|
+
Source unit vector.
|
|
379
|
+
i : int
|
|
380
|
+
0-based index.
|
|
381
|
+
|
|
382
|
+
Returns
|
|
383
|
+
-------
|
|
384
|
+
grid_py.Unit
|
|
385
|
+
The unit at *i* (or *u* itself when it is already scalar).
|
|
386
|
+
"""
|
|
387
|
+
n = len(u)
|
|
388
|
+
if n <= 1:
|
|
389
|
+
return u
|
|
390
|
+
return u[i % n]
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# R's ``StripNested`` ggproto instance used as the parent of every clone.
|
|
394
|
+
_STRIP_NESTED_SINGLETON: "StripNested" = StripNested()
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def strip_nested(
|
|
398
|
+
clip: str = "inherit",
|
|
399
|
+
size: str = "constant",
|
|
400
|
+
bleed: bool = False,
|
|
401
|
+
text_x: Any = None,
|
|
402
|
+
text_y: Any = None,
|
|
403
|
+
background_x: Any = None,
|
|
404
|
+
background_y: Any = None,
|
|
405
|
+
by_layer_x: bool = False,
|
|
406
|
+
by_layer_y: bool = False,
|
|
407
|
+
) -> StripNested:
|
|
408
|
+
"""Create a nested (label-merging) strip.
|
|
409
|
+
|
|
410
|
+
Port of R ``strip_nested()`` (``strip_nested.R:66-97``).
|
|
411
|
+
|
|
412
|
+
Parameters
|
|
413
|
+
----------
|
|
414
|
+
clip : str, default ``"inherit"``
|
|
415
|
+
Whether labels are clipped to background boxes.
|
|
416
|
+
size : str, default ``"constant"``
|
|
417
|
+
Whether strip margins across layers are ``"constant"`` or ``"variable"``.
|
|
418
|
+
bleed : bool, default ``False``
|
|
419
|
+
Whether lower-layer strips may merge across higher-layer boundaries.
|
|
420
|
+
text_x, text_y, background_x, background_y : list or element or None
|
|
421
|
+
Per-strip themed elements (see :func:`ggh4x.strip_themed.strip_themed`).
|
|
422
|
+
by_layer_x, by_layer_y : bool, default ``False``
|
|
423
|
+
Map elements to layers (``True``) or strips (``False``).
|
|
424
|
+
|
|
425
|
+
Returns
|
|
426
|
+
-------
|
|
427
|
+
StripNested
|
|
428
|
+
A ``StripNested`` ggproto instance usable in ggh4x facets.
|
|
429
|
+
"""
|
|
430
|
+
params = {
|
|
431
|
+
"clip": arg_match0(clip, ["on", "off", "inherit"], arg_name="clip"),
|
|
432
|
+
"size": arg_match0(size, ["constant", "variable"], arg_name="size"),
|
|
433
|
+
"bleed": bool(bleed),
|
|
434
|
+
}
|
|
435
|
+
given_elements = {
|
|
436
|
+
"text_x": validate_element_list(text_x, "element_text"),
|
|
437
|
+
"text_y": validate_element_list(text_y, "element_text"),
|
|
438
|
+
"background_x": validate_element_list(background_x, "element_rect"),
|
|
439
|
+
"background_y": validate_element_list(background_y, "element_rect"),
|
|
440
|
+
"by_layer_x": bool(by_layer_x),
|
|
441
|
+
"by_layer_y": bool(by_layer_y),
|
|
442
|
+
}
|
|
443
|
+
return ggproto(
|
|
444
|
+
None,
|
|
445
|
+
_STRIP_NESTED_SINGLETON,
|
|
446
|
+
params=params,
|
|
447
|
+
given_elements=given_elements,
|
|
448
|
+
)
|