scales-python 1.4.0.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.
scales/__init__.py ADDED
@@ -0,0 +1,295 @@
1
+ """Python port of the R scales package — scale functions for visualization."""
2
+
3
+ __version__ = "1.4.0.9000"
4
+ __r_commit__ = "04fc333"
5
+
6
+ # -- Utilities ---------------------------------------------------------------
7
+ from ._utils import * # noqa: F401,F403
8
+
9
+ # -- Bounds / out-of-bounds --------------------------------------------------
10
+ from .bounds import * # noqa: F401,F403
11
+
12
+ # -- Transforms --------------------------------------------------------------
13
+ from .transforms import * # noqa: F401,F403
14
+
15
+ # -- Breaks ------------------------------------------------------------------
16
+ from .breaks import * # noqa: F401,F403
17
+ from .breaks_log import * # noqa: F401,F403
18
+ from .minor_breaks import * # noqa: F401,F403
19
+
20
+ # -- Palettes ----------------------------------------------------------------
21
+ from .palettes import * # noqa: F401,F403
22
+
23
+ # -- Colour utilities --------------------------------------------------------
24
+ from .colour_ramp import * # noqa: F401,F403
25
+ from .colour_manip import * # noqa: F401,F403
26
+ from .colour_mapping import * # noqa: F401,F403
27
+
28
+ # -- Labels ------------------------------------------------------------------
29
+ from .labels import * # noqa: F401,F403
30
+
31
+ # -- Range -------------------------------------------------------------------
32
+ from .range import * # noqa: F401,F403
33
+
34
+ # -- Scale helpers -----------------------------------------------------------
35
+ from .scale_continuous import * # noqa: F401,F403
36
+ from .scale_discrete import * # noqa: F401,F403
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Comprehensive public API
40
+ # ---------------------------------------------------------------------------
41
+ __all__ = [
42
+ # _utils
43
+ "zero_range",
44
+ "expand_range",
45
+ "rescale_common",
46
+ "recycle_common",
47
+ "fullseq",
48
+ "round_any",
49
+ "offset_by",
50
+ "precision",
51
+ "demo_continuous",
52
+ "demo_log10",
53
+ "demo_discrete",
54
+ "demo_datetime",
55
+ "demo_time",
56
+ "demo_timespan",
57
+ # bounds
58
+ "rescale",
59
+ "rescale_mid",
60
+ "rescale_max",
61
+ "rescale_none",
62
+ "censor",
63
+ "squish",
64
+ "squish_infinite",
65
+ "discard",
66
+ "oob_censor",
67
+ "oob_censor_any",
68
+ "oob_squish",
69
+ "oob_squish_any",
70
+ "oob_squish_infinite",
71
+ "oob_keep",
72
+ "oob_discard",
73
+ "trim_to_domain",
74
+ "trans_range", # R alias: trans_range <- trim_to_domain
75
+ # transforms – core API
76
+ "Transform",
77
+ "new_transform",
78
+ "is_transform",
79
+ "as_transform",
80
+ "trans_breaks",
81
+ "trans_format",
82
+ # transforms – constructors
83
+ "transform_identity",
84
+ "transform_log",
85
+ "transform_log10",
86
+ "transform_log2",
87
+ "transform_log1p",
88
+ "transform_exp",
89
+ "transform_sqrt",
90
+ "transform_reverse",
91
+ "transform_reciprocal",
92
+ "transform_asinh",
93
+ "transform_asn",
94
+ "transform_atanh",
95
+ "transform_boxcox",
96
+ "transform_modulus",
97
+ "transform_yj",
98
+ "transform_pseudo_log",
99
+ "transform_logit",
100
+ "transform_probit",
101
+ "transform_probability",
102
+ "transform_date",
103
+ "transform_time",
104
+ "transform_timespan",
105
+ "transform_compose",
106
+ # transforms – legacy aliases
107
+ "trans_new",
108
+ "identity_trans",
109
+ "log_trans",
110
+ "log10_trans",
111
+ "log2_trans",
112
+ "log1p_trans",
113
+ "exp_trans",
114
+ "sqrt_trans",
115
+ "reverse_trans",
116
+ "reciprocal_trans",
117
+ "asinh_trans",
118
+ "asn_trans",
119
+ "atanh_trans",
120
+ "boxcox_trans",
121
+ "modulus_trans",
122
+ "yj_trans",
123
+ "pseudo_log_trans",
124
+ "logit_trans",
125
+ "probit_trans",
126
+ "probability_trans",
127
+ "date_trans",
128
+ "time_trans",
129
+ "timespan_trans",
130
+ "transform_hms",
131
+ "hms_trans",
132
+ "compose_trans",
133
+ "is_trans",
134
+ "as_trans",
135
+ # breaks
136
+ "breaks_extended",
137
+ "breaks_pretty",
138
+ "breaks_width",
139
+ "breaks_timespan",
140
+ "breaks_exp",
141
+ "cbreaks",
142
+ "extended_breaks",
143
+ "pretty_breaks",
144
+ # breaks_log
145
+ "breaks_log",
146
+ "log_breaks", # R alias: log_breaks <- breaks_log
147
+ "minor_breaks_log",
148
+ # minor_breaks
149
+ "minor_breaks_n",
150
+ "minor_breaks_width",
151
+ "regular_minor_breaks",
152
+ # palettes – core classes
153
+ "ContinuousPalette",
154
+ "DiscretePalette",
155
+ # palettes – constructors
156
+ "new_continuous_palette",
157
+ "new_discrete_palette",
158
+ # palettes – testing / getters
159
+ "is_pal",
160
+ "is_continuous_pal",
161
+ "is_discrete_pal",
162
+ "is_colour_pal",
163
+ "is_numeric_pal",
164
+ "palette_nlevels",
165
+ "palette_na_safe",
166
+ "palette_type",
167
+ # palettes – coercion
168
+ "as_discrete_pal",
169
+ "as_continuous_pal",
170
+ "register_palette",
171
+ "get_palette",
172
+ "palette_names",
173
+ "reset_palettes",
174
+ # palettes – discrete factories
175
+ "pal_brewer",
176
+ "pal_hue",
177
+ "pal_viridis",
178
+ "pal_grey",
179
+ "pal_shape",
180
+ "pal_linetype",
181
+ "pal_identity",
182
+ "pal_manual",
183
+ "pal_dichromat",
184
+ # palettes – continuous factories
185
+ "pal_gradient_n",
186
+ "pal_div_gradient",
187
+ "pal_seq_gradient",
188
+ "pal_area",
189
+ "pal_rescale",
190
+ "abs_area",
191
+ # palettes – legacy aliases
192
+ "brewer_pal",
193
+ "hue_pal",
194
+ "viridis_pal",
195
+ "grey_pal",
196
+ "shape_pal",
197
+ "linetype_pal",
198
+ "identity_pal",
199
+ "manual_pal",
200
+ "dichromat_pal",
201
+ "gradient_n_pal",
202
+ "div_gradient_pal",
203
+ "seq_gradient_pal",
204
+ "area_pal",
205
+ "rescale_pal",
206
+ # colour_ramp
207
+ "colour_ramp",
208
+ # colour_manip
209
+ "alpha",
210
+ "muted",
211
+ "col2hcl",
212
+ "show_col",
213
+ "col_mix",
214
+ "col_shift",
215
+ "col_lighter",
216
+ "col_darker",
217
+ "col_saturate",
218
+ # colour_mapping
219
+ "col_numeric",
220
+ "col_bin",
221
+ "col_quantile",
222
+ "col_factor",
223
+ # labels – closure factories
224
+ "label_number",
225
+ "label_comma",
226
+ "label_percent",
227
+ "label_dollar",
228
+ "label_currency",
229
+ "label_scientific",
230
+ "label_bytes",
231
+ "label_ordinal",
232
+ "label_pvalue",
233
+ "label_date",
234
+ "label_date_short",
235
+ "label_time",
236
+ "label_timespan",
237
+ "label_wrap",
238
+ "label_glue",
239
+ "label_parse",
240
+ "label_math",
241
+ "label_log",
242
+ "label_number_auto",
243
+ "label_number_si",
244
+ "label_dictionary",
245
+ "compose_label",
246
+ # labels – ordinal helpers
247
+ "ordinal_english",
248
+ "ordinal_french",
249
+ "ordinal_spanish",
250
+ # labels – direct formatting functions
251
+ "number",
252
+ "comma",
253
+ "dollar",
254
+ "percent",
255
+ "scientific",
256
+ "ordinal",
257
+ "pvalue",
258
+ # labels – core log formatting
259
+ "format_log",
260
+ # labels – scale cut helpers
261
+ "cut_short_scale",
262
+ "cut_long_scale",
263
+ "cut_time_scale",
264
+ "cut_si",
265
+ # labels – date utilities
266
+ "date_breaks",
267
+ "date_format",
268
+ "time_format",
269
+ # labels – legacy aliases
270
+ "comma_format",
271
+ "dollar_format",
272
+ "percent_format",
273
+ "scientific_format",
274
+ "ordinal_format",
275
+ "pvalue_format",
276
+ "number_format",
277
+ "number_bytes_format",
278
+ "number_bytes",
279
+ "parse_format",
280
+ "math_format",
281
+ "wrap_format",
282
+ "unit_format",
283
+ "format_format",
284
+ "number_options",
285
+ # range
286
+ "Range",
287
+ "ContinuousRange",
288
+ "DiscreteRange",
289
+ # scale_continuous
290
+ "cscale",
291
+ "train_continuous",
292
+ # scale_discrete
293
+ "dscale",
294
+ "train_discrete",
295
+ ]
scales/_colors.py ADDED
@@ -0,0 +1,272 @@
1
+ """Pure-Python colour parsing — replaces matplotlib.colors dependency.
2
+
3
+ Provides to_rgba, to_hex, to_rgb with the same API as matplotlib.colors
4
+ but without requiring matplotlib to be installed.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Optional, Sequence, Tuple, Union
10
+
11
+
12
+ # CSS4 named colours (148 entries, matching matplotlib.colors.CSS4_COLORS)
13
+ _CSS4_COLORS: dict[str, str] = {
14
+ "aliceblue": "#f0f8ff",
15
+ "antiquewhite": "#faebd7",
16
+ "aqua": "#00ffff",
17
+ "aquamarine": "#7fffd4",
18
+ "azure": "#f0ffff",
19
+ "beige": "#f5f5dc",
20
+ "bisque": "#ffe4c4",
21
+ "black": "#000000",
22
+ "blanchedalmond": "#ffebcd",
23
+ "blue": "#0000ff",
24
+ "blueviolet": "#8a2be2",
25
+ "brown": "#a52a2a",
26
+ "burlywood": "#deb887",
27
+ "cadetblue": "#5f9ea0",
28
+ "chartreuse": "#7fff00",
29
+ "chocolate": "#d2691e",
30
+ "coral": "#ff7f50",
31
+ "cornflowerblue": "#6495ed",
32
+ "cornsilk": "#fff8dc",
33
+ "crimson": "#dc143c",
34
+ "cyan": "#00ffff",
35
+ "darkblue": "#00008b",
36
+ "darkcyan": "#008b8b",
37
+ "darkgoldenrod": "#b8860b",
38
+ "darkgray": "#a9a9a9",
39
+ "darkgreen": "#006400",
40
+ "darkgrey": "#a9a9a9",
41
+ "darkkhaki": "#bdb76b",
42
+ "darkmagenta": "#8b008b",
43
+ "darkolivegreen": "#556b2f",
44
+ "darkorange": "#ff8c00",
45
+ "darkorchid": "#9932cc",
46
+ "darkred": "#8b0000",
47
+ "darksalmon": "#e9967a",
48
+ "darkseagreen": "#8fbc8f",
49
+ "darkslateblue": "#483d8b",
50
+ "darkslategray": "#2f4f4f",
51
+ "darkslategrey": "#2f4f4f",
52
+ "darkturquoise": "#00ced1",
53
+ "darkviolet": "#9400d3",
54
+ "deeppink": "#ff1493",
55
+ "deepskyblue": "#00bfff",
56
+ "dimgray": "#696969",
57
+ "dimgrey": "#696969",
58
+ "dodgerblue": "#1e90ff",
59
+ "firebrick": "#b22222",
60
+ "floralwhite": "#fffaf0",
61
+ "forestgreen": "#228b22",
62
+ "fuchsia": "#ff00ff",
63
+ "gainsboro": "#dcdcdc",
64
+ "ghostwhite": "#f8f8ff",
65
+ "gold": "#ffd700",
66
+ "goldenrod": "#daa520",
67
+ "gray": "#808080",
68
+ "green": "#008000",
69
+ "greenyellow": "#adff2f",
70
+ "grey": "#808080",
71
+ "honeydew": "#f0fff0",
72
+ "hotpink": "#ff69b4",
73
+ "indianred": "#cd5c5c",
74
+ "indigo": "#4b0082",
75
+ "ivory": "#fffff0",
76
+ "khaki": "#f0e68c",
77
+ "lavender": "#e6e6fa",
78
+ "lavenderblush": "#fff0f5",
79
+ "lawngreen": "#7cfc00",
80
+ "lemonchiffon": "#fffacd",
81
+ "lightblue": "#add8e6",
82
+ "lightcoral": "#f08080",
83
+ "lightcyan": "#e0ffff",
84
+ "lightgoldenrodyellow": "#fafad2",
85
+ "lightgray": "#d3d3d3",
86
+ "lightgreen": "#90ee90",
87
+ "lightgrey": "#d3d3d3",
88
+ "lightpink": "#ffb6c1",
89
+ "lightsalmon": "#ffa07a",
90
+ "lightseagreen": "#20b2aa",
91
+ "lightskyblue": "#87cefa",
92
+ "lightslategray": "#778899",
93
+ "lightslategrey": "#778899",
94
+ "lightsteelblue": "#b0c4de",
95
+ "lightyellow": "#ffffe0",
96
+ "lime": "#00ff00",
97
+ "limegreen": "#32cd32",
98
+ "linen": "#faf0e6",
99
+ "magenta": "#ff00ff",
100
+ "maroon": "#800000",
101
+ "mediumaquamarine": "#66cdaa",
102
+ "mediumblue": "#0000cd",
103
+ "mediumorchid": "#ba55d3",
104
+ "mediumpurple": "#9370db",
105
+ "mediumseagreen": "#3cb371",
106
+ "mediumslateblue": "#7b68ee",
107
+ "mediumspringgreen": "#00fa9a",
108
+ "mediumturquoise": "#48d1cc",
109
+ "mediumvioletred": "#c71585",
110
+ "midnightblue": "#191970",
111
+ "mintcream": "#f5fffa",
112
+ "mistyrose": "#ffe4e1",
113
+ "moccasin": "#ffe4b5",
114
+ "navajowhite": "#ffdead",
115
+ "navy": "#000080",
116
+ "oldlace": "#fdf5e6",
117
+ "olive": "#808000",
118
+ "olivedrab": "#6b8e23",
119
+ "orange": "#ffa500",
120
+ "orangered": "#ff4500",
121
+ "orchid": "#da70d6",
122
+ "palegoldenrod": "#eee8aa",
123
+ "palegreen": "#98fb98",
124
+ "paleturquoise": "#afeeee",
125
+ "palevioletred": "#db7093",
126
+ "papayawhip": "#ffefd5",
127
+ "peachpuff": "#ffdab9",
128
+ "peru": "#cd853f",
129
+ "pink": "#ffc0cb",
130
+ "plum": "#dda0dd",
131
+ "powderblue": "#b0e0e6",
132
+ "purple": "#800080",
133
+ "rebeccapurple": "#663399",
134
+ "red": "#ff0000",
135
+ "rosybrown": "#bc8f8f",
136
+ "royalblue": "#4169e1",
137
+ "saddlebrown": "#8b4513",
138
+ "salmon": "#fa8072",
139
+ "sandybrown": "#f4a460",
140
+ "seagreen": "#2e8b57",
141
+ "seashell": "#fff5ee",
142
+ "sienna": "#a0522d",
143
+ "silver": "#c0c0c0",
144
+ "skyblue": "#87ceeb",
145
+ "slateblue": "#6a5acd",
146
+ "slategray": "#708090",
147
+ "slategrey": "#708090",
148
+ "snow": "#fffafa",
149
+ "springgreen": "#00ff7f",
150
+ "steelblue": "#4682b4",
151
+ "tan": "#d2b48c",
152
+ "teal": "#008080",
153
+ "thistle": "#d8bfd8",
154
+ "tomato": "#ff6347",
155
+ "turquoise": "#40e0d0",
156
+ "violet": "#ee82ee",
157
+ "wheat": "#f5deb3",
158
+ "white": "#ffffff",
159
+ "whitesmoke": "#f5f5f5",
160
+ "yellow": "#ffff00",
161
+ "yellowgreen": "#9acd32",
162
+ }
163
+
164
+ # Also accept single-letter aliases
165
+ _BASE_COLORS: dict[str, str] = {
166
+ "b": "#0000ff", "g": "#008000", "r": "#ff0000",
167
+ "c": "#00bfbf", "m": "#bf00bf", "y": "#bfbf00",
168
+ "k": "#000000", "w": "#ffffff",
169
+ }
170
+
171
+ # Merge into single lookup
172
+ _NAMED_COLORS: dict[str, str] = {**_CSS4_COLORS, **_BASE_COLORS}
173
+
174
+ ColorLike = Union[str, Tuple[float, ...], Sequence[float]]
175
+
176
+
177
+ def _parse_hex(s: str) -> Tuple[float, float, float, float]:
178
+ """Parse #RGB, #RRGGBB, or #RRGGBBAA → (r, g, b, a) in [0, 1]."""
179
+ s = s.lstrip("#")
180
+ if len(s) == 3:
181
+ r = int(s[0] * 2, 16) / 255.0
182
+ g = int(s[1] * 2, 16) / 255.0
183
+ b = int(s[2] * 2, 16) / 255.0
184
+ return (r, g, b, 1.0)
185
+ elif len(s) == 6:
186
+ r = int(s[0:2], 16) / 255.0
187
+ g = int(s[2:4], 16) / 255.0
188
+ b = int(s[4:6], 16) / 255.0
189
+ return (r, g, b, 1.0)
190
+ elif len(s) == 8:
191
+ r = int(s[0:2], 16) / 255.0
192
+ g = int(s[2:4], 16) / 255.0
193
+ b = int(s[4:6], 16) / 255.0
194
+ a = int(s[6:8], 16) / 255.0
195
+ return (r, g, b, a)
196
+ else:
197
+ raise ValueError(f"Invalid hex colour: #{s}")
198
+
199
+
200
+ def to_rgba(c: ColorLike) -> Tuple[float, float, float, float]:
201
+ """
202
+ Convert a colour specification to an (r, g, b, a) tuple in [0, 1].
203
+
204
+ Accepts hex strings (#RGB, #RRGGBB, #RRGGBBAA), CSS4 colour names,
205
+ single-letter aliases (r/g/b/c/m/y/k/w), or numeric tuples.
206
+ """
207
+ if isinstance(c, str):
208
+ c = c.strip().lower()
209
+ if c.startswith("#"):
210
+ return _parse_hex(c)
211
+ if c in _NAMED_COLORS:
212
+ return _parse_hex(_NAMED_COLORS[c])
213
+ # Try "none" / "transparent"
214
+ if c in ("none", "transparent"):
215
+ return (0.0, 0.0, 0.0, 0.0)
216
+ raise ValueError(f"Unknown colour: {c!r}")
217
+
218
+ # Tuple / list / sequence
219
+ t = tuple(float(x) for x in c)
220
+ if len(t) == 3:
221
+ return (t[0], t[1], t[2], 1.0)
222
+ elif len(t) == 4:
223
+ return t # type: ignore[return-value]
224
+ else:
225
+ raise ValueError(f"Colour tuple must have 3 or 4 elements, got {len(t)}")
226
+
227
+
228
+ def to_rgb(c: ColorLike) -> Tuple[float, float, float]:
229
+ """Convert a colour specification to an (r, g, b) tuple in [0, 1]."""
230
+ r, g, b, _a = to_rgba(c)
231
+ return (r, g, b)
232
+
233
+
234
+ def to_hex(c: ColorLike, keep_alpha: bool = True) -> str:
235
+ """
236
+ Convert a colour specification to a hex string.
237
+
238
+ Parameters
239
+ ----------
240
+ c : colour-like
241
+ Colour to convert.
242
+ keep_alpha : bool, default True
243
+ If True and alpha != 1.0, return #RRGGBBAA.
244
+ If False, always return #RRGGBB.
245
+ """
246
+ if isinstance(c, str) and c.startswith("#"):
247
+ rgba = _parse_hex(c)
248
+ elif isinstance(c, str):
249
+ rgba = to_rgba(c)
250
+ else:
251
+ t = tuple(float(x) for x in c)
252
+ if len(t) == 3:
253
+ rgba = (t[0], t[1], t[2], 1.0)
254
+ elif len(t) == 4:
255
+ rgba = t
256
+ else:
257
+ raise ValueError(f"Expected 3 or 4 values, got {len(t)}")
258
+
259
+ r = int(round(rgba[0] * 255))
260
+ g = int(round(rgba[1] * 255))
261
+ b = int(round(rgba[2] * 255))
262
+ a = int(round(rgba[3] * 255))
263
+
264
+ r = max(0, min(255, r))
265
+ g = max(0, min(255, g))
266
+ b = max(0, min(255, b))
267
+ a = max(0, min(255, a))
268
+
269
+ if keep_alpha and a < 255:
270
+ return f"#{r:02x}{g:02x}{b:02x}{a:02x}"
271
+ else:
272
+ return f"#{r:02x}{g:02x}{b:02x}"