lets-plot 4.5.2.dev1__cp38-cp38-macosx_11_0_arm64.whl → 4.6.0__cp38-cp38-macosx_11_0_arm64.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.

@@ -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
  """
lets_plot/plot/pos.py CHANGED
@@ -146,7 +146,7 @@ def position_jitter(width=None, height=None, seed=None):
146
146
  return _pos('jitter', width=width, height=height, seed=seed)
147
147
 
148
148
 
149
- def position_nudge(x=None, y=None):
149
+ def position_nudge(x=None, y=None, unit=None):
150
150
  """
151
151
  Adjust position by nudging a given offset.
152
152
 
@@ -156,6 +156,13 @@ def position_nudge(x=None, y=None):
156
156
  Nudging width.
157
157
  y : float
158
158
  Nudging height.
159
+ unit : {'identity', 'size', 'px'}, default='identity'
160
+ Units for x and y nudging.
161
+ Possible values:
162
+
163
+ - 'identity': a unit of 1 corresponds to a difference of 1 in data space;
164
+ - 'size': a unit of 1 corresponds to the diameter of a point with `size=1`;
165
+ - 'px': the unit is measured in screen pixels.
159
166
 
160
167
  Returns
161
168
  -------
@@ -182,10 +189,10 @@ def position_nudge(x=None, y=None):
182
189
  t = np.random.choice(list('abcdefghijk'), size=n)
183
190
  ggplot({'x': x, 'y': y, 't': t}, aes('x', 'y')) + \\
184
191
  geom_point(size=5, shape=21, color='black', fill='red') + \\
185
- geom_text(aes(label='t'), position=position_nudge(y=.05))
192
+ geom_text(aes(label='t'), position=position_nudge(y=.05, unit='identity'))
186
193
 
187
194
  """
188
- return _pos('nudge', x=x, y=y)
195
+ return _pos('nudge', x=x, y=y, unit=unit)
189
196
 
190
197
 
191
198
  def position_jitterdodge(dodge_width=None, jitter_width=None, jitter_height=None, seed=None):
lets_plot/plot/scale.py CHANGED
@@ -90,10 +90,8 @@ def scale_shape(solid=True, name=None, breaks=None, labels=None, lablim=None, li
90
90
  labels=labels,
91
91
  lablim=lablim,
92
92
  limits=limits,
93
- expand=None,
94
93
  na_value=na_value,
95
94
  guide=guide,
96
- trans=None,
97
95
  format=format,
98
96
  #
99
97
  solid=solid)
@@ -192,10 +190,8 @@ def scale_manual(aesthetic, values, *,
192
190
  labels=labels,
193
191
  lablim=lablim,
194
192
  limits=limits,
195
- expand=None,
196
193
  na_value=na_value,
197
194
  guide=guide,
198
- trans=None,
199
195
  format=format,
200
196
  #
201
197
  values=values)
@@ -668,7 +664,7 @@ def _is_color_scale(aesthetic):
668
664
 
669
665
  def scale_continuous(aesthetic, *,
670
666
  name=None, breaks=None, labels=None, lablim=None,
671
- limits=None, na_value=None, guide=None, trans=None, format=None,
667
+ limits=None, expand=None, na_value=None, guide=None, trans=None, format=None,
672
668
  scale_mapper_kind=None,
673
669
  **kwargs):
674
670
  """
@@ -691,6 +687,10 @@ def scale_continuous(aesthetic, *,
691
687
  The maximum label length (in characters) before trimming is applied.
692
688
  limits : list
693
689
  A numeric vector of length two providing limits of the scale.
690
+ expand : list
691
+ A numeric vector of length two giving multiplicative and additive expansion constants.
692
+ The vector size == 1 => only multiplicative expand (and additive expand by default).
693
+ Defaults: multiplicative = 0.05, additive = 0.
694
694
  na_value
695
695
  Missing values will be replaced with this value.
696
696
  guide
@@ -737,6 +737,22 @@ def scale_continuous(aesthetic, *,
737
737
  coord_cartesian() + \\
738
738
  ggsize(600, 200)
739
739
 
740
+ |
741
+
742
+ .. jupyter-execute::
743
+ :linenos:
744
+ :emphasize-lines: 9
745
+
746
+ from lets_plot import *
747
+ LetsPlot.setup_html()
748
+ data = {
749
+ 'x': [0, 1, 2],
750
+ 'y': [0, 1, 2]
751
+ }
752
+ ggplot(data) + \\
753
+ geom_point(aes(x='x', y='y'), size=15) + \\
754
+ scale_continuous(['x','y'], expand=[0, 0]) # no expansion - points right on the edges
755
+
740
756
  """
741
757
  if _is_color_scale(aesthetic):
742
758
  scale_mapper_kind = 'color_gradient' if scale_mapper_kind is None else scale_mapper_kind
@@ -747,7 +763,7 @@ def scale_continuous(aesthetic, *,
747
763
  labels=labels,
748
764
  lablim=lablim,
749
765
  limits=limits,
750
- expand=None,
766
+ expand=expand,
751
767
  na_value=na_value,
752
768
  guide=guide,
753
769
  trans=trans,
@@ -992,7 +1008,6 @@ def scale_gradient(aesthetic, *,
992
1008
  labels=labels,
993
1009
  lablim=lablim,
994
1010
  limits=limits,
995
- expand=None,
996
1011
  na_value=na_value,
997
1012
  guide=guide,
998
1013
  trans=trans,
@@ -1242,7 +1257,6 @@ def scale_gradient2(aesthetic, *,
1242
1257
  labels=labels,
1243
1258
  lablim=lablim,
1244
1259
  limits=limits,
1245
- expand=None,
1246
1260
  na_value=na_value,
1247
1261
  guide=guide,
1248
1262
  trans=trans,
@@ -1498,7 +1512,6 @@ def scale_gradientn(aesthetic, *,
1498
1512
  labels=labels,
1499
1513
  lablim=lablim,
1500
1514
  limits=limits,
1501
- expand=None,
1502
1515
  na_value=na_value,
1503
1516
  guide=guide,
1504
1517
  trans=trans,
@@ -1746,7 +1759,6 @@ def scale_hue(aesthetic, *,
1746
1759
  labels=labels,
1747
1760
  lablim=lablim,
1748
1761
  limits=limits,
1749
- expand=None,
1750
1762
  na_value=na_value,
1751
1763
  guide=guide,
1752
1764
  trans=trans,
@@ -1933,7 +1945,8 @@ def scale_color_hue(h=None, c=None, l=None, h_start=None, direction=None, name=N
1933
1945
 
1934
1946
  def scale_discrete(aesthetic, *,
1935
1947
  direction=None,
1936
- name=None, breaks=None, labels=None, lablim=None, limits=None, na_value=None, guide=None, format=None,
1948
+ name=None, breaks=None, labels=None, lablim=None,
1949
+ limits=None, expand=None, na_value=None, guide=None, format=None,
1937
1950
  scale_mapper_kind=None,
1938
1951
  **kwargs):
1939
1952
  """
@@ -1960,6 +1973,10 @@ def scale_discrete(aesthetic, *,
1960
1973
  limits : list
1961
1974
  A vector specifying the data range for the scale
1962
1975
  and the default order of their display in guides.
1976
+ expand : list
1977
+ A numeric vector of length two giving multiplicative and additive expansion constants.
1978
+ The vector size == 1 => only multiplicative expand (and additive expand by default).
1979
+ Defaults: multiplicative = 0.05, additive = 0.
1963
1980
  na_value
1964
1981
  Missing values will be replaced with this value.
1965
1982
  guide
@@ -2006,6 +2023,22 @@ def scale_discrete(aesthetic, *,
2006
2023
  ggplot() + geom_point(aes(x, y, color=z, fill=z), shape=21, size=4) + \\
2007
2024
  scale_discrete(aesthetic=['color', 'fill'], guide='none')
2008
2025
 
2026
+ |
2027
+
2028
+ .. jupyter-execute::
2029
+ :linenos:
2030
+ :emphasize-lines: 9
2031
+
2032
+ from lets_plot import *
2033
+ LetsPlot.setup_html()
2034
+ data = {
2035
+ 'x': [0, 1, 2],
2036
+ 'y': [0, 1, 2]
2037
+ }
2038
+ ggplot(data) + \\
2039
+ geom_point(aes(x='x', y='y'), size=15) + \\
2040
+ scale_discrete(['x','y'], expand=[0, 0]) # no expansion - points right on the edges
2041
+
2009
2042
  """
2010
2043
  return _scale(aesthetic=aesthetic,
2011
2044
  name=name,
@@ -2013,7 +2046,7 @@ def scale_discrete(aesthetic, *,
2013
2046
  labels=labels,
2014
2047
  lablim=lablim,
2015
2048
  limits=limits,
2016
- expand=None,
2049
+ expand=expand,
2017
2050
  na_value=na_value,
2018
2051
  guide=guide,
2019
2052
  format=format,
@@ -2266,7 +2299,6 @@ def scale_grey(aesthetic, *,
2266
2299
  labels=labels,
2267
2300
  lablim=lablim,
2268
2301
  limits=limits,
2269
- expand=None,
2270
2302
  na_value=na_value,
2271
2303
  guide=guide,
2272
2304
  trans=trans,
@@ -2550,7 +2582,6 @@ def scale_brewer(aesthetic, *,
2550
2582
  labels=labels,
2551
2583
  lablim=lablim,
2552
2584
  limits=limits,
2553
- expand=None,
2554
2585
  na_value=na_value,
2555
2586
  guide=guide,
2556
2587
  trans=trans,
@@ -2858,7 +2889,6 @@ def scale_viridis(aesthetic, *,
2858
2889
  labels=labels,
2859
2890
  lablim=lablim,
2860
2891
  limits=limits,
2861
- expand=None,
2862
2892
  na_value=na_value,
2863
2893
  guide=guide,
2864
2894
  trans=trans,
@@ -3146,7 +3176,6 @@ def scale_alpha(range=None, name=None, breaks=None, labels=None, lablim=None, li
3146
3176
  labels=labels,
3147
3177
  lablim=lablim,
3148
3178
  limits=limits,
3149
- expand=None,
3150
3179
  na_value=na_value,
3151
3180
  guide=guide,
3152
3181
  trans=trans,
@@ -3221,7 +3250,6 @@ def scale_size(range=None, name=None, breaks=None, labels=None, lablim=None, lim
3221
3250
  labels=labels,
3222
3251
  lablim=lablim,
3223
3252
  limits=limits,
3224
- expand=None,
3225
3253
  na_value=na_value,
3226
3254
  guide=guide,
3227
3255
  trans=trans,
@@ -3300,7 +3328,6 @@ def scale_size_area(max_size=None, name=None, breaks=None, labels=None, lablim=N
3300
3328
  labels=labels,
3301
3329
  lablim=lablim,
3302
3330
  limits=limits,
3303
- expand=None,
3304
3331
  na_value=na_value,
3305
3332
  guide=guide,
3306
3333
  trans=trans,
@@ -3375,7 +3402,6 @@ def scale_linewidth(range=None, name=None, breaks=None, labels=None, lablim=None
3375
3402
  labels=labels,
3376
3403
  lablim=lablim,
3377
3404
  limits=limits,
3378
- expand=None,
3379
3405
  na_value=na_value,
3380
3406
  guide=guide,
3381
3407
  trans=trans,
@@ -3449,7 +3475,6 @@ def scale_stroke(range=None, name=None, breaks=None, labels=None, lablim=None, l
3449
3475
  labels=labels,
3450
3476
  lablim=lablim,
3451
3477
  limits=limits,
3452
- expand=None,
3453
3478
  na_value=na_value,
3454
3479
  guide=guide,
3455
3480
  trans=trans,
@@ -86,11 +86,17 @@ def infer_type(data: Union[Dict, 'pandas.DataFrame', 'polars.DataFrame']) -> Dic
86
86
  continue
87
87
 
88
88
  type_set = set(type(val) for val in var_content)
89
- if None in type_set:
90
- type_set.remove(None)
89
+ if type(None) in type_set:
90
+ type_set.remove(type(None))
91
+
92
+ if len(type_set) == 0:
93
+ continue
91
94
 
92
95
  if len(type_set) > 1:
93
- type_info[var_name] = 'unknown(mixed types)'
96
+ if all(issubclass(type_obj, int) or issubclass(type_obj, float) for type_obj in type_set):
97
+ type_info[var_name] = TYPE_FLOATING
98
+ else:
99
+ type_info[var_name] = 'unknown(mixed types)'
94
100
  continue
95
101
 
96
102
  try:
@@ -67,7 +67,7 @@ class SupPlotsSpec(FeatureSpec):
67
67
  supplots = supplots.__add__(spec)
68
68
  return supplots
69
69
 
70
- elif isinstance(other, FeatureSpec) and other.kind in ["ggsize", "theme", "ggtoolbar"]:
70
+ elif isinstance(other, FeatureSpec) and other.kind in ["ggsize", "theme", "ggtitle", "caption", "ggtoolbar"]:
71
71
 
72
72
  supplots = SupPlotsSpec.duplicate(self)
73
73
  # ToDo: duplication!
@@ -0,0 +1,24 @@
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 lets_plot.plot.core import PlotSpec
5
+ from ._global_theme import _get_global_theme
6
+ from .subplots import SupPlotsSpec
7
+
8
+
9
+ def _strip_theme_if_global(fig):
10
+ # Strip this global theme if defined
11
+ global_theme_options = _get_global_theme()
12
+
13
+ # Strip global theme options from plots in grid (see issue: #966).
14
+ if global_theme_options is not None and fig is not None and 'theme' in fig.props() and fig.props()[
15
+ 'theme'] == global_theme_options.props():
16
+ if isinstance(fig, PlotSpec):
17
+ fig = PlotSpec.duplicate(fig)
18
+ fig.props().pop('theme')
19
+ return fig
20
+ elif isinstance(fig, SupPlotsSpec):
21
+ fig = SupPlotsSpec.duplicate(fig)
22
+ fig.props().pop('theme')
23
+ return fig
24
+ return fig
lets_plot/plot/theme_.py CHANGED
@@ -10,6 +10,7 @@ __all__ = [
10
10
  "element_line",
11
11
  'element_rect',
12
12
  'element_text',
13
+ 'element_markdown',
13
14
  'margin',
14
15
  'element_geom',
15
16
  ]
@@ -48,12 +49,11 @@ def theme(*,
48
49
  legend_key_size=None, legend_key_width=None, legend_key_height=None,
49
50
  legend_key_spacing=None, legend_key_spacing_x=None, legend_key_spacing_y=None,
50
51
  legend_box=None, legend_box_just=None, legend_box_spacing=None,
51
- # ToDo: other legend options...
52
+ legend_ticks=None, legend_ticks_length=None,
52
53
 
53
54
  panel_background=None,
54
55
  panel_border=None,
55
56
  panel_border_ontop=None,
56
- # ToDo: other panel options...
57
57
 
58
58
  panel_grid=None,
59
59
  panel_grid_ontop=None,
@@ -99,20 +99,20 @@ def theme(*,
99
99
  Parameters
100
100
  ----------
101
101
  exponent_format : {'e', 'pow', 'pow_full'} or tuple, default='e'
102
- Format for numeric labels in scientific notation.
102
+ Controls the appearance of numbers formatted with 'e' or 'g' types.
103
103
 
104
- - e for "e" notation (e.g. 1e+6);
105
- - pow_full for "power" notation (e.g. 1x10^6). This will enable superscript formatting for the exponent;
106
- - pow works as pow_full but will shorten powers of 10 (e.g. 10^6 instead of 1x10^6).
104
+ Value is either a string - style, or a tuple: (style, lower_exp_bound, upper_exp_bound)
105
+ where style can be:
106
+
107
+ - 'e' : e-notation (e.g., 1e+6)
108
+ - 'pow' : superscript powers of 10 in shortened form (e.g., 10^6)
109
+ - 'pow_full' : superscript powers of 10 with coefficient (e.g., 1×10^6)
107
110
 
108
- If parameter is a tuple, then it should be a three-element tuple:
111
+ For 'g' type formatting, scientific notation is applied when the number's exponent
112
+ is less than or equal to the lower_exp_bound (-7 by default) or greater than or equal
113
+ to the upper_exp_bound (6 by default, but can be affected by `precision` in format specifier).
109
114
 
110
- - the first element is the format - 'e', 'pow', 'pow_full';
111
- - the second element is minimum exponent value from which to use scientific notation (default is -7);
112
- - the third element is maximum exponent value from which to use scientific notation (default is taken from `precision` of the current formatting, see `Formatting <https://lets-plot.org/python/pages/formats.html>`__).
113
-
114
- Minimum and maximum exponent values are only taken into account when "g" format is used,
115
- see `Formatting <https://lets-plot.org/python/pages/formats.html>`__.
115
+ See `Formatting <https://lets-plot.org/python/pages/formats.html>`__.
116
116
 
117
117
  Superscript is not supported when exporting to PNG/PDF.
118
118
  line : str or dict
@@ -221,6 +221,14 @@ def theme(*,
221
221
  Justification of each legend within the overall bounding box, when there are multiple legends.
222
222
  legend_box_spacing : float
223
223
  Spacing between plotting area and legend box.
224
+ legend_ticks : str or dict
225
+ Tick marks in colorbars.
226
+ Set 'blank' or result of `element_blank()` to draw nothing and assign no space.
227
+ Set `element_line()` to specify all tick mark parameters.
228
+ `legend_ticks_*` DOES NOT inherit from `line`.
229
+ By default, the colorbar tick marks are drawn with the same color as the colorbar background.
230
+ legend_ticks_length : float
231
+ Length of colorbar tick marks in px.
224
232
  panel_background : str or dict
225
233
  Background of plotting area.
226
234
  Set 'blank' or result of `element_blank()` to draw nothing.
@@ -576,7 +584,7 @@ def element_text(
576
584
  face : str
577
585
  Font face ("plain", "italic", "bold", "bold_italic").
578
586
  size : int
579
- Text size in pt.
587
+ Text size in px.
580
588
  angle : float
581
589
  Angle to rotate the text (in degrees).
582
590
  hjust : float
@@ -593,12 +601,11 @@ def element_text(
593
601
  Can be used with values out of range, but behaviour is not specified.
594
602
  margin : number or list of numbers
595
603
  Margins around the text.
596
-
597
604
  The margin may be specified using a number or a list of numbers:
605
+
598
606
  - a number or list of one number - the same margin it applied to all four sides;
599
607
  - a list of two numbers - the first margin applies to the top and bottom, the second - to the left and right;
600
- - a list of three numbers - the first margin applies to the top, the second - to the right and left,
601
- the third - to the bottom;
608
+ - a list of three numbers - the first margin applies to the top, the second - to the right and left, the third - to the bottom;
602
609
  - a list of four numbers - the margins are applied to the top, right, bottom and left in that order.
603
610
 
604
611
  It is acceptable to use None for any side; in this case, the default side value for this element will be used.
@@ -628,6 +635,87 @@ def element_text(
628
635
  return locals()
629
636
 
630
637
 
638
+ def element_markdown(
639
+ color=None,
640
+ family=None,
641
+ face=None,
642
+ size=None,
643
+ angle=None,
644
+ # ToDo: lineheight
645
+ hjust=None,
646
+ vjust=None,
647
+ margin=None,
648
+ blank=False,
649
+ ) -> dict:
650
+ """
651
+ Specify how non-data components of the plot are drawn.
652
+ This theme element draws texts with Markdown support.
653
+
654
+ Parameters
655
+ ----------
656
+ color : str
657
+ Text color.
658
+ family : str
659
+ Font family.
660
+ face : {'plain', 'italic', 'bold', 'bold_italic'}, default='plain'
661
+ Font face.
662
+ size : int
663
+ Text size in px.
664
+ angle : float
665
+ Angle to rotate the text (in degrees).
666
+ hjust : float
667
+ Horizontal justification (in [0, 1]).
668
+ 0 - left-justified;
669
+ 1 - right-justified;
670
+ 0.5 - center-justified.
671
+ Can be used with values out of range, but behaviour is not specified.
672
+ vjust : float
673
+ Vertical justification (in [0, 1]).
674
+ 0 - bottom-justified;
675
+ 1 - top-justified;
676
+ 0.5 - middle-justified.
677
+ Can be used with values out of range, but behaviour is not specified.
678
+ margin : number or list of numbers
679
+ Margins around the text.
680
+ The margin may be specified using a number or a list of numbers:
681
+
682
+ - a number or list of one number - the same margin it applied to all four sides;
683
+ - a list of two numbers - the first margin applies to the top and bottom, the second - to the left and right;
684
+ - a list of three numbers - the first margin applies to the top, the second - to the right and left, the third - to the bottom;
685
+ - a list of four numbers - the margins are applied to the top, right, bottom and left in that order.
686
+
687
+ It is acceptable to use None for any side; in this case, the default side value for this element will be used.
688
+ blank : bool, default=False
689
+ If True - draws nothing, and assigns no space.
690
+
691
+ Returns
692
+ -------
693
+ `dict`
694
+ Theme element specification.
695
+
696
+ Examples
697
+ --------
698
+ .. jupyter-execute::
699
+ :linenos:
700
+ :emphasize-lines: 12
701
+
702
+ from lets_plot import *
703
+ LetsPlot.setup_html()
704
+ ggplot() + \\
705
+ geom_blank() + \\
706
+ labs(
707
+ title='*Hello*, **world**',
708
+ subtitle='_Simple plot_',
709
+ caption='*Powered by **lets-plot***',
710
+ x='Title **X**',
711
+ y='Title **Y**'
712
+ ) + \\
713
+ theme(title=element_markdown())
714
+
715
+ """
716
+ return {'markdown': True, **locals()}
717
+
718
+
631
719
  def margin(t=None, r=None, b=None, l=None):
632
720
  """
633
721
  Function `margin()` is deprecated.