lets-plot 4.5.3a1__cp310-cp310-win_amd64.whl → 4.6.0a2__cp310-cp310-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.

Potentially problematic release.


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

@@ -13,6 +13,7 @@ from .geom_extras import *
13
13
  from .geom_function_ import *
14
14
  from .geom_imshow_ import *
15
15
  from .geom_livemap_ import *
16
+ from .ggbunch_ import *
16
17
  from .gggrid_ import *
17
18
  from .ggtb_ import *
18
19
  from .guide import *
@@ -56,6 +57,7 @@ __all__ = (coord.__all__ +
56
57
  annotation.__all__ +
57
58
  marginal_layer.__all__ +
58
59
  font_features.__all__ +
60
+ ggbunch_.__all__ +
59
61
  gggrid_.__all__ +
60
62
  ggtb_.__all__ +
61
63
  expand_limits_.__all__
lets_plot/plot/core.py CHANGED
@@ -706,7 +706,7 @@ class LayerSpec(FeatureSpec):
706
706
  map_data_meta = {'georeference': {}}
707
707
  else:
708
708
  # Fetch proper GeoDataFrame. Further processing is the same as if map was a GDF.
709
- if name in ['point', 'pie', 'text', 'livemap']:
709
+ if name in ['point', 'pie', 'text', 'label', 'livemap']:
710
710
  map = map.get_centroids()
711
711
  elif name in ['map', 'polygon']:
712
712
  map = map.get_boundaries()
lets_plot/plot/geom.py CHANGED
@@ -12,7 +12,7 @@ from .util import as_annotated_data, is_geo_data_frame, geo_data_frame_to_crs, g
12
12
  #
13
13
  __all__ = ['geom_point', 'geom_path', 'geom_line',
14
14
  'geom_smooth', 'geom_bar',
15
- 'geom_histogram', 'geom_dotplot', 'geom_bin2d',
15
+ 'geom_histogram', 'geom_dotplot', 'geom_bin2d', 'geom_hex',
16
16
  'geom_tile', 'geom_raster',
17
17
  'geom_errorbar', 'geom_crossbar', 'geom_linerange', 'geom_pointrange',
18
18
  'geom_contour',
@@ -128,7 +128,7 @@ def geom_point(mapping=None, *, data=None, stat=None, position=None, show_legend
128
128
 
129
129
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Point` and `MultiPoint`.
130
130
 
131
- The `map` parameter of `Geocoder` type implicitly invokes `centroids()` function.
131
+ The `map` parameter of `Geocoder` type implicitly invokes `get_centroids()` function.
132
132
 
133
133
  ----
134
134
 
@@ -193,7 +193,7 @@ def geom_point(mapping=None, *, data=None, stat=None, position=None, show_legend
193
193
 
194
194
  .. jupyter-execute::
195
195
  :linenos:
196
- :emphasize-lines: 8-11
196
+ :emphasize-lines: 8-12
197
197
 
198
198
  from lets_plot import *
199
199
  from lets_plot.geo_data import *
@@ -205,7 +205,8 @@ def geom_point(mapping=None, *, data=None, stat=None, position=None, show_legend
205
205
  geom_point(aes(size="est_pop_2019"), color="red", show_legend=False, \\
206
206
  data=data, map=centroids, map_join="city", \\
207
207
  tooltips=layer_tooltips().title("@city")
208
- .line("population|@est_pop_2019"))
208
+ .line("population|@est_pop_2019")
209
+ .format("@est_pop_2019", ".2~s"))
209
210
 
210
211
  """
211
212
  return _geom('point',
@@ -1420,6 +1421,173 @@ def geom_bin2d(mapping=None, *, data=None, stat=None, position=None, show_legend
1420
1421
  **other_args)
1421
1422
 
1422
1423
 
1424
+ def geom_hex(mapping=None, *, data=None, stat=None, position=None, show_legend=None, inherit_aes=None,
1425
+ manual_key=None, sampling=None,
1426
+ tooltips=None,
1427
+ bins=None,
1428
+ binwidth=None,
1429
+ drop=None,
1430
+ color_by=None, fill_by=None,
1431
+ **other_args):
1432
+ """
1433
+ Apply a hexagonal grid to the plane, count observations in each cell (hexagonal bin) of the grid,
1434
+ and map the count to the fill color of the cell (hexagonal tile).
1435
+
1436
+ By default, this geom uses `coord_fixed()`.
1437
+ However, this may not be the best choice when the values on the X/Y axis have significantly different magnitudes.
1438
+ In such cases, try using `coord_cartesian()`.
1439
+
1440
+ Parameters
1441
+ ----------
1442
+ mapping : `FeatureSpec`
1443
+ Set of aesthetic mappings created by `aes()` function.
1444
+ Aesthetic mappings describe the way that variables in the data are
1445
+ mapped to plot "aesthetics".
1446
+ data : dict or Pandas or Polars `DataFrame`
1447
+ The data to be displayed in this layer. If None, the default, the data
1448
+ is inherited from the plot data as specified in the call to ggplot.
1449
+ stat : str, default='binhex'
1450
+ The statistical transformation to use on the data for this layer, as a string.
1451
+ position : str or `FeatureSpec`, default='identity'
1452
+ Position adjustment.
1453
+ Either a position adjustment name: 'dodge', 'dodgev', 'jitter', 'nudge', 'jitterdodge', 'fill',
1454
+ 'stack' or 'identity', or the result of calling a position adjustment function (e.g., `position_dodge()` etc.).
1455
+ show_legend : bool, default=True
1456
+ False - do not show legend for this layer.
1457
+ inherit_aes : bool, default=True
1458
+ False - do not combine the layer aesthetic mappings with the plot shared mappings.
1459
+ manual_key : str or `layer_key`
1460
+ The key to show in the manual legend.
1461
+ Specify text for the legend label or advanced settings using the `layer_key()` function.
1462
+ sampling : `FeatureSpec`
1463
+ Result of the call to the `sampling_xxx()` function.
1464
+ To prevent any sampling for this layer pass value "none" (string "none").
1465
+ tooltips : `layer_tooltips`
1466
+ Result of the call to the `layer_tooltips()` function.
1467
+ Specify appearance, style and content.
1468
+ Set tooltips='none' to hide tooltips from the layer.
1469
+ bins : list of int, default=[30, 30]
1470
+ Number of hexagonal bins in both directions, vertical and horizontal. Overridden by `binwidth`.
1471
+ binwidth : list of float
1472
+ The width of the hexagonal bins in both directions, vertical and horizontal.
1473
+ Override `bins`. The default is to use bin widths that cover the entire range of the data.
1474
+ drop : bool, default=True
1475
+ Specify whether to remove all hexagonal bins with 0 counts.
1476
+ color_by : {'fill', 'color', 'paint_a', 'paint_b', 'paint_c'}, default='color'
1477
+ Define the color aesthetic for the geometry.
1478
+ fill_by : {'fill', 'color', 'paint_a', 'paint_b', 'paint_c'}, default='fill'
1479
+ Define the fill aesthetic for the geometry.
1480
+ other_args
1481
+ Other arguments passed on to the layer.
1482
+ These are often aesthetics settings used to set an aesthetic to a fixed value,
1483
+ like color='red', fill='blue', size=3 or shape=21.
1484
+ They may also be parameters to the paired geom/stat.
1485
+
1486
+ Returns
1487
+ -------
1488
+ `LayerSpec`
1489
+ Geom object specification.
1490
+
1491
+ Notes
1492
+ -----
1493
+
1494
+ Computed variables:
1495
+
1496
+ - ..count.. : number of points with coordinates in the same hexagonal bin.
1497
+
1498
+ `geom_hex()` understands the following aesthetics mappings:
1499
+
1500
+ - x : x-axis value.
1501
+ - y : y-axis value.
1502
+ - alpha : transparency level of a layer. Accept values between 0 and 1.
1503
+ - color (colour) : color of the geometry lines. For more info see `Color and Fill <https://lets-plot.org/python/pages/aesthetics.html#color-and-fill>`__.
1504
+ - fill : fill color. For more info see `Color and Fill <https://lets-plot.org/python/pages/aesthetics.html#color-and-fill>`__.
1505
+ - size : line width, default=0 (i.e. tiles outline initially is not visible).
1506
+ - weight : used by 'binhex' stat to compute weighted sum instead of simple count.
1507
+ - width : width of the hexagon.
1508
+ - height : the real height of the hexagon will be 2/sqrt(3) times this value, so with width=height the hexagon will be the regular.
1509
+
1510
+ ----
1511
+
1512
+ To hide axis tooltips, set 'blank' or the result of `element_blank()`
1513
+ to the `axis_tooltip`, `axis_tooltip_x` or `axis_tooltip_y` parameter of the `theme()`.
1514
+
1515
+ Examples
1516
+ --------
1517
+ .. jupyter-execute::
1518
+ :linenos:
1519
+ :emphasize-lines: 8
1520
+
1521
+ import numpy as np
1522
+ from lets_plot import *
1523
+ LetsPlot.setup_html()
1524
+ np.random.seed(42)
1525
+ mean = np.zeros(2)
1526
+ cov = np.eye(2)
1527
+ x, y = np.random.multivariate_normal(mean, cov, 1000).T
1528
+ ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + geom_hex()
1529
+
1530
+ |
1531
+
1532
+ .. jupyter-execute::
1533
+ :linenos:
1534
+ :emphasize-lines: 9-14
1535
+
1536
+ import numpy as np
1537
+ from lets_plot import *
1538
+ LetsPlot.setup_html()
1539
+ np.random.seed(42)
1540
+ n = 5000
1541
+ x = np.random.uniform(-2, 2, size=n)
1542
+ y = np.random.normal(scale=.5, size=n)
1543
+ ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + \\
1544
+ geom_hex(aes(fill='..density..'), binwidth=[.25, .24], \\
1545
+ tooltips=layer_tooltips().format('@x', '.2f')
1546
+ .format('@y', '.2f').line('(@x, @y)')
1547
+ .line('count|@..count..')
1548
+ .format('@..density..', '.3f')
1549
+ .line('density|@..density..')) + \\
1550
+ scale_fill_gradient(low='black', high='red')
1551
+
1552
+ |
1553
+
1554
+ .. jupyter-execute::
1555
+ :linenos:
1556
+ :emphasize-lines: 10-11
1557
+
1558
+ import numpy as np
1559
+ from lets_plot import *
1560
+ LetsPlot.setup_html()
1561
+ np.random.seed(42)
1562
+ mean = np.zeros(2)
1563
+ cov = [[1, .5],
1564
+ [.5, 1]]
1565
+ x, y = np.random.multivariate_normal(mean, cov, 500).T
1566
+ ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + \\
1567
+ geom_hex(aes(alpha='..count..'), bins=[20, 20], \\
1568
+ fill='darkgreen') + \\
1569
+ geom_point(size=1.5, shape=21, color='white', \\
1570
+ fill='darkgreen') + \\
1571
+ ggsize(600, 450)
1572
+
1573
+ """
1574
+ return _geom('hex',
1575
+ mapping=mapping,
1576
+ data=data,
1577
+ stat=stat,
1578
+ position=position,
1579
+ show_legend=show_legend,
1580
+ inherit_aes=inherit_aes,
1581
+ manual_key=manual_key,
1582
+ sampling=sampling,
1583
+ tooltips=tooltips,
1584
+ bins=bins,
1585
+ binwidth=binwidth,
1586
+ drop=drop,
1587
+ color_by=color_by, fill_by=fill_by,
1588
+ **other_args)
1589
+
1590
+
1423
1591
  def geom_tile(mapping=None, *, data=None, stat=None, position=None, show_legend=None, inherit_aes=None,
1424
1592
  manual_key=None, sampling=None,
1425
1593
  tooltips=None,
@@ -2617,7 +2785,7 @@ def geom_polygon(mapping=None, *, data=None, stat=None, position=None, show_lege
2617
2785
 
2618
2786
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Polygon` and `MultiPolygon`.
2619
2787
 
2620
- The `map` parameter of `Geocoder` type implicitly invokes `boundaries()` function.
2788
+ The `map` parameter of `Geocoder` type implicitly invokes `get_boundaries()` function.
2621
2789
 
2622
2790
  ----
2623
2791
 
@@ -2807,7 +2975,7 @@ def geom_map(mapping=None, *, data=None, stat=None, position=None, show_legend=N
2807
2975
 
2808
2976
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Polygon` and `MultiPolygon`.
2809
2977
 
2810
- The `map` parameter of `Geocoder` type implicitly invokes `boundaries()` function.
2978
+ The `map` parameter of `Geocoder` type implicitly invokes `get_boundaries()` function.
2811
2979
 
2812
2980
  ----
2813
2981
 
@@ -4652,7 +4820,7 @@ def geom_density(mapping=None, *, data=None, stat=None, position=None, show_lege
4652
4820
 
4653
4821
  .. jupyter-execute::
4654
4822
  :linenos:
4655
- :emphasize-lines: 10
4823
+ :emphasize-lines: 11
4656
4824
 
4657
4825
  import numpy as np
4658
4826
  from lets_plot import *
@@ -4660,13 +4828,17 @@ def geom_density(mapping=None, *, data=None, stat=None, position=None, show_lege
4660
4828
  np.random.seed(42)
4661
4829
  x = np.random.normal(size=1000)
4662
4830
  p = ggplot({'x': x}, aes(x='x'))
4663
- bunch = GGBunch()
4664
- for i, bw in enumerate([.1, .2, .4]):
4665
- for j, n in enumerate([16, 64, 256]):
4666
- bunch.add_plot(p + geom_density(kernel='epanechikov', bw=bw, n=n) + \\
4667
- ggtitle('bw={0}, n={1}'.format(bw, n)),
4668
- j * 300, i * 200, 300, 200)
4669
- bunch.show()
4831
+ bandwidths = [0.1, 0.2, 0.4]
4832
+ sample_sizes = [16, 64, 256]
4833
+ plots = [
4834
+ p + geom_density(
4835
+ kernel='epanechikov', bw=bw, n=n
4836
+ ) + ggtitle(f'bw={bw}, n={n}')
4837
+ for bw in bandwidths
4838
+ for n in sample_sizes
4839
+ ]
4840
+
4841
+ gggrid(plots, ncol=3) + ggsize(900, 600)
4670
4842
 
4671
4843
  |
4672
4844
 
@@ -4681,13 +4853,15 @@ def geom_density(mapping=None, *, data=None, stat=None, position=None, show_lege
4681
4853
  x = np.random.normal(size=1000)
4682
4854
  y = np.sign(x)
4683
4855
  p = ggplot({'x': x, 'y': y}, aes(x='x'))
4684
- bunch = GGBunch()
4685
- for i, adjust in [(i, .5 * (1 + i)) for i in range(3)]:
4686
- bunch.add_plot(p + geom_density(aes(weight='y'), kernel='cosine', \\
4687
- adjust=adjust) + \\
4688
- ggtitle('adjust={0}'.format(adjust)),
4689
- i * 300, 0, 300, 200)
4690
- bunch.show()
4856
+ adjustments = [0.5 * (1 + i) for i in range(3)]
4857
+ plots = [
4858
+ p + geom_density(aes(weight='y'),
4859
+ kernel='cosine', adjust=adjust
4860
+ ) + ggtitle(f'adjust={adjust}')
4861
+ for adjust in adjustments
4862
+ ]
4863
+
4864
+ gggrid(plots) + ggsize(800, 200)
4691
4865
 
4692
4866
  """
4693
4867
  return _geom('density',
@@ -4847,7 +5021,7 @@ def geom_density2d(mapping=None, *, data=None, stat=None, position=None, show_le
4847
5021
 
4848
5022
  .. jupyter-execute::
4849
5023
  :linenos:
4850
- :emphasize-lines: 12
5024
+ :emphasize-lines: 13
4851
5025
 
4852
5026
  import numpy as np
4853
5027
  from lets_plot import *
@@ -4857,19 +5031,23 @@ def geom_density2d(mapping=None, *, data=None, stat=None, position=None, show_le
4857
5031
  x = np.random.normal(size=n)
4858
5032
  y = np.random.normal(size=n)
4859
5033
  p = ggplot({'x': x, 'y': y}, aes('x', 'y'))
4860
- bunch = GGBunch()
4861
- for i, bw in enumerate([.2, .4]):
4862
- for j, n in enumerate([16, 256]):
4863
- bunch.add_plot(p + geom_density2d(kernel='epanechikov', bw=bw, n=n) + \\
4864
- ggtitle('bw={0}, n={1}'.format(bw, n)),
4865
- j * 400, i * 400, 400, 400)
4866
- bunch.show()
5034
+ bandwidths = [0.2, 0.4]
5035
+ sample_sizes = [16, 256]
5036
+ plots = [
5037
+ p + geom_density2d(kernel='epanechikov',
5038
+ bw=bw, n=n
5039
+ ) + ggtitle(f'bw={bw}, n={n}')
5040
+ for bw in bandwidths
5041
+ for n in sample_sizes
5042
+ ]
5043
+
5044
+ gggrid(plots, ncol=2) + ggsize(600, 650)
4867
5045
 
4868
5046
  |
4869
5047
 
4870
5048
  .. jupyter-execute::
4871
5049
  :linenos:
4872
- :emphasize-lines: 12-13
5050
+ :emphasize-lines: 13-14
4873
5051
 
4874
5052
  import numpy as np
4875
5053
  from lets_plot import *
@@ -4879,14 +5057,18 @@ def geom_density2d(mapping=None, *, data=None, stat=None, position=None, show_le
4879
5057
  x = np.random.normal(size=n)
4880
5058
  y = np.random.normal(size=n)
4881
5059
  p = ggplot({'x': x, 'y': y}, aes('x', 'y'))
4882
- bunch = GGBunch()
4883
- for i, adjust in enumerate([1.5, 2.5]):
4884
- for j, bins in enumerate([5, 15]):
4885
- bunch.add_plot(p + geom_density2d(kernel='cosine', \\
4886
- adjust=adjust, bins=bins) + \\
4887
- ggtitle('adjust={0}, bins={1}'.format(adjust, bins)),
4888
- j * 400, i * 400, 400, 400)
4889
- bunch.show()
5060
+ adjustments = [1.5, 2.5]
5061
+ bin_counts = [5, 15]
5062
+ plots = [
5063
+ p + geom_density2d(kernel='cosine',
5064
+ adjust=adjust,
5065
+ bins=bins
5066
+ ) + ggtitle(f'adjust={adjust}, bins={bins}')
5067
+ for adjust in adjustments
5068
+ for bins in bin_counts
5069
+ ]
5070
+
5071
+ gggrid(plots, ncol=2) + ggsize(600, 650)
4890
5072
 
4891
5073
  |
4892
5074
 
@@ -5073,7 +5255,7 @@ def geom_density2df(mapping=None, *, data=None, stat=None, position=None, show_l
5073
5255
 
5074
5256
  .. jupyter-execute::
5075
5257
  :linenos:
5076
- :emphasize-lines: 12-13
5258
+ :emphasize-lines: 13
5077
5259
 
5078
5260
  import numpy as np
5079
5261
  from lets_plot import *
@@ -5083,14 +5265,16 @@ def geom_density2df(mapping=None, *, data=None, stat=None, position=None, show_l
5083
5265
  x = np.random.normal(size=n)
5084
5266
  y = np.random.normal(size=n)
5085
5267
  p = ggplot({'x': x, 'y': y}, aes(x='x', y='y'))
5086
- bunch = GGBunch()
5087
- for i, bw in enumerate([.2, .4]):
5088
- for j, n in enumerate([16, 256]):
5089
- bunch.add_plot(p + geom_density2df(kernel='epanechikov', bw=bw, n=n, \\
5090
- size=.5, color='white') + \\
5091
- ggtitle('bw={0}, n={1}'.format(bw, n)),
5092
- j * 400, i * 400, 400, 400)
5093
- bunch.show()
5268
+ bandwidths = [0.2, 0.4]
5269
+ sample_sizes = [16, 256]
5270
+ plots = [
5271
+ p + geom_density2df(kernel='epanechikov', size=.5, color='white',
5272
+ bw=bw, n=n
5273
+ ) + ggtitle(f'bw={bw}, n={n}')
5274
+ for bw in bandwidths
5275
+ for n in sample_sizes
5276
+ ]
5277
+ gggrid(plots, ncol=2) + ggsize(600, 650)
5094
5278
 
5095
5279
  |
5096
5280
 
@@ -5106,15 +5290,18 @@ def geom_density2df(mapping=None, *, data=None, stat=None, position=None, show_l
5106
5290
  x = np.random.normal(size=n)
5107
5291
  y = np.random.normal(size=n)
5108
5292
  p = ggplot({'x': x, 'y': y}, aes(x='x', y='y'))
5109
- bunch = GGBunch()
5110
- for i, adjust in enumerate([1.5, 2.5]):
5111
- for j, bins in enumerate([5, 15]):
5112
- bunch.add_plot(p + geom_density2df(kernel='cosine', \\
5113
- size=.5, color='white', \\
5114
- adjust=adjust, bins=bins) + \\
5115
- ggtitle('adjust={0}, bins={1}'.format(adjust, bins)),
5116
- j * 400, i * 400, 400, 400)
5117
- bunch.show()
5293
+ adjustments = [1.5, 2.5]
5294
+ bin_counts = [5, 15]
5295
+ plots = [
5296
+ p + geom_density2df(kernel='cosine', size=.5, color='white',
5297
+ adjust=adjust,
5298
+ bins=bins
5299
+ ) + ggtitle(f'adjust={adjust}, bins={bins}')
5300
+ for adjust in adjustments
5301
+ for bins in bin_counts
5302
+ ]
5303
+
5304
+ gggrid(plots, ncol=2) + ggsize(600, 650)
5118
5305
 
5119
5306
  |
5120
5307
 
@@ -6154,7 +6341,7 @@ def geom_rect(mapping=None, *, data=None, stat=None, position=None, show_legend=
6154
6341
 
6155
6342
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `MultiPoint`, `Line`, `MultiLine`, `Polygon` and `MultiPolygon`.
6156
6343
 
6157
- The `map` parameter of `Geocoder` type implicitly invokes `limits()` function.
6344
+ The `map` parameter of `Geocoder` type implicitly invokes `get_limits()` function.
6158
6345
 
6159
6346
  ----
6160
6347
 
@@ -6820,7 +7007,7 @@ def geom_text(mapping=None, *, data=None, stat=None, position=None, show_legend=
6820
7007
 
6821
7008
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Point` and `MultiPoint`.
6822
7009
 
6823
- The `map` parameter of `Geocoder` type implicitly invokes `centroids()` function.
7010
+ The `map` parameter of `Geocoder` type implicitly invokes `get_centroids()` function.
6824
7011
 
6825
7012
  ----
6826
7013
 
@@ -7067,7 +7254,7 @@ def geom_label(mapping=None, *, data=None, stat=None, position=None, show_legend
7067
7254
 
7068
7255
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Point` and `MultiPoint`.
7069
7256
 
7070
- The `map` parameter of `Geocoder` type implicitly invokes `centroids()` function.
7257
+ The `map` parameter of `Geocoder` type implicitly invokes `get_centroids()` function.
7071
7258
 
7072
7259
  ----
7073
7260
 
@@ -7305,7 +7492,7 @@ def geom_pie(mapping=None, *, data=None, stat=None, position=None, show_legend=N
7305
7492
 
7306
7493
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Point` and `MultiPoint`.
7307
7494
 
7308
- The `map` parameter of `Geocoder` type implicitly invokes `centroids()` function.
7495
+ The `map` parameter of `Geocoder` type implicitly invokes `get_centroids()` function.
7309
7496
 
7310
7497
  ----
7311
7498
 
@@ -7814,7 +8001,7 @@ def geom_blank(mapping=None, *, data=None, stat=None, position=None, show_legend
7814
8001
 
7815
8002
  The `data` and `map` parameters of `GeoDataFrame` type support shapes `Point` and `MultiPoint`.
7816
8003
 
7817
- The `map` parameter of `Geocoder` type implicitly invokes `centroids()` function.
8004
+ The `map` parameter of `Geocoder` type implicitly invokes `get_centroids()` function.
7818
8005
 
7819
8006
  ----
7820
8007
 
@@ -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
lets_plot/plot/gggrid_.py CHANGED
@@ -3,10 +3,10 @@
3
3
  # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4
4
  #
5
5
 
6
- from lets_plot.plot.core import PlotSpec
7
6
  from ._global_theme import _get_global_theme
8
7
  from .subplots import SupPlotsLayoutSpec
9
8
  from .subplots import SupPlotsSpec
9
+ from .subplots_util import _strip_theme_if_global
10
10
 
11
11
  __all__ = ['gggrid']
12
12
 
@@ -114,27 +114,12 @@ def gggrid(plots: list, ncol: int = None, *,
114
114
  align=align
115
115
  )
116
116
 
117
- # Global Theme
118
- global_theme_options = _get_global_theme()
119
-
120
- def _strip_theme_if_global(fig):
121
- # Strip global theme options from plots in grid (see issue: #966).
122
- if global_theme_options is not None and fig is not None and 'theme' in fig.props() and fig.props()[
123
- 'theme'] == global_theme_options.props():
124
- if isinstance(fig, PlotSpec):
125
- fig = PlotSpec.duplicate(fig)
126
- fig.props().pop('theme')
127
- return fig
128
- elif isinstance(fig, SupPlotsSpec):
129
- fig = SupPlotsSpec.duplicate(fig)
130
- fig.props().pop('theme')
131
- return fig
132
- return fig
133
-
134
117
  figures = [_strip_theme_if_global(fig) for fig in plots]
135
118
 
136
119
  figure_spec = SupPlotsSpec(figures=figures, layout=layout)
137
120
 
121
+ # Apply global theme if defined
122
+ global_theme_options = _get_global_theme()
138
123
  if global_theme_options is not None:
139
124
  figure_spec += global_theme_options
140
125
 
lets_plot/plot/plot.py CHANGED
@@ -142,6 +142,9 @@ def ggsize(width, height):
142
142
 
143
143
  class GGBunch(FeatureSpec):
144
144
  """
145
+ Class `GGBunch` is deprecated and will be removed in future releases.
146
+ Please, use function `ggbunch()` to combine several plots into a single figure with custom layout.
147
+
145
148
  Collection of plots created by ggplot function.
146
149
  Use method `add_plot()` to add plot to 'bunch'.
147
150
  Each plot can have arbitrary location and size.
@@ -176,6 +179,10 @@ class GGBunch(FeatureSpec):
176
179
  """
177
180
  super().__init__('ggbunch', None)
178
181
  self.items = []
182
+ print("\n(!) WARN: class GGBunch is deprecated and will be removed in future releases.\n\n"
183
+ " Please, use function ggbunch() to combine several plots into\n"
184
+ " a single figure with custom layout.\n")
185
+
179
186
 
180
187
  def add_plot(self, plot_spec: PlotSpec, x, y, width=None, height=None):
181
188
  """