lets-plot 4.6.2__cp313-cp313-macosx_11_0_arm64.whl → 4.7.0__cp313-cp313-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.

@@ -14,7 +14,24 @@ __all__ = ['layer_labels']
14
14
 
15
15
  class layer_labels(FeatureSpec):
16
16
  """
17
- Configure annotations (for pie and bar charts).
17
+ Configure annotations for geometry layers.
18
+
19
+ Annotations are currently supported for bar, pie, and crossbar geometry
20
+ layers. This class provides methods to customize the appearance and
21
+ content of text labels displayed on these geometries.
22
+
23
+ Notes
24
+ -----
25
+ By default, annotation text color is automatically selected for optimal
26
+ contrast: white text appears on darker filled geometries, and black text
27
+ appears on lighter filled geometries.
28
+
29
+ The text color can be manually specified using:
30
+ ``theme(label_text=element_text(color=...))``
31
+
32
+ Alternatively, the ``inherit_color()`` method can be used to override both
33
+ automatic and manual color settings, making the annotation text use the
34
+ geometry's ``color`` aesthetic instead.
18
35
 
19
36
  Examples
20
37
  --------
@@ -24,9 +41,9 @@ class layer_labels(FeatureSpec):
24
41
 
25
42
  from lets_plot import *
26
43
  LetsPlot.setup_html()
27
- data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ] }
44
+ data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
28
45
  ggplot(data) + geom_pie(aes(slice='value', fill='name'), size=15, hole=0.4, \\
29
- stat='identity', tooltips = 'none', \\
46
+ stat='identity', tooltips='none', \\
30
47
  labels=layer_labels().line('@value'))
31
48
 
32
49
  """
@@ -46,11 +63,12 @@ class layer_labels(FeatureSpec):
46
63
  self._lines: List = None
47
64
  self._variables = variables
48
65
  self._size = None
66
+ self._useLayerColor = None
49
67
  super().__init__('labels', name=None)
50
68
 
51
69
  def as_dict(self):
52
70
  """
53
- Return the dictionary of all properties of the object.
71
+ Return a dictionary of all properties of the object.
54
72
 
55
73
  Returns
56
74
  -------
@@ -76,6 +94,7 @@ class layer_labels(FeatureSpec):
76
94
  d['lines'] = self._lines
77
95
  d['variables'] = self._variables
78
96
  d['annotation_size'] = self._size
97
+ d['use_layer_color'] = self._useLayerColor
79
98
  return _filter_none(d)
80
99
 
81
100
  def format(self, field=None, format=None):
@@ -113,7 +132,7 @@ class layer_labels(FeatureSpec):
113
132
 
114
133
  from lets_plot import *
115
134
  LetsPlot.setup_html()
116
- data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ] }
135
+ data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
117
136
  ggplot(data) + geom_pie(aes(fill=as_discrete('name', order_by='..count..'), weight='value'), \\
118
137
  size=15, tooltips='none', \\
119
138
  labels=layer_labels(['..proppct..']) \\
@@ -127,7 +146,7 @@ class layer_labels(FeatureSpec):
127
146
 
128
147
  from lets_plot import *
129
148
  LetsPlot.setup_html()
130
- data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ] }
149
+ data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
131
150
  ggplot(data) + geom_pie(aes(fill=as_discrete('name', order_by='..count..', order=1), weight='value'), \\
132
151
  size=15, tooltips='none', \\
133
152
  labels=layer_labels() \\
@@ -146,12 +165,17 @@ class layer_labels(FeatureSpec):
146
165
 
147
166
  def line(self, value):
148
167
  """
149
- Line to show in the annotation.
168
+ Add a line of text to the multiline label annotation.
169
+
170
+ This method configures one line of text that will be displayed in a
171
+ multiline label. Multiple calls to this method can be chained to build
172
+ up a complete multiline annotation.
150
173
 
151
174
  Parameters
152
175
  ----------
153
176
  value : str
154
- Enriched string which becomes one line of the annotation.
177
+ The text content for this line of the annotation. Can include
178
+ variable and aesthetic references.
155
179
 
156
180
  Returns
157
181
  -------
@@ -162,17 +186,16 @@ class layer_labels(FeatureSpec):
162
186
  -----
163
187
  Variables and aesthetics can be accessed via special syntax:
164
188
 
165
- - ^color for aes,
189
+ - ^color for aesthetics,
166
190
  - @x for variable,
167
191
  - @{x + 1} for variable with spaces in the name,
168
192
  - @{x^2 + 1} for variable with spaces and '^' symbol in the name,
169
193
  - @x^2 for variable with '^' symbol in its name.
170
194
 
171
- A '^' symbol can be escaped with a backslash, a brace character
172
- in the literal text - by doubling:
195
+ Special characters can be escaped:
173
196
 
174
- - 'x\\\\^2' -> "x^2"
175
- - '{{x}}' -> "{x}"
197
+ - 'x\\\\^2' -> "x^2" (escape ^ with backslash)
198
+ - '{{x}}' -> "{x}" (escape braces by doubling)
176
199
 
177
200
  Examples
178
201
  --------
@@ -182,7 +205,7 @@ class layer_labels(FeatureSpec):
182
205
 
183
206
  from lets_plot import *
184
207
  LetsPlot.setup_html()
185
- data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ] }
208
+ data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
186
209
  ggplot(data) + geom_pie(aes(fill='name', weight='value'), size=15, \\
187
210
  tooltips='none', \\
188
211
  labels=layer_labels()\\
@@ -200,12 +223,12 @@ class layer_labels(FeatureSpec):
200
223
 
201
224
  def size(self, value):
202
225
  """
203
- Text size in the annotation.
226
+ Set the text size for the annotation.
204
227
 
205
228
  Parameters
206
229
  ----------
207
230
  value : float
208
- Text size in the annotation.
231
+ The text size value for the annotation.
209
232
 
210
233
  Returns
211
234
  -------
@@ -221,9 +244,9 @@ class layer_labels(FeatureSpec):
221
244
 
222
245
  from lets_plot import *
223
246
  LetsPlot.setup_html()
224
- data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ] }
247
+ data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
225
248
  ggplot(data) + geom_pie(aes(slice='value', fill='name'), size=15, hole=0.4, \\
226
- stat='identity', tooltips = 'none', \\
249
+ stat='identity', tooltips='none', \\
227
250
  labels=layer_labels().line('@value')
228
251
  .size(25))
229
252
 
@@ -231,3 +254,37 @@ class layer_labels(FeatureSpec):
231
254
 
232
255
  self._size = value
233
256
  return self
257
+
258
+ def inherit_color(self):
259
+ """
260
+ Use the layer's color for the annotation text.
261
+
262
+ When enabled, the annotation text will inherit the color from the
263
+ layer it's associated with, rather than using a default or
264
+ explicitly set color.
265
+
266
+ Returns
267
+ -------
268
+ `layer_labels`
269
+ Annotations specification.
270
+
271
+ Examples
272
+ --------
273
+
274
+ .. jupyter-execute::
275
+ :linenos:
276
+ :emphasize-lines: 8
277
+
278
+ from lets_plot import *
279
+ LetsPlot.setup_html()
280
+ data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
281
+ ggplot(data) + geom_pie(aes(slice='value', color='name'), alpha=0, size=15, hole=0.4, \\
282
+ stroke=5, spacer_color='pen', \\
283
+ stat='identity', tooltips='none', \\
284
+ labels=layer_labels().line('@value')
285
+ .inherit_color())
286
+
287
+ """
288
+
289
+ self._useLayerColor = True
290
+ return self
lets_plot/plot/core.py CHANGED
@@ -9,7 +9,7 @@ from typing import Union
9
9
 
10
10
  __all__ = ['aes', 'layer']
11
11
 
12
- from lets_plot._global_settings import get_global_bool, has_global_value, FRAGMENTS_ENABLED
12
+ from lets_plot._global_settings import get_global_bool, has_global_value, FRAGMENTS_ENABLED, MAGICK_EXPORT
13
13
 
14
14
 
15
15
  def aes(x=None, y=None, **kwargs):
@@ -96,7 +96,7 @@ def layer(geom=None, stat=None, data=None, mapping=None, position=None, **kwargs
96
96
  mapped to plot "aesthetics".
97
97
  position : str or `FeatureSpec`
98
98
  Position adjustment.
99
- Either a position adjustment name: 'dodge', 'dodgev', 'jitter', 'nudge', 'jitterdodge', 'fill',
99
+ Either a position adjustment name: 'dodge', 'jitter', 'nudge', 'jitterdodge', 'fill',
100
100
  'stack' or 'identity', or the result of calling a position adjustment function (e.g., `position_dodge()` etc.).
101
101
  kwargs:
102
102
  Other arguments passed on to layer. These are often aesthetics settings, used to set an aesthetic to a fixed
@@ -402,9 +402,11 @@ class PlotSpec(FeatureSpec):
402
402
  return plot
403
403
 
404
404
  if other.kind == 'mapping': # +aes(..)
405
- existing_spec = plot.props().get('mapping', aes())
406
- merged_mapping = {**existing_spec.as_dict(), **other.as_dict()}
407
- plot.props()['mapping'] = aes(**merged_mapping)
405
+ # existing_spec = plot.props().get('mapping', aes())
406
+ # merged_mapping = {**existing_spec.as_dict(), **other.as_dict()}
407
+ # plot.props()['mapping'] = aes(**merged_mapping)
408
+ from lets_plot.plot.util import update_plot_aes_mapping # local import to break circular reference
409
+ update_plot_aes_mapping(plot, other)
408
410
  return plot
409
411
 
410
412
  # add feature to properties
@@ -566,10 +568,10 @@ class PlotSpec(FeatureSpec):
566
568
  h : float, default=None
567
569
  Height of the output image in units.
568
570
  Only applicable when exporting to PNG or PDF.
569
- unit : {'in', 'cm', 'mm'}, default=None
571
+ unit : {'in', 'cm', 'mm'}, default='in'
570
572
  Unit of the output image. One of: 'in', 'cm', 'mm'.
571
573
  Only applicable when exporting to PNG or PDF.
572
- dpi : int, default=None
574
+ dpi : int, default=300
573
575
  Resolution in dots per inch.
574
576
  Only applicable when exporting to PNG or PDF.
575
577
 
@@ -580,9 +582,27 @@ class PlotSpec(FeatureSpec):
580
582
 
581
583
  Notes
582
584
  -----
583
- Export to PNG file uses the CairoSVG library.
584
- CairoSVG is free and distributed under the LGPL-3.0 license.
585
- For more details visit: https://cairosvg.org/documentation/
585
+ - If `w`, `h`, `unit`, and `dpi` are all specified:
586
+
587
+ - The plot's pixel size (default or set by `ggsize()`) is ignored.
588
+ - The output size is calculated using the specified `w`, `h`, `unit`, and `dpi`.
589
+
590
+ - The plot is resized to fit the specified `w` x `h` area, which may affect the layout, tick labels, and other elements.
591
+
592
+ - If only `dpi` is specified:
593
+
594
+ - The plot's pixel size (default or set by `ggsize()`) is converted to inches using the standard display PPI of 96.
595
+ - The output size is then calculated based on the specified DPI.
596
+
597
+ - The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
598
+ - Useful for printing - the plot will appear nearly the same size as on screen.
599
+
600
+ - If `w`, `h` are not specified:
601
+
602
+ - The `scale` parameter is used to determine the output size.
603
+
604
+ - The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
605
+ - Useful for generating high-resolution images suitable for publication.
586
606
 
587
607
  Examples
588
608
  --------
@@ -600,6 +620,7 @@ class PlotSpec(FeatureSpec):
600
620
  file_like = io.BytesIO()
601
621
  p.to_png(file_like)
602
622
  display.Image(file_like.getvalue())
623
+
603
624
  """
604
625
  return _export_as_raster(self, path, scale, 'png', w=w, h=h, unit=unit, dpi=dpi)
605
626
 
@@ -623,10 +644,10 @@ class PlotSpec(FeatureSpec):
623
644
  h : float, default=None
624
645
  Height of the output image in units.
625
646
  Only applicable when exporting to PNG or PDF.
626
- unit : {'in', 'cm', 'mm'}, default=None
647
+ unit : {'in', 'cm', 'mm'}, default='in'
627
648
  Unit of the output image. One of: 'in', 'cm', 'mm'.
628
649
  Only applicable when exporting to PNG or PDF.
629
- dpi : int, default=None
650
+ dpi : int, default=300
630
651
  Resolution in dots per inch.
631
652
  Only applicable when exporting to PNG or PDF.
632
653
 
@@ -637,9 +658,27 @@ class PlotSpec(FeatureSpec):
637
658
 
638
659
  Notes
639
660
  -----
640
- Export to PDF file uses the CairoSVG library.
641
- CairoSVG is free and distributed under the LGPL-3.0 license.
642
- For more details visit: https://cairosvg.org/documentation/
661
+ - If `w`, `h`, `unit`, and `dpi` are all specified:
662
+
663
+ - The plot's pixel size (default or set by `ggsize()`) is ignored.
664
+ - The output size is calculated using the specified `w`, `h`, `unit`, and `dpi`.
665
+
666
+ - The plot is resized to fit the specified `w` x `h` area, which may affect the layout, tick labels, and other elements.
667
+
668
+ - If only `dpi` is specified:
669
+
670
+ - The plot's pixel size (default or set by `ggsize()`) is converted to inches using the standard display PPI of 96.
671
+ - The output size is then calculated based on the specified DPI.
672
+
673
+ - The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
674
+ - Useful for printing - the plot will appear nearly the same size as on screen.
675
+
676
+ - If `w`, `h` are not specified:
677
+
678
+ - The `scale` parameter is used to determine the output size.
679
+
680
+ - The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
681
+ - Useful for generating high-resolution images suitable for publication.
643
682
 
644
683
  Examples
645
684
  --------
@@ -660,6 +699,7 @@ class PlotSpec(FeatureSpec):
660
699
  p = ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + geom_jitter()
661
700
  file_like = io.BytesIO()
662
701
  p.to_pdf(file_like)
702
+
663
703
  """
664
704
  return _export_as_raster(self, path, scale, 'pdf', w=w, h=h, unit=unit, dpi=dpi)
665
705
 
@@ -875,29 +915,108 @@ def _to_html(spec, path, iframe: bool) -> Union[str, None]:
875
915
  return None
876
916
 
877
917
 
878
- def _export_as_raster(spec, path, scale: float, export_format: str, w=None, h=None, unit=None, dpi=None) -> Union[
879
- str, None]:
880
- try:
881
- import cairosvg
882
- except ImportError:
883
- import sys
884
- print("\n"
885
- "To export Lets-Plot figure to a PNG or PDF file please install CairoSVG library"
886
- "to your Python environment.\n"
887
- "CairoSVG is free and distributed under the LGPL-3.0 license.\n"
888
- "For more details visit: https://cairosvg.org/documentation/\n", file=sys.stderr)
889
- return None
918
+ def _export_as_raster(spec, path, scale: float, export_format: str, w=None, h=None, unit=None, dpi=None) -> Union[str, None]:
919
+ if get_global_bool(MAGICK_EXPORT):
920
+ if w is None and h is None and unit is None and dpi is None:
921
+ def_scale = 2.0
922
+ def_dpi = -1
923
+ def_unit = ""
924
+ else:
925
+ def_scale = 1.0
926
+ def_dpi = 300
927
+ def_unit = 'in'
928
+
929
+ return _export_with_magick(
930
+ spec,
931
+ path,
932
+ scale if scale is not None else def_scale,
933
+ export_format,
934
+ w if w is not None else -1,
935
+ h if h is not None else -1,
936
+ unit if unit is not None else def_unit,
937
+ dpi if dpi is not None else def_dpi
938
+ )
939
+ else:
940
+ return _export_with_cairo(spec, path, scale, export_format, w, h, unit, dpi)
941
+
942
+
943
+ def _export_with_magick(spec, path, scale: float, export_format: str, w, h, unit, dpi) -> Union[str, None]:
944
+ import base64
945
+ from .. import _kbridge
946
+
947
+ if isinstance(path, str):
948
+ file_path = _makedirs(path)
949
+ file_like_object = None
950
+ else:
951
+ file_like_object = path
952
+ file_path = None
953
+
954
+ png_base64 = _kbridge._export_png(spec.as_dict(), float(w), float(h), unit, int(dpi), float(scale))
955
+ png = base64.b64decode(png_base64)
890
956
 
891
957
  if export_format.lower() == 'png':
892
- export_function = cairosvg.svg2png
958
+ if file_path is not None:
959
+ with open(file_path, 'wb') as f:
960
+ f.write(png)
961
+ return file_path
962
+ else:
963
+ file_like_object.write(png)
964
+ return None
893
965
  elif export_format.lower() == 'pdf':
894
- export_function = cairosvg.svg2pdf
966
+ try:
967
+ from PIL import Image
968
+ except ImportError:
969
+ import sys
970
+ print("\n"
971
+ "To export Lets-Plot figure to a PDF file please install pillow library"
972
+ "to your Python environment.\n"
973
+ "Pillow is free and distributed under the MIT-CMU license.\n"
974
+ "For more details visit: https://python-pillow.github.io/\n", file=sys.stderr)
975
+ return None
976
+
977
+
978
+ with Image.open(io.BytesIO(png)) as img:
979
+ if img.mode == 'RGBA':
980
+ img = img.convert('RGB')
981
+
982
+ dpi = dpi if dpi is not None else 96 # Default DPI if not specified
983
+ if file_path is not None:
984
+ img.save(file_path, "PDF", dpi=(dpi, dpi))
985
+ return file_path
986
+ else:
987
+ img.save(file_like_object, "PDF", dpi=(dpi, dpi))
988
+ return None
895
989
  else:
896
990
  raise ValueError("Unknown export format: {}".format(export_format))
897
991
 
992
+
993
+ def _export_with_cairo(spec, path, scale: float, export_format: str, w=None, h=None, unit=None, dpi=None) -> Union[str, None]:
898
994
  from .. import _kbridge
899
- # Use SVG image-rendering style as Cairo doesn't support CSS image-rendering style,
900
- svg = _kbridge._generate_svg(spec.as_dict(), use_css_pixelated_image_rendering=False)
995
+
996
+ input = None
997
+ export_function = None
998
+
999
+ if export_format.lower() == 'png' or export_format.lower() == 'pdf':
1000
+ try:
1001
+ import cairosvg
1002
+ except ImportError:
1003
+ import sys
1004
+ print("\n"
1005
+ "To export Lets-Plot figure to a PNG or PDF file please install CairoSVG library"
1006
+ "to your Python environment.\n"
1007
+ "CairoSVG is free and distributed under the LGPL-3.0 license.\n"
1008
+ "For more details visit: https://cairosvg.org/documentation/\n", file=sys.stderr)
1009
+ return None
1010
+
1011
+ if export_format.lower() == 'png':
1012
+ export_function = cairosvg.svg2png
1013
+ elif export_format.lower() == 'pdf':
1014
+ export_function = cairosvg.svg2pdf
1015
+
1016
+ # Use SVG image-rendering style as Cairo doesn't support CSS image-rendering style,
1017
+ input = _kbridge._generate_svg(spec.as_dict(), use_css_pixelated_image_rendering=False)
1018
+ else:
1019
+ raise ValueError("Unknown export format: {}".format(export_format))
901
1020
 
902
1021
  if isinstance(path, str):
903
1022
  abspath = _makedirs(path)
@@ -910,10 +1029,10 @@ def _export_as_raster(spec, path, scale: float, export_format: str, w=None, h=No
910
1029
  raise ValueError("w, h, unit, and dpi must all be specified")
911
1030
 
912
1031
  w, h = _to_inches(w, unit) * dpi, _to_inches(h, unit) * dpi
913
- export_function(bytestring=svg, write_to=path, dpi=dpi, output_width=w, output_height=h)
1032
+ export_function(bytestring=input, write_to=path, dpi=dpi, output_width=w, output_height=h)
914
1033
  else:
915
1034
  scale = scale if scale is not None else 2.0
916
- export_function(bytestring=svg, write_to=path, scale=scale)
1035
+ export_function(bytestring=input, write_to=path, scale=scale)
917
1036
 
918
1037
  return result
919
1038