lets-plot 4.8.1rc1__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.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.
Files changed (97) hide show
  1. lets_plot/__init__.py +382 -0
  2. lets_plot/_global_settings.py +192 -0
  3. lets_plot/_kbridge.py +197 -0
  4. lets_plot/_type_utils.py +133 -0
  5. lets_plot/_version.py +6 -0
  6. lets_plot/bistro/__init__.py +16 -0
  7. lets_plot/bistro/_plot2d_common.py +106 -0
  8. lets_plot/bistro/corr.py +448 -0
  9. lets_plot/bistro/im.py +196 -0
  10. lets_plot/bistro/joint.py +192 -0
  11. lets_plot/bistro/qq.py +207 -0
  12. lets_plot/bistro/residual.py +341 -0
  13. lets_plot/bistro/waterfall.py +332 -0
  14. lets_plot/export/__init__.py +6 -0
  15. lets_plot/export/ggsave_.py +172 -0
  16. lets_plot/frontend_context/__init__.py +8 -0
  17. lets_plot/frontend_context/_configuration.py +140 -0
  18. lets_plot/frontend_context/_dynamic_configure_html.py +115 -0
  19. lets_plot/frontend_context/_frontend_ctx.py +16 -0
  20. lets_plot/frontend_context/_html_contexts.py +223 -0
  21. lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
  22. lets_plot/frontend_context/_isolated_webview_panel_ctx.py +81 -0
  23. lets_plot/frontend_context/_json_contexts.py +39 -0
  24. lets_plot/frontend_context/_jupyter_notebook_ctx.py +82 -0
  25. lets_plot/frontend_context/_mime_types.py +7 -0
  26. lets_plot/frontend_context/_static_html_page_ctx.py +76 -0
  27. lets_plot/frontend_context/_static_svg_ctx.py +26 -0
  28. lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
  29. lets_plot/frontend_context/sandbox.py +5 -0
  30. lets_plot/geo_data/__init__.py +19 -0
  31. lets_plot/geo_data/core.py +335 -0
  32. lets_plot/geo_data/geocoder.py +988 -0
  33. lets_plot/geo_data/geocodes.py +512 -0
  34. lets_plot/geo_data/gis/__init__.py +0 -0
  35. lets_plot/geo_data/gis/fluent_dict.py +201 -0
  36. lets_plot/geo_data/gis/geocoding_service.py +42 -0
  37. lets_plot/geo_data/gis/geometry.py +91 -0
  38. lets_plot/geo_data/gis/json_request.py +232 -0
  39. lets_plot/geo_data/gis/json_response.py +308 -0
  40. lets_plot/geo_data/gis/request.py +492 -0
  41. lets_plot/geo_data/gis/response.py +247 -0
  42. lets_plot/geo_data/livemap_helper.py +65 -0
  43. lets_plot/geo_data/to_geo_data_frame.py +141 -0
  44. lets_plot/geo_data/type_assertion.py +34 -0
  45. lets_plot/geo_data_internals/__init__.py +4 -0
  46. lets_plot/geo_data_internals/constants.py +13 -0
  47. lets_plot/geo_data_internals/utils.py +33 -0
  48. lets_plot/mapping.py +115 -0
  49. lets_plot/package_data/lets-plot.min.js +3 -0
  50. lets_plot/plot/__init__.py +64 -0
  51. lets_plot/plot/_global_theme.py +14 -0
  52. lets_plot/plot/annotation.py +290 -0
  53. lets_plot/plot/coord.py +242 -0
  54. lets_plot/plot/core.py +1071 -0
  55. lets_plot/plot/expand_limits_.py +78 -0
  56. lets_plot/plot/facet.py +210 -0
  57. lets_plot/plot/font_features.py +71 -0
  58. lets_plot/plot/geom.py +9146 -0
  59. lets_plot/plot/geom_extras.py +53 -0
  60. lets_plot/plot/geom_function_.py +219 -0
  61. lets_plot/plot/geom_imshow_.py +393 -0
  62. lets_plot/plot/geom_livemap_.py +343 -0
  63. lets_plot/plot/ggbunch_.py +96 -0
  64. lets_plot/plot/gggrid_.py +139 -0
  65. lets_plot/plot/ggtb_.py +81 -0
  66. lets_plot/plot/guide.py +231 -0
  67. lets_plot/plot/label.py +187 -0
  68. lets_plot/plot/marginal_layer.py +181 -0
  69. lets_plot/plot/plot.py +245 -0
  70. lets_plot/plot/pos.py +344 -0
  71. lets_plot/plot/sampling.py +338 -0
  72. lets_plot/plot/sandbox_.py +26 -0
  73. lets_plot/plot/scale.py +3580 -0
  74. lets_plot/plot/scale_colormap_mpl.py +300 -0
  75. lets_plot/plot/scale_convenience.py +155 -0
  76. lets_plot/plot/scale_identity_.py +653 -0
  77. lets_plot/plot/scale_position.py +1342 -0
  78. lets_plot/plot/series_meta.py +209 -0
  79. lets_plot/plot/stat.py +585 -0
  80. lets_plot/plot/subplots.py +331 -0
  81. lets_plot/plot/subplots_util.py +24 -0
  82. lets_plot/plot/theme_.py +790 -0
  83. lets_plot/plot/theme_set.py +418 -0
  84. lets_plot/plot/tooltip.py +486 -0
  85. lets_plot/plot/util.py +267 -0
  86. lets_plot/settings_utils.py +244 -0
  87. lets_plot/tilesets.py +429 -0
  88. lets_plot-4.8.1rc1.dist-info/METADATA +221 -0
  89. lets_plot-4.8.1rc1.dist-info/RECORD +97 -0
  90. lets_plot-4.8.1rc1.dist-info/WHEEL +6 -0
  91. lets_plot-4.8.1rc1.dist-info/licenses/LICENSE +21 -0
  92. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
  93. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
  94. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
  95. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
  96. lets_plot-4.8.1rc1.dist-info/top_level.txt +2 -0
  97. lets_plot_kotlin_bridge.cpython-311-x86_64-linux-gnu.so +0 -0
@@ -0,0 +1,343 @@
1
+ #
2
+ # Copyright (c) 2019. JetBrains s.r.o.
3
+ # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4
+ #
5
+ from enum import Enum
6
+ from typing import Union, Optional, List
7
+
8
+ from lets_plot._global_settings import MAPTILES_KIND, MAPTILES_URL, MAPTILES_THEME, MAPTILES_ATTRIBUTION, \
9
+ GEOCODING_PROVIDER_URL, GEOCODING_ROUTE, \
10
+ TILES_RASTER_ZXY, TILES_VECTOR_LETS_PLOT, MAPTILES_MIN_ZOOM, MAPTILES_MAX_ZOOM, TILES_SOLID, \
11
+ MAPTILES_SOLID_FILL_COLOR, TILES_CHESSBOARD
12
+ from lets_plot._global_settings import has_global_value, get_global_val
13
+ from .geom import _geom
14
+
15
+ try:
16
+ import pandas
17
+ except ImportError:
18
+ pandas = None
19
+
20
+ # from ..geo_data.livemap_helper import _prepare_location
21
+ # from ..geo_data.livemap_helper import _prepare_parent
22
+ # from ..geo_data.livemap_helper import _prepare_tiles
23
+
24
+ __all__ = ['geom_livemap']
25
+
26
+
27
+ def geom_livemap(*,
28
+ location=None,
29
+ zoom=None,
30
+ projection=None,
31
+ tiles=None,
32
+ show_coord_pick_tools=None,
33
+ data_size_zoomin=None,
34
+ const_size_zoomin=None,
35
+ **other_args):
36
+ """
37
+ Display an interactive map.
38
+
39
+ Parameters
40
+ ----------
41
+ location : list
42
+ Initial position of the map. If not set, display the United States.
43
+ There are [lon1, lat1, lon2, lat2,..., lonN, latN]:
44
+ lon1, lon2,..., lonN are longitudes in degrees (positive in the Eastern hemisphere);
45
+ lat1, lat2,..., latN are latitudes in degrees (positive in the Northern hemisphere).
46
+ zoom : int
47
+ Zoom of the map in the range 1 - 15.
48
+ projection : str, default='epsg3857'
49
+ The map projection. There are: 'epsg3857' for Mercator projection;
50
+ 'epsg4326' for Equirectangular projection. ``projection`` only works
51
+ with vector map tiles (i.e. Lets-Plot map tiles).
52
+ tiles : str
53
+ Tile provider:
54
+
55
+ - pass a predefined constant from the ``tilesets`` module (Lets-Plot's vector tiles, e.g. `LETS_PLOT_COLOR <https://lets-plot.org/python/pages/api/lets_plot.tilesets.LETS_PLOT_COLOR.html>`__, or external raster tiles, e.g. `OPEN_TOPO_MAP <https://lets-plot.org/python/pages/api/lets_plot.tilesets.OPEN_TOPO_MAP.html>`__);
56
+ - pass a URL for a standard raster ZXY tile provider with {z}, {x} and {y} wildcards (e.g. 'http://my.tile.com/{z}/{x}/{y}.png') if the required tileset not present in the module;
57
+ - pass the result of a call to a `maptiles_zxy() <https://lets-plot.org/python/pages/api/lets_plot.maptiles_zxy.html>`__ function if further customisation is required (e.g. attribution or zoom).
58
+
59
+ More information about tiles can be found here:
60
+ https://lets-plot.org/python/pages/basemap_tiles.html
61
+ show_coord_pick_tools : bool, default=False
62
+ Show buttons "copy location" and "draw geometry".
63
+ data_size_zoomin : int, default=0
64
+ Control how zooming-in of the map widget increases size of geometry objects (circles, lines etc.) on map
65
+ when the size is set by means of mapping between the data and the ``size`` aesthetic.
66
+
67
+ 0 - size never increases;
68
+
69
+ -1 - size will be increasing without limits;
70
+
71
+ n - a number of zooming-in steps (counting from the initial state of the map widget)
72
+ when size of objects will be increasing. Farther zooming will no longer affect the size.
73
+
74
+ const_size_zoomin : int, default=-1
75
+ Control how zooming-in of the map widget increases size of geometry objects (circles, lines etc.) on map
76
+ when the size is not linked to a data (i.e. constant size).
77
+
78
+ 0 - size never increases;
79
+
80
+ -1 - size will be increasing without limits;
81
+
82
+ n - a number of zooming-in steps (counting from the initial state of the map widget)
83
+ when size of objects will be increasing. Farther zooming will no longer affect the size.
84
+
85
+ other_args
86
+ Other arguments passed on to the layer.
87
+
88
+ Returns
89
+ -------
90
+ ``LayerSpec``
91
+ Geom object specification.
92
+
93
+ Notes
94
+ -----
95
+ ``geom_livemap()`` draws a map, which can be dragged and zoomed.
96
+
97
+ ----
98
+
99
+ By default the livemap area has a non-zero inset. You can get rid of this with the theme: ``theme(plot_inset=0)``.
100
+
101
+ ---
102
+
103
+ When drawing a path with two points, the shortest route is taken. To create a longer arc, add intermediate points.
104
+
105
+ Examples
106
+ --------
107
+ .. jupyter-execute::
108
+ :linenos:
109
+ :emphasize-lines: 3
110
+
111
+ from lets_plot import *
112
+ LetsPlot.setup_html()
113
+ ggplot() + geom_livemap()
114
+
115
+ |
116
+
117
+ .. jupyter-execute::
118
+ :linenos:
119
+ :emphasize-lines: 10
120
+
121
+ from lets_plot import *
122
+ from lets_plot import tilesets
123
+ LetsPlot.setup_html()
124
+ data = {
125
+ 'city': ['New York City', 'Prague'],
126
+ 'lon': [-73.7997, 14.418540],
127
+ 'lat': [40.6408, 50.073658],
128
+ }
129
+ ggplot(data, aes(x='lon', y='lat')) + \\
130
+ geom_livemap(projection='epsg4326', tiles=tilesets.LETS_PLOT_DARK) + \\
131
+ geom_path(color='white', geodesic=True) + \\
132
+ geom_point(color='white', tooltips=layer_tooltips().line('@city')) + \\
133
+ ggtitle("The shortest path between New York and Prague")
134
+
135
+ |
136
+
137
+ .. jupyter-execute::
138
+ :linenos:
139
+ :emphasize-lines: 9
140
+
141
+ from lets_plot import *
142
+ LetsPlot.setup_html()
143
+ data = {
144
+ 'x': [-170, 170, -170, 0, 170],
145
+ 'y': [10, 10, -10, -10, -10],
146
+ 'route': ['A', 'A', 'B', 'B', 'B'],
147
+ }
148
+ ggplot(data) + \\
149
+ geom_livemap(zoom=1, location=[180, 0]) + \\
150
+ geom_path(aes('x', 'y', color='route'), size=1) + \\
151
+ scale_color_manual(values=['red', 'green'],
152
+ labels={'A': "'x': [-170, 170]",
153
+ 'B': "'x': [-170, 0, 170]"}) + \\
154
+ ggtitle("A path that crosses the antimeridian")
155
+
156
+ """
157
+ if 'symbol' in other_args:
158
+ print("WARN: The parameter 'symbol' is no longer supported. "
159
+ "Use separate geom_point() or geom_pie() geometry layers to display markers on the map.")
160
+ other_args.pop('symbol')
161
+
162
+ deprecated_params = set.intersection(
163
+ {'data', 'mapping', 'map', 'map_join', 'ontop', 'stat', 'position', 'show_legend', 'sampling', 'tooltips'},
164
+ other_args
165
+ )
166
+ if len(deprecated_params) > 0:
167
+ print(f"WARN: These parameters are not supported and will be ignored: {str(deprecated_params):s}. "
168
+ "Specify a separate geometry layer to display data on the livemap.")
169
+
170
+ for param in deprecated_params:
171
+ other_args.pop(param)
172
+
173
+ if location is not None:
174
+ location = _prepare_location(location)
175
+
176
+ tiles = _prepare_tiles(tiles)
177
+ geocoding = _prepare_geocoding()
178
+
179
+ return _geom('livemap',
180
+ mapping=None,
181
+ data=None,
182
+ stat=None,
183
+ position=None,
184
+ show_legend=None,
185
+ sampling=None,
186
+ tooltips=None,
187
+ map=None, map_join=None,
188
+ location=location,
189
+ zoom=zoom,
190
+ projection=projection,
191
+ tiles=tiles,
192
+ geocoding=geocoding,
193
+ show_coord_pick_tools=show_coord_pick_tools,
194
+ data_size_zoomin=data_size_zoomin,
195
+ const_size_zoomin=const_size_zoomin,
196
+ **other_args
197
+ )
198
+
199
+
200
+ LOCATION_COORDINATE_COLUMNS = {'lon', 'lat'}
201
+ LOCATION_RECTANGLE_COLUMNS = {'lonmin', 'latmin', 'lonmax', 'latmax'}
202
+ LOCATION_LIST_ERROR_MESSAGE = "Expected: location = [double lon1, double lat1, ... , double lonN, double latN]"
203
+ LOCATION_DATAFRAME_ERROR_MESSAGE = "Expected: location = DataFrame with [{}] or [{}] columns" \
204
+ .format(', '.join(LOCATION_COORDINATE_COLUMNS), ', '.join(LOCATION_RECTANGLE_COLUMNS))
205
+
206
+ OPTIONS_MAPTILES_KIND = 'kind'
207
+ OPTIONS_MAPTILES_URL = 'url'
208
+ OPTIONS_MAPTILES_THEME = 'theme'
209
+ OPTIONS_MAPTILES_ATTRIBUTION = 'attribution'
210
+ OPTIONS_MAPTILES_MIN_ZOOM = 'min_zoom'
211
+ OPTIONS_MAPTILES_MAX_ZOOM = 'max_zoom'
212
+ OPTIONS_MAPTILES_FILL_COLOR = 'fill_color'
213
+ OPTIONS_GEOCODING_PROVIDER_URL = 'url'
214
+
215
+
216
+ class RegionKind(Enum):
217
+ region_ids = 'region_ids'
218
+ region_name = 'region_name'
219
+ coordinates = 'coordinates'
220
+ data_frame = 'data_frame'
221
+
222
+
223
+ def _prepare_geocoding():
224
+ if has_global_value(GEOCODING_PROVIDER_URL):
225
+ return {
226
+ OPTIONS_GEOCODING_PROVIDER_URL: get_global_val(GEOCODING_PROVIDER_URL) + GEOCODING_ROUTE
227
+ }
228
+
229
+ return {}
230
+
231
+
232
+ def _prepare_tiles(tiles: Optional[Union[str, dict]]) -> Optional[dict]:
233
+ if isinstance(tiles, str):
234
+ return {
235
+ OPTIONS_MAPTILES_KIND: TILES_RASTER_ZXY,
236
+ OPTIONS_MAPTILES_URL: tiles
237
+ }
238
+
239
+ if isinstance(tiles, dict):
240
+ if tiles.get(MAPTILES_KIND) == TILES_RASTER_ZXY:
241
+ _warn_deprecated_tiles(tiles)
242
+ return {
243
+ OPTIONS_MAPTILES_KIND: TILES_RASTER_ZXY,
244
+ OPTIONS_MAPTILES_URL: tiles[MAPTILES_URL],
245
+ OPTIONS_MAPTILES_ATTRIBUTION: tiles[MAPTILES_ATTRIBUTION],
246
+ OPTIONS_MAPTILES_MIN_ZOOM: tiles[MAPTILES_MIN_ZOOM],
247
+ OPTIONS_MAPTILES_MAX_ZOOM: tiles[MAPTILES_MAX_ZOOM],
248
+ }
249
+ elif tiles.get(MAPTILES_KIND) == TILES_VECTOR_LETS_PLOT:
250
+ return {
251
+ OPTIONS_MAPTILES_KIND: TILES_VECTOR_LETS_PLOT,
252
+ OPTIONS_MAPTILES_URL: tiles[MAPTILES_URL],
253
+ OPTIONS_MAPTILES_THEME: tiles[MAPTILES_THEME],
254
+ OPTIONS_MAPTILES_ATTRIBUTION: tiles[MAPTILES_ATTRIBUTION],
255
+ }
256
+ elif tiles.get(MAPTILES_KIND) == TILES_SOLID:
257
+ return {
258
+ OPTIONS_MAPTILES_KIND: TILES_SOLID,
259
+ OPTIONS_MAPTILES_FILL_COLOR: tiles[MAPTILES_SOLID_FILL_COLOR]
260
+ }
261
+ elif tiles.get(MAPTILES_KIND) == TILES_CHESSBOARD:
262
+ return {
263
+ OPTIONS_MAPTILES_KIND: TILES_CHESSBOARD
264
+ }
265
+ else:
266
+ raise ValueError("Unsupported 'tiles' kind: " + tiles.get(MAPTILES_KIND))
267
+
268
+ if tiles is not None:
269
+ raise ValueError("Unsupported 'tiles' parameter type: " + type(tiles))
270
+
271
+ # tiles are not set for this livemap - try to get global tiles config
272
+ if has_global_value(MAPTILES_KIND):
273
+ if not has_global_value(MAPTILES_URL):
274
+ raise ValueError('URL for tiles service is not set')
275
+
276
+ if get_global_val(MAPTILES_KIND) == TILES_RASTER_ZXY:
277
+ _warn_deprecated_tiles(None)
278
+ return {
279
+ OPTIONS_MAPTILES_KIND: TILES_RASTER_ZXY,
280
+ OPTIONS_MAPTILES_URL: get_global_val(MAPTILES_URL),
281
+ OPTIONS_MAPTILES_ATTRIBUTION: get_global_val(MAPTILES_ATTRIBUTION) if has_global_value(
282
+ MAPTILES_ATTRIBUTION) else None,
283
+ OPTIONS_MAPTILES_MIN_ZOOM: get_global_val(MAPTILES_MIN_ZOOM) if has_global_value(
284
+ MAPTILES_MIN_ZOOM) else None,
285
+ OPTIONS_MAPTILES_MAX_ZOOM: get_global_val(MAPTILES_MAX_ZOOM) if has_global_value(
286
+ MAPTILES_MAX_ZOOM) else None,
287
+ }
288
+
289
+ if get_global_val(MAPTILES_KIND) == TILES_VECTOR_LETS_PLOT:
290
+ return {
291
+ OPTIONS_MAPTILES_KIND: TILES_VECTOR_LETS_PLOT,
292
+ OPTIONS_MAPTILES_URL: get_global_val(MAPTILES_URL),
293
+ OPTIONS_MAPTILES_THEME: get_global_val(MAPTILES_THEME) if has_global_value(MAPTILES_THEME) else None,
294
+ OPTIONS_MAPTILES_ATTRIBUTION: get_global_val(MAPTILES_ATTRIBUTION) if has_global_value(
295
+ MAPTILES_ATTRIBUTION) else None,
296
+ }
297
+
298
+ if get_global_val(MAPTILES_KIND) == TILES_SOLID:
299
+ return {
300
+ OPTIONS_MAPTILES_KIND: TILES_SOLID,
301
+ OPTIONS_MAPTILES_FILL_COLOR: get_global_val(MAPTILES_SOLID_FILL_COLOR),
302
+ }
303
+
304
+ raise ValueError('Tile provider is not set.')
305
+
306
+
307
+ def _warn_deprecated_tiles(tiles: Union[dict, None]):
308
+ if tiles is None:
309
+ maptiles_url = get_global_val(MAPTILES_URL)
310
+ else:
311
+ maptiles_url = tiles[MAPTILES_URL]
312
+
313
+ # Check if the current tiles should be deprecated and print a deprecation message. Otherwise, return.
314
+ return
315
+
316
+
317
+ def _prepare_location(location: Union[str, List[float]]) -> Optional[dict]:
318
+ if location is None:
319
+ return None
320
+
321
+ value = location
322
+ # if isinstance(location, Geocoder):
323
+ # kind = RegionKind.region_ids
324
+ # value = location.unique_ids()
325
+
326
+ if isinstance(location, str):
327
+ kind = RegionKind.region_name
328
+
329
+ elif isinstance(location, list):
330
+ if len(location) == 0 or len(location) % 2 != 0:
331
+ raise ValueError(LOCATION_LIST_ERROR_MESSAGE)
332
+ kind = RegionKind.coordinates
333
+
334
+ elif pandas and isinstance(location, pandas.DataFrame):
335
+ if not LOCATION_COORDINATE_COLUMNS.issubset(location.columns) and not LOCATION_RECTANGLE_COLUMNS.issubset(
336
+ location.columns):
337
+ raise ValueError(LOCATION_DATAFRAME_ERROR_MESSAGE)
338
+ kind = RegionKind.data_frame
339
+
340
+ else:
341
+ raise ValueError('Wrong location type: ' + location.__str__())
342
+
343
+ return {'type': kind.value, 'data': value}
@@ -0,0 +1,96 @@
1
+ # Copyright (c) 2025. JetBrains s.r.o.
2
+ # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
3
+
4
+ from numbers import Number
5
+ from typing import List, Tuple
6
+
7
+ from ._global_theme import _get_global_theme
8
+ from .subplots import SupPlotsLayoutSpec
9
+ from .subplots import SupPlotsSpec
10
+ from .subplots_util import _strip_theme_if_global
11
+
12
+ __all__ = ['ggbunch']
13
+
14
+
15
+ def ggbunch(plots: List,
16
+ regions: List[Tuple[float, float, float, float, float, float]]
17
+ ) -> SupPlotsSpec:
18
+ """
19
+ Combine several plots into a single figure with custom layout.
20
+
21
+ Parameters
22
+ ----------
23
+ plots : List
24
+ A list where each element is one of:
25
+
26
+ - a plot specification
27
+ - a subplots specification
28
+ - None
29
+
30
+ regions : List[Tuple]
31
+ Layout parameters for each plot. Each region is specified as
32
+ (x, y, width, height, dx, dy) where:
33
+
34
+ - x, y: Position of the plot's top-left corner in relative coordinates ([0,0] is top-left corner, [1,1] is bottom-right corner of the container).
35
+ - width, height: Size of the plot relative to container dimensions (1 equal to the full container width/height).
36
+ - dx, dy: Pixel offsets to move the region (defaults to 0).
37
+
38
+ Returns
39
+ -------
40
+ ``SupPlotsSpec``
41
+ A specification describing the combined figure with all plots and their layout.
42
+
43
+ Examples
44
+ --------
45
+ .. jupyter-execute::
46
+ :linenos:
47
+ :emphasize-lines: 10-14
48
+
49
+ import numpy as np
50
+ from lets_plot import *
51
+ LetsPlot.setup_html()
52
+ np.random.seed(42)
53
+ data = {'x': np.random.gamma(2.0, size=100)}
54
+ p1 = ggplot(data, aes(x='x')) + \\
55
+ geom_histogram(aes(color='x', fill='x'))
56
+ p2 = ggplot(data, aes(x='x')) + \\
57
+ geom_density() + theme_bw() + theme(axis='blank', panel_grid='blank')
58
+ ggbunch(
59
+ [p1, p2],
60
+ [(0, 0, 1, 1),
61
+ (0.5, 0.1, 0.3, 0.3)]
62
+ ) + ggsize(400, 300)
63
+
64
+ """
65
+
66
+ if not len(plots):
67
+ raise ValueError("Supplots list is empty.")
68
+
69
+ # Validate provided regions
70
+ for i, region in enumerate(regions):
71
+ if len(region) not in (4, 6):
72
+ raise ValueError(f"Region {i} must have 4 or 6 values, got {len(region)}")
73
+ if not all(isinstance(x, Number) for x in region):
74
+ raise ValueError(f"Region {i} contains non-numeric values: {region}")
75
+
76
+ # Validate size is positive
77
+ if any(x <= 0 for x in region[2:4]):
78
+ raise ValueError(f"Region {i} sizes must be positive: {region}")
79
+
80
+ # Convert regions tuples to lists
81
+ regions_list = [list(r) for r in regions]
82
+ layout = SupPlotsLayoutSpec(
83
+ name="free",
84
+ regions=regions_list
85
+ )
86
+
87
+ figures = [_strip_theme_if_global(fig) for fig in plots]
88
+
89
+ figure_spec = SupPlotsSpec(figures=figures, layout=layout)
90
+
91
+ # Apply global theme if defined
92
+ global_theme_options = _get_global_theme()
93
+ if global_theme_options is not None:
94
+ figure_spec += global_theme_options
95
+
96
+ return figure_spec
@@ -0,0 +1,139 @@
1
+ #
2
+ # Copyright (c) 2023. JetBrains s.r.o.
3
+ # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4
+ #
5
+
6
+ from ._global_theme import _get_global_theme
7
+ from .subplots import SupPlotsLayoutSpec
8
+ from .subplots import SupPlotsSpec
9
+ from .subplots_util import _strip_theme_if_global
10
+
11
+ __all__ = ['gggrid']
12
+
13
+
14
+ def gggrid(plots: list, ncol: int = None, *,
15
+ sharex: str = None,
16
+ sharey: str = None,
17
+ widths: list = None,
18
+ heights: list = None,
19
+ hspace: float = None,
20
+ vspace: float = None,
21
+ fit: bool = None,
22
+ align: bool = None,
23
+ guides: str = None
24
+ ) -> SupPlotsSpec:
25
+ """
26
+ Combine several plots on one figure, organized in a regular grid.
27
+
28
+ Parameters
29
+ ----------
30
+ plots : list
31
+ A list where each element is a plot specification, a subplot specification, or None.
32
+ Use None to fill in empty cells in the grid.
33
+ ncol : int
34
+ Number of columns in the grid.
35
+ If not specified, shows plots horizontally, in one row.
36
+ sharex, sharey : bool or str, default=False
37
+ Controls sharing of axis limits between subplots in the grid.
38
+
39
+ - 'all'/True - share limits between all subplots.
40
+ - 'none'/False - do not share limits between subplots.
41
+ - 'row' - share limits between subplots in the same row.
42
+ - 'col' - share limits between subplots in the same column.
43
+
44
+ widths : list of numbers
45
+ Relative width of each column in the grid, left to right.
46
+ heights : list of numbers
47
+ Relative height of each row in the grid, top-down.
48
+ hspace : float, default=4.0
49
+ Cell horizontal spacing in px.
50
+ vspace : float, default=4.0
51
+ Cell vertical spacing in px.
52
+ fit : bool, default=True
53
+ Whether to stretch each plot to match the aspect ratio of its cell (``fit=True``),
54
+ or to preserve the original aspect ratio of plots (``fit=False``).
55
+ align : bool, default=False
56
+ If True, align inner areas (i.e. "geom" bounds) of plots.
57
+ However, cells containing other (sub)grids are not participating in the plot "inner areas" layouting.
58
+ guides : str, default='auto'
59
+ Specifies how guides (legends and colorbars) should be treated in the layout.
60
+
61
+ - 'collect' - collect guides from all subplots, removing duplicates.
62
+ - 'keep' - keep guides in their original subplots; do not collect at this level.
63
+ - 'auto' - allow guides to be collected if an upper-level layout uses ``guides='collect'``; otherwise, keep them in subplots.
64
+
65
+ Duplicates are identified by comparing visual properties:
66
+
67
+ - For legends: title, labels, and all aesthetic values (colors, shapes, sizes, etc.).
68
+ - For colorbars: title, domain limits, breaks, and color gradient.
69
+
70
+ Returns
71
+ -------
72
+ ``SupPlotsSpec``
73
+ The grid specification.
74
+
75
+ Examples
76
+ --------
77
+ .. jupyter-execute::
78
+ :linenos:
79
+ :emphasize-lines: 11, 14
80
+
81
+ import numpy as np
82
+ from lets_plot import *
83
+ LetsPlot.setup_html()
84
+ np.random.seed(42)
85
+ n = 100
86
+ x = np.arange(n)
87
+ y = np.random.normal(size=n)
88
+ w, h = 200, 150
89
+ p = ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + ggsize(w, h)
90
+ plot_list=[
91
+ gggrid([p+geom_point(), p+geom_histogram(bins=3)]),
92
+ p+geom_line()
93
+ ]
94
+ gggrid(plot_list, ncol=1) + ggsize(400, 300)
95
+
96
+ """
97
+
98
+ if not len(plots):
99
+ raise ValueError("Supplots list is empty.")
100
+
101
+ if ncol is None:
102
+ ncol = len(plots)
103
+ nrow = 1
104
+ else:
105
+ extended_list = plots + [None] * (ncol - 1)
106
+ nrow = len(extended_list) // ncol
107
+ length = ncol * nrow
108
+ plots = extended_list[0:length]
109
+
110
+ if sharex is not None and type(sharex) != str:
111
+ sharex = 'all' if sharex else 'none'
112
+ if sharey is not None and type(sharey) != str:
113
+ sharey = 'all' if sharey else 'none'
114
+
115
+ layout = SupPlotsLayoutSpec(
116
+ name="grid",
117
+ ncol=ncol,
118
+ nrow=nrow,
119
+ sharex=sharex,
120
+ sharey=sharey,
121
+ widths=widths,
122
+ heights=heights,
123
+ hspace=hspace,
124
+ vspace=vspace,
125
+ fit=fit,
126
+ align=align,
127
+ guides=guides
128
+ )
129
+
130
+ figures = [_strip_theme_if_global(fig) for fig in plots]
131
+
132
+ figure_spec = SupPlotsSpec(figures=figures, layout=layout)
133
+
134
+ # Apply global theme if defined
135
+ global_theme_options = _get_global_theme()
136
+ if global_theme_options is not None:
137
+ figure_spec += global_theme_options
138
+
139
+ return figure_spec
@@ -0,0 +1,81 @@
1
+ #
2
+ # Copyright (c) 2024. JetBrains s.r.o.
3
+ # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4
+ #
5
+ from .core import FeatureSpec
6
+
7
+ __all__ = ['ggtb']
8
+
9
+
10
+ def ggtb(size_basis=None, size_zoomin=None) -> FeatureSpec:
11
+ """
12
+ Add a toolbar to a chart.
13
+
14
+ This function adds a toolbar containing three tool-buttons (pan, rubber-band zoom,
15
+ and center-point zoom) to a chart. Each tool uses mouse-drag for its
16
+ specific functionality. Additionally, the mouse wheel can be used for zooming
17
+ in and out, regardless of the selected tool.
18
+
19
+ The toolbar includes:
20
+
21
+ - Pan: Drag to move the plot.
22
+ - Rubber-band zoom: Drag to define a rectangular area to zoom into.
23
+ - Center-point zoom: Drag up or down to zoom in or out from a center point.
24
+ - Reset button: Click to reset the plot and tools to their original state.
25
+
26
+ Double-clicking anywhere on the plot resets it to its original coordinates,
27
+ regardless of whether a tool is selected or not.
28
+
29
+ Limitations:
30
+
31
+ - The toolbar does not work with interactive maps.
32
+ - The toolbar cannot be used with plots using a polar coordinate system.
33
+
34
+ Parameters
35
+ ----------
36
+ size_zoomin : int, default=0
37
+ Control how zooming in affects the size of geometry objects on the plot. Currently, works only with
38
+ the geom_point layer and layers based on it (geom_jitter, geom_sina, etc.).
39
+
40
+ 0 - size never increases;
41
+
42
+ -1 - size will be increasing without limits;
43
+
44
+ n - the number of times the size of objects will increase (relative to the initial state of the plot).
45
+ Farther zooming will no longer affect the size.
46
+
47
+ size_basis : {'x', 'y', 'min', 'max'}, default='max'
48
+ Defines the axis along which the scaling factor for geometry objects will be calculated.
49
+
50
+ 'x' - size changes only when zooming in/out along x-axis;
51
+
52
+ 'y' - size changes only when zooming in/out along y-axis;
53
+
54
+ 'min' - size changes when zooming in/out along any axis, but the change is determined by the axis
55
+ with the minimum zoom factor;
56
+
57
+ 'max' - size changes when zooming in/out along any axis, but the change is determined by the axis
58
+ with the maximum zoom factor.
59
+
60
+ Returns
61
+ -------
62
+ ``FeatureSpec``
63
+ Toolbar feature specification.
64
+
65
+ Examples
66
+ --------
67
+ .. jupyter-execute::
68
+ :linenos:
69
+ :emphasize-lines: 8
70
+
71
+ import numpy as np
72
+ from lets_plot import *
73
+ LetsPlot.setup_html()
74
+ x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
75
+ y = np.sin(x)
76
+ ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + \\
77
+ geom_point() + \\
78
+ ggtb()
79
+
80
+ """
81
+ return FeatureSpec(kind='ggtoolbar', name=None, size_basis=size_basis, size_zoomin=size_zoomin)