lets-plot 4.7.0rc1__cp310-cp310-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.

Potentially problematic release.


This version of lets-plot might be problematic. Click here for more details.

Files changed (95) hide show
  1. lets_plot/__init__.py +283 -0
  2. lets_plot/_global_settings.py +196 -0
  3. lets_plot/_kbridge.py +141 -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 +100 -0
  8. lets_plot/bistro/corr.py +447 -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 +333 -0
  14. lets_plot/export/__init__.py +6 -0
  15. lets_plot/export/ggsave_.py +141 -0
  16. lets_plot/frontend_context/__init__.py +8 -0
  17. lets_plot/frontend_context/_configuration.py +151 -0
  18. lets_plot/frontend_context/_frontend_ctx.py +16 -0
  19. lets_plot/frontend_context/_html_contexts.py +117 -0
  20. lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
  21. lets_plot/frontend_context/_json_contexts.py +39 -0
  22. lets_plot/frontend_context/_jupyter_notebook_ctx.py +119 -0
  23. lets_plot/frontend_context/_mime_types.py +7 -0
  24. lets_plot/frontend_context/_static_html_page_ctx.py +27 -0
  25. lets_plot/frontend_context/_static_svg_ctx.py +26 -0
  26. lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
  27. lets_plot/frontend_context/sandbox.py +5 -0
  28. lets_plot/geo_data/__init__.py +19 -0
  29. lets_plot/geo_data/core.py +331 -0
  30. lets_plot/geo_data/geocoder.py +977 -0
  31. lets_plot/geo_data/geocodes.py +512 -0
  32. lets_plot/geo_data/gis/__init__.py +0 -0
  33. lets_plot/geo_data/gis/fluent_dict.py +201 -0
  34. lets_plot/geo_data/gis/geocoding_service.py +42 -0
  35. lets_plot/geo_data/gis/geometry.py +91 -0
  36. lets_plot/geo_data/gis/json_request.py +232 -0
  37. lets_plot/geo_data/gis/json_response.py +308 -0
  38. lets_plot/geo_data/gis/request.py +492 -0
  39. lets_plot/geo_data/gis/response.py +247 -0
  40. lets_plot/geo_data/livemap_helper.py +65 -0
  41. lets_plot/geo_data/to_geo_data_frame.py +141 -0
  42. lets_plot/geo_data/type_assertion.py +34 -0
  43. lets_plot/geo_data_internals/__init__.py +4 -0
  44. lets_plot/geo_data_internals/constants.py +13 -0
  45. lets_plot/geo_data_internals/utils.py +33 -0
  46. lets_plot/mapping.py +115 -0
  47. lets_plot/package_data/lets-plot.min.js +3 -0
  48. lets_plot/plot/__init__.py +64 -0
  49. lets_plot/plot/_global_theme.py +14 -0
  50. lets_plot/plot/annotation.py +290 -0
  51. lets_plot/plot/coord.py +242 -0
  52. lets_plot/plot/core.py +1060 -0
  53. lets_plot/plot/expand_limits_.py +78 -0
  54. lets_plot/plot/facet.py +206 -0
  55. lets_plot/plot/font_features.py +71 -0
  56. lets_plot/plot/geom.py +8839 -0
  57. lets_plot/plot/geom_extras.py +53 -0
  58. lets_plot/plot/geom_function_.py +216 -0
  59. lets_plot/plot/geom_imshow_.py +392 -0
  60. lets_plot/plot/geom_livemap_.py +310 -0
  61. lets_plot/plot/ggbunch_.py +96 -0
  62. lets_plot/plot/gggrid_.py +126 -0
  63. lets_plot/plot/ggtb_.py +55 -0
  64. lets_plot/plot/guide.py +229 -0
  65. lets_plot/plot/label.py +187 -0
  66. lets_plot/plot/marginal_layer.py +181 -0
  67. lets_plot/plot/plot.py +244 -0
  68. lets_plot/plot/pos.py +320 -0
  69. lets_plot/plot/sampling.py +338 -0
  70. lets_plot/plot/sandbox_.py +26 -0
  71. lets_plot/plot/scale.py +3577 -0
  72. lets_plot/plot/scale_colormap_mpl.py +297 -0
  73. lets_plot/plot/scale_convenience.py +155 -0
  74. lets_plot/plot/scale_identity_.py +658 -0
  75. lets_plot/plot/scale_position.py +1342 -0
  76. lets_plot/plot/series_meta.py +203 -0
  77. lets_plot/plot/stat.py +581 -0
  78. lets_plot/plot/subplots.py +322 -0
  79. lets_plot/plot/subplots_util.py +24 -0
  80. lets_plot/plot/theme_.py +772 -0
  81. lets_plot/plot/theme_set.py +393 -0
  82. lets_plot/plot/tooltip.py +486 -0
  83. lets_plot/plot/util.py +237 -0
  84. lets_plot/settings_utils.py +244 -0
  85. lets_plot/tilesets.py +429 -0
  86. lets_plot-4.7.0rc1.dist-info/METADATA +196 -0
  87. lets_plot-4.7.0rc1.dist-info/RECORD +95 -0
  88. lets_plot-4.7.0rc1.dist-info/WHEEL +6 -0
  89. lets_plot-4.7.0rc1.dist-info/licenses/LICENSE +21 -0
  90. lets_plot-4.7.0rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
  91. lets_plot-4.7.0rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
  92. lets_plot-4.7.0rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
  93. lets_plot-4.7.0rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
  94. lets_plot-4.7.0rc1.dist-info/top_level.txt +2 -0
  95. lets_plot_kotlin_bridge.cpython-310-x86_64-linux-gnu.so +0 -0
@@ -0,0 +1,310 @@
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`, or external raster tiles, e.g. `OPEN_TOPO_MAP`);
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()` 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
+ `0` - size never increases;
67
+ `-1` - size will be increasing without limits;
68
+ `n` - a number of zooming-in steps (counting from the initial state of the map widget)
69
+ when size of objects will be increasing. Farther zooming will no longer affect the size.
70
+ const_size_zoomin : int, default=-1
71
+ Control how zooming-in of the map widget increases size of geometry objects (circles, lines etc.) on map
72
+ when the size is not linked to a data (i.e. constant size).
73
+ `0` - size never increases;
74
+ `-1` - size will be increasing without limits;
75
+ `n` - a number of zooming-in steps (counting from the initial state of the map widget)
76
+ when size of objects will be increasing. Farther zooming will no longer affect the size.
77
+ other_args
78
+ Other arguments passed on to the layer.
79
+
80
+ Returns
81
+ -------
82
+ `LayerSpec`
83
+ Geom object specification.
84
+
85
+ Notes
86
+ -----
87
+ `geom_livemap()` draws a map, which can be dragged and zoomed.
88
+
89
+ ----
90
+
91
+ By default the livemap area has a non-zero inset. You can get rid of this with the theme: `theme(plot_inset=0)`.
92
+
93
+ Examples
94
+ --------
95
+ .. jupyter-execute::
96
+ :linenos:
97
+ :emphasize-lines: 3
98
+
99
+ from lets_plot import *
100
+ LetsPlot.setup_html()
101
+ ggplot() + geom_livemap()
102
+
103
+ |
104
+
105
+ .. jupyter-execute::
106
+ :linenos:
107
+ :emphasize-lines: 10
108
+
109
+ from lets_plot import *
110
+ from lets_plot import tilesets
111
+ LetsPlot.setup_html()
112
+ data = {
113
+ 'city': ['New York City', 'Prague'],
114
+ 'lon': [-73.7997, 14.418540],
115
+ 'lat': [40.6408, 50.073658],
116
+ }
117
+ ggplot(data, aes(x='lon', y='lat')) + \\
118
+ geom_livemap(projection='epsg4326', tiles=tilesets.LETS_PLOT_DARK) + \\
119
+ geom_path(color='white', geodesic=True) + \\
120
+ geom_point(color='white', tooltips=layer_tooltips().line('@city')) + \\
121
+ ggtitle("The shortest path between New York and Prague")
122
+
123
+ """
124
+ if 'symbol' in other_args:
125
+ print("WARN: The parameter 'symbol' is no longer supported. "
126
+ "Use separate geom_point() or geom_pie() geometry layers to display markers on the map.")
127
+ other_args.pop('symbol')
128
+
129
+ deprecated_params = set.intersection(
130
+ {'data', 'mapping', 'map', 'map_join', 'ontop', 'stat', 'position', 'show_legend', 'sampling', 'tooltips'},
131
+ other_args
132
+ )
133
+ if len(deprecated_params) > 0:
134
+ print(f"WARN: These parameters are not supported and will be ignored: {str(deprecated_params):s}. "
135
+ "Specify a separate geometry layer to display data on the livemap.")
136
+
137
+ for param in deprecated_params:
138
+ other_args.pop(param)
139
+
140
+ if location is not None:
141
+ location = _prepare_location(location)
142
+
143
+ tiles = _prepare_tiles(tiles)
144
+ geocoding = _prepare_geocoding()
145
+
146
+ return _geom('livemap',
147
+ mapping=None,
148
+ data=None,
149
+ stat=None,
150
+ position=None,
151
+ show_legend=None,
152
+ sampling=None,
153
+ tooltips=None,
154
+ map=None, map_join=None,
155
+ location=location,
156
+ zoom=zoom,
157
+ projection=projection,
158
+ tiles=tiles,
159
+ geocoding=geocoding,
160
+ show_coord_pick_tools=show_coord_pick_tools,
161
+ data_size_zoomin=data_size_zoomin,
162
+ const_size_zoomin=const_size_zoomin,
163
+ **other_args
164
+ )
165
+
166
+
167
+ LOCATION_COORDINATE_COLUMNS = {'lon', 'lat'}
168
+ LOCATION_RECTANGLE_COLUMNS = {'lonmin', 'latmin', 'lonmax', 'latmax'}
169
+ LOCATION_LIST_ERROR_MESSAGE = "Expected: location = [double lon1, double lat1, ... , double lonN, double latN]"
170
+ LOCATION_DATAFRAME_ERROR_MESSAGE = "Expected: location = DataFrame with [{}] or [{}] columns" \
171
+ .format(', '.join(LOCATION_COORDINATE_COLUMNS), ', '.join(LOCATION_RECTANGLE_COLUMNS))
172
+
173
+ OPTIONS_MAPTILES_KIND = 'kind'
174
+ OPTIONS_MAPTILES_URL = 'url'
175
+ OPTIONS_MAPTILES_THEME = 'theme'
176
+ OPTIONS_MAPTILES_ATTRIBUTION = 'attribution'
177
+ OPTIONS_MAPTILES_MIN_ZOOM = 'min_zoom'
178
+ OPTIONS_MAPTILES_MAX_ZOOM = 'max_zoom'
179
+ OPTIONS_MAPTILES_FILL_COLOR = 'fill_color'
180
+ OPTIONS_GEOCODING_PROVIDER_URL = 'url'
181
+
182
+
183
+ class RegionKind(Enum):
184
+ region_ids = 'region_ids'
185
+ region_name = 'region_name'
186
+ coordinates = 'coordinates'
187
+ data_frame = 'data_frame'
188
+
189
+
190
+ def _prepare_geocoding():
191
+ if has_global_value(GEOCODING_PROVIDER_URL):
192
+ return {
193
+ OPTIONS_GEOCODING_PROVIDER_URL: get_global_val(GEOCODING_PROVIDER_URL) + GEOCODING_ROUTE
194
+ }
195
+
196
+ return {}
197
+
198
+
199
+ def _prepare_tiles(tiles: Optional[Union[str, dict]]) -> Optional[dict]:
200
+ if isinstance(tiles, str):
201
+ return {
202
+ OPTIONS_MAPTILES_KIND: TILES_RASTER_ZXY,
203
+ OPTIONS_MAPTILES_URL: tiles
204
+ }
205
+
206
+ if isinstance(tiles, dict):
207
+ if tiles.get(MAPTILES_KIND) == TILES_RASTER_ZXY:
208
+ _warn_deprecated_tiles(tiles)
209
+ return {
210
+ OPTIONS_MAPTILES_KIND: TILES_RASTER_ZXY,
211
+ OPTIONS_MAPTILES_URL: tiles[MAPTILES_URL],
212
+ OPTIONS_MAPTILES_ATTRIBUTION: tiles[MAPTILES_ATTRIBUTION],
213
+ OPTIONS_MAPTILES_MIN_ZOOM: tiles[MAPTILES_MIN_ZOOM],
214
+ OPTIONS_MAPTILES_MAX_ZOOM: tiles[MAPTILES_MAX_ZOOM],
215
+ }
216
+ elif tiles.get(MAPTILES_KIND) == TILES_VECTOR_LETS_PLOT:
217
+ return {
218
+ OPTIONS_MAPTILES_KIND: TILES_VECTOR_LETS_PLOT,
219
+ OPTIONS_MAPTILES_URL: tiles[MAPTILES_URL],
220
+ OPTIONS_MAPTILES_THEME: tiles[MAPTILES_THEME],
221
+ OPTIONS_MAPTILES_ATTRIBUTION: tiles[MAPTILES_ATTRIBUTION],
222
+ }
223
+ elif tiles.get(MAPTILES_KIND) == TILES_SOLID:
224
+ return {
225
+ OPTIONS_MAPTILES_KIND: TILES_SOLID,
226
+ OPTIONS_MAPTILES_FILL_COLOR: tiles[MAPTILES_SOLID_FILL_COLOR]
227
+ }
228
+ elif tiles.get(MAPTILES_KIND) == TILES_CHESSBOARD:
229
+ return {
230
+ OPTIONS_MAPTILES_KIND: TILES_CHESSBOARD
231
+ }
232
+ else:
233
+ raise ValueError("Unsupported 'tiles' kind: " + tiles.get(MAPTILES_KIND))
234
+
235
+ if tiles is not None:
236
+ raise ValueError("Unsupported 'tiles' parameter type: " + type(tiles))
237
+
238
+ # tiles are not set for this livemap - try to get global tiles config
239
+ if has_global_value(MAPTILES_KIND):
240
+ if not has_global_value(MAPTILES_URL):
241
+ raise ValueError('URL for tiles service is not set')
242
+
243
+ if get_global_val(MAPTILES_KIND) == TILES_RASTER_ZXY:
244
+ _warn_deprecated_tiles(None)
245
+ return {
246
+ OPTIONS_MAPTILES_KIND: TILES_RASTER_ZXY,
247
+ OPTIONS_MAPTILES_URL: get_global_val(MAPTILES_URL),
248
+ OPTIONS_MAPTILES_ATTRIBUTION: get_global_val(MAPTILES_ATTRIBUTION) if has_global_value(
249
+ MAPTILES_ATTRIBUTION) else None,
250
+ OPTIONS_MAPTILES_MIN_ZOOM: get_global_val(MAPTILES_MIN_ZOOM) if has_global_value(
251
+ MAPTILES_MIN_ZOOM) else None,
252
+ OPTIONS_MAPTILES_MAX_ZOOM: get_global_val(MAPTILES_MAX_ZOOM) if has_global_value(
253
+ MAPTILES_MAX_ZOOM) else None,
254
+ }
255
+
256
+ if get_global_val(MAPTILES_KIND) == TILES_VECTOR_LETS_PLOT:
257
+ return {
258
+ OPTIONS_MAPTILES_KIND: TILES_VECTOR_LETS_PLOT,
259
+ OPTIONS_MAPTILES_URL: get_global_val(MAPTILES_URL),
260
+ OPTIONS_MAPTILES_THEME: get_global_val(MAPTILES_THEME) if has_global_value(MAPTILES_THEME) else None,
261
+ OPTIONS_MAPTILES_ATTRIBUTION: get_global_val(MAPTILES_ATTRIBUTION) if has_global_value(
262
+ MAPTILES_ATTRIBUTION) else None,
263
+ }
264
+
265
+ if get_global_val(MAPTILES_KIND) == TILES_SOLID:
266
+ return {
267
+ OPTIONS_MAPTILES_KIND: TILES_SOLID,
268
+ OPTIONS_MAPTILES_FILL_COLOR: get_global_val(MAPTILES_SOLID_FILL_COLOR),
269
+ }
270
+
271
+ raise ValueError('Tile provider is not set.')
272
+
273
+
274
+ def _warn_deprecated_tiles(tiles: Union[dict, None]):
275
+ if tiles is None:
276
+ maptiles_url = get_global_val(MAPTILES_URL)
277
+ else:
278
+ maptiles_url = tiles[MAPTILES_URL]
279
+
280
+ # Check if the current tiles should be deprecated and print a deprecation message. Otherwise, return.
281
+ return
282
+
283
+
284
+ def _prepare_location(location: Union[str, List[float]]) -> Optional[dict]:
285
+ if location is None:
286
+ return None
287
+
288
+ value = location
289
+ # if isinstance(location, Geocoder):
290
+ # kind = RegionKind.region_ids
291
+ # value = location.unique_ids()
292
+
293
+ if isinstance(location, str):
294
+ kind = RegionKind.region_name
295
+
296
+ elif isinstance(location, list):
297
+ if len(location) == 0 or len(location) % 2 != 0:
298
+ raise ValueError(LOCATION_LIST_ERROR_MESSAGE)
299
+ kind = RegionKind.coordinates
300
+
301
+ elif pandas and isinstance(location, pandas.DataFrame):
302
+ if not LOCATION_COORDINATE_COLUMNS.issubset(location.columns) and not LOCATION_RECTANGLE_COLUMNS.issubset(
303
+ location.columns):
304
+ raise ValueError(LOCATION_DATAFRAME_ERROR_MESSAGE)
305
+ kind = RegionKind.data_frame
306
+
307
+ else:
308
+ raise ValueError('Wrong location type: ' + location.__str__())
309
+
310
+ 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,126 @@
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
+ ) -> SupPlotsSpec:
24
+ """
25
+ Combine several plots on one figure, organized in a regular grid.
26
+
27
+ Parameters
28
+ ----------
29
+ plots : list
30
+ A list where each element is a plot specification, a subplots specification, or None.
31
+ Use value None to fill-in empty cells in grid.
32
+ ncol : int
33
+ Number of columns in grid.
34
+ If not specified, shows plots horizontally, in one row.
35
+ sharex, sharey : bool or str, default=False
36
+ Controls sharing of axis limits between subplots in the grid.
37
+
38
+ - 'all'/True - share limits between all subplots.
39
+ - 'none'/False - do not share limits between subplots.
40
+ - 'row' - share limits between subplots in the same row.
41
+ - 'col' - share limits between subplots in the same column.
42
+
43
+ widths : list of numbers
44
+ Relative width of each column of grid, left to right.
45
+ heights : list of numbers
46
+ Relative height of each row of grid, top-down.
47
+ hspace : float, default=4.0
48
+ Cell horizontal spacing in px.
49
+ vspace : float, default=4.0
50
+ Cell vertical spacing in px.
51
+ fit : bool, default=True
52
+ Whether to stretch each plot to match the aspect ratio of its cell (`fit=True`),
53
+ or to preserve the original aspect ratio of plots (`fit=False`).
54
+ align : bool, default=False
55
+ If True, align inner areas (i.e. "geom" bounds) of plots.
56
+ However, cells containing other (sub)grids are not participating in the plot "inner areas" layouting.
57
+
58
+ Returns
59
+ -------
60
+ `SupPlotsSpec`
61
+ The grid specification.
62
+
63
+ Examples
64
+ --------
65
+ .. jupyter-execute::
66
+ :linenos:
67
+ :emphasize-lines: 11, 14
68
+
69
+ import numpy as np
70
+ from lets_plot import *
71
+ LetsPlot.setup_html()
72
+ np.random.seed(42)
73
+ n = 100
74
+ x = np.arange(n)
75
+ y = np.random.normal(size=n)
76
+ w, h = 200, 150
77
+ p = ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + ggsize(w, h)
78
+ plot_list=[
79
+ gggrid([p+geom_point(), p+geom_histogram(bins=3)]),
80
+ p+geom_line()
81
+ ]
82
+ gggrid(plot_list, ncol=1) + ggsize(400, 300)
83
+
84
+ """
85
+
86
+ if not len(plots):
87
+ raise ValueError("Supplots list is empty.")
88
+
89
+ if ncol is None:
90
+ ncol = len(plots)
91
+ nrow = 1
92
+ else:
93
+ extended_list = plots + [None] * (ncol - 1)
94
+ nrow = len(extended_list) // ncol
95
+ length = ncol * nrow
96
+ plots = extended_list[0:length]
97
+
98
+ if sharex is not None and type(sharex) != str:
99
+ sharex = 'all' if sharex else 'none'
100
+ if sharey is not None and type(sharey) != str:
101
+ sharey = 'all' if sharey else 'none'
102
+
103
+ layout = SupPlotsLayoutSpec(
104
+ name="grid",
105
+ ncol=ncol,
106
+ nrow=nrow,
107
+ sharex=sharex,
108
+ sharey=sharey,
109
+ widths=widths,
110
+ heights=heights,
111
+ hspace=hspace,
112
+ vspace=vspace,
113
+ fit=fit,
114
+ align=align
115
+ )
116
+
117
+ figures = [_strip_theme_if_global(fig) for fig in plots]
118
+
119
+ figure_spec = SupPlotsSpec(figures=figures, layout=layout)
120
+
121
+ # Apply global theme if defined
122
+ global_theme_options = _get_global_theme()
123
+ if global_theme_options is not None:
124
+ figure_spec += global_theme_options
125
+
126
+ return figure_spec
@@ -0,0 +1,55 @@
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() -> 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
+ Returns
35
+ -------
36
+ `FeatureSpec`
37
+ Toolbar feature specification.
38
+
39
+ Examples
40
+ --------
41
+ .. jupyter-execute::
42
+ :linenos:
43
+ :emphasize-lines: 8
44
+
45
+ import numpy as np
46
+ from lets_plot import *
47
+ LetsPlot.setup_html()
48
+ x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
49
+ y = np.sin(x)
50
+ ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + \\
51
+ geom_point() + \\
52
+ ggtb()
53
+
54
+ """
55
+ return FeatureSpec(kind='ggtoolbar', name=None)