lets-plot 4.8.1rc1__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. lets_plot/__init__.py +382 -0
  2. lets_plot/_global_settings.py +192 -0
  3. lets_plot/_kbridge.py +197 -0
  4. lets_plot/_type_utils.py +133 -0
  5. lets_plot/_version.py +6 -0
  6. lets_plot/bistro/__init__.py +16 -0
  7. lets_plot/bistro/_plot2d_common.py +106 -0
  8. lets_plot/bistro/corr.py +448 -0
  9. lets_plot/bistro/im.py +196 -0
  10. lets_plot/bistro/joint.py +192 -0
  11. lets_plot/bistro/qq.py +207 -0
  12. lets_plot/bistro/residual.py +341 -0
  13. lets_plot/bistro/waterfall.py +332 -0
  14. lets_plot/export/__init__.py +6 -0
  15. lets_plot/export/ggsave_.py +172 -0
  16. lets_plot/frontend_context/__init__.py +8 -0
  17. lets_plot/frontend_context/_configuration.py +140 -0
  18. lets_plot/frontend_context/_dynamic_configure_html.py +115 -0
  19. lets_plot/frontend_context/_frontend_ctx.py +16 -0
  20. lets_plot/frontend_context/_html_contexts.py +223 -0
  21. lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
  22. lets_plot/frontend_context/_isolated_webview_panel_ctx.py +81 -0
  23. lets_plot/frontend_context/_json_contexts.py +39 -0
  24. lets_plot/frontend_context/_jupyter_notebook_ctx.py +82 -0
  25. lets_plot/frontend_context/_mime_types.py +7 -0
  26. lets_plot/frontend_context/_static_html_page_ctx.py +76 -0
  27. lets_plot/frontend_context/_static_svg_ctx.py +26 -0
  28. lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
  29. lets_plot/frontend_context/sandbox.py +5 -0
  30. lets_plot/geo_data/__init__.py +19 -0
  31. lets_plot/geo_data/core.py +335 -0
  32. lets_plot/geo_data/geocoder.py +988 -0
  33. lets_plot/geo_data/geocodes.py +512 -0
  34. lets_plot/geo_data/gis/__init__.py +0 -0
  35. lets_plot/geo_data/gis/fluent_dict.py +201 -0
  36. lets_plot/geo_data/gis/geocoding_service.py +42 -0
  37. lets_plot/geo_data/gis/geometry.py +91 -0
  38. lets_plot/geo_data/gis/json_request.py +232 -0
  39. lets_plot/geo_data/gis/json_response.py +308 -0
  40. lets_plot/geo_data/gis/request.py +492 -0
  41. lets_plot/geo_data/gis/response.py +247 -0
  42. lets_plot/geo_data/livemap_helper.py +65 -0
  43. lets_plot/geo_data/to_geo_data_frame.py +141 -0
  44. lets_plot/geo_data/type_assertion.py +34 -0
  45. lets_plot/geo_data_internals/__init__.py +4 -0
  46. lets_plot/geo_data_internals/constants.py +13 -0
  47. lets_plot/geo_data_internals/utils.py +33 -0
  48. lets_plot/mapping.py +115 -0
  49. lets_plot/package_data/lets-plot.min.js +3 -0
  50. lets_plot/plot/__init__.py +64 -0
  51. lets_plot/plot/_global_theme.py +14 -0
  52. lets_plot/plot/annotation.py +290 -0
  53. lets_plot/plot/coord.py +242 -0
  54. lets_plot/plot/core.py +1071 -0
  55. lets_plot/plot/expand_limits_.py +78 -0
  56. lets_plot/plot/facet.py +210 -0
  57. lets_plot/plot/font_features.py +71 -0
  58. lets_plot/plot/geom.py +9146 -0
  59. lets_plot/plot/geom_extras.py +53 -0
  60. lets_plot/plot/geom_function_.py +219 -0
  61. lets_plot/plot/geom_imshow_.py +393 -0
  62. lets_plot/plot/geom_livemap_.py +343 -0
  63. lets_plot/plot/ggbunch_.py +96 -0
  64. lets_plot/plot/gggrid_.py +139 -0
  65. lets_plot/plot/ggtb_.py +81 -0
  66. lets_plot/plot/guide.py +231 -0
  67. lets_plot/plot/label.py +187 -0
  68. lets_plot/plot/marginal_layer.py +181 -0
  69. lets_plot/plot/plot.py +245 -0
  70. lets_plot/plot/pos.py +344 -0
  71. lets_plot/plot/sampling.py +338 -0
  72. lets_plot/plot/sandbox_.py +26 -0
  73. lets_plot/plot/scale.py +3580 -0
  74. lets_plot/plot/scale_colormap_mpl.py +300 -0
  75. lets_plot/plot/scale_convenience.py +155 -0
  76. lets_plot/plot/scale_identity_.py +653 -0
  77. lets_plot/plot/scale_position.py +1342 -0
  78. lets_plot/plot/series_meta.py +209 -0
  79. lets_plot/plot/stat.py +585 -0
  80. lets_plot/plot/subplots.py +331 -0
  81. lets_plot/plot/subplots_util.py +24 -0
  82. lets_plot/plot/theme_.py +790 -0
  83. lets_plot/plot/theme_set.py +418 -0
  84. lets_plot/plot/tooltip.py +486 -0
  85. lets_plot/plot/util.py +267 -0
  86. lets_plot/settings_utils.py +244 -0
  87. lets_plot/tilesets.py +429 -0
  88. lets_plot-4.8.1rc1.dist-info/METADATA +221 -0
  89. lets_plot-4.8.1rc1.dist-info/RECORD +97 -0
  90. lets_plot-4.8.1rc1.dist-info/WHEEL +6 -0
  91. lets_plot-4.8.1rc1.dist-info/licenses/LICENSE +21 -0
  92. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
  93. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
  94. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
  95. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
  96. lets_plot-4.8.1rc1.dist-info/top_level.txt +2 -0
  97. lets_plot_kotlin_bridge.cpython-311-x86_64-linux-gnu.so +0 -0
@@ -0,0 +1,53 @@
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 .core import FeatureSpec
6
+
7
+ __all__ = ['arrow']
8
+
9
+ #
10
+ # See R doc: https://www.rdocumentation.org/packages/grid/versions/3.4.1/topics/arrow
11
+ #
12
+ def arrow(angle=None, length=None, ends=None, type=None):
13
+ """
14
+ Describe arrows to add to a line.
15
+
16
+ Parameters
17
+ ----------
18
+ angle : float
19
+ The angle of the arrow head in degrees (smaller numbers produce narrower, pointer arrows).
20
+ Essentially describes the width of the arrow head.
21
+ length : int
22
+ The length of the arrow head (px).
23
+ ends : {'last', 'first', 'both'}
24
+ Indicating which ends of the line to draw arrow heads.
25
+ type : {'open', 'closed'}
26
+ Indicating whether the arrow head should be a closed triangle.
27
+
28
+ Returns
29
+ -------
30
+ ``FeatureSpec``
31
+ Arrow object specification.
32
+
33
+ Examples
34
+ --------
35
+ .. jupyter-execute::
36
+ :linenos:
37
+ :emphasize-lines: 5, 7, 9, 11
38
+
39
+ from lets_plot import *
40
+ LetsPlot.setup_html()
41
+ ggplot() + \\
42
+ geom_segment(x=2, y=10, xend=4, yend=9, \\
43
+ arrow=arrow(type='closed')) + \\
44
+ geom_segment(x=3, y=6, xend=3, yend=9, \\
45
+ arrow=arrow(type='open')) + \\
46
+ geom_segment(x=4, y=7, xend=5, yend=10, \\
47
+ arrow=arrow(type='closed', ends='both', length=23)) + \\
48
+ geom_segment(x=5, y=8, xend=7, yend=7, \\
49
+ arrow=arrow(type='open', ends='first', angle=120, length=23))
50
+
51
+ """
52
+
53
+ return FeatureSpec('arrow', 'arrow', **locals())
@@ -0,0 +1,219 @@
1
+ # Copyright (c) 2023. 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
+ try:
4
+ import pandas as pd
5
+ except ImportError:
6
+ pd = None
7
+
8
+ try:
9
+ import polars as pl
10
+ except ImportError:
11
+ pl = None
12
+
13
+ from .core import aes
14
+ from .geom import _geom
15
+
16
+ __all__ = ['geom_function']
17
+
18
+ _fun_x_name, _fun_y_name = 'x', 'y'
19
+
20
+
21
+ def _linspace(start, stop, num):
22
+ if num == 1:
23
+ return [start]
24
+
25
+ step = (stop - start) / (num - 1)
26
+
27
+ return [start + step * i for i in range(num)]
28
+
29
+
30
+ def _get_default_xrange(xlim, n):
31
+ default_xlim = [0.0, 1.0]
32
+ default_size = 512
33
+
34
+ start, stop = xlim if xlim is not None else default_xlim
35
+ size = n if n is not None else default_size
36
+
37
+ return _linspace(start, stop, size)
38
+
39
+
40
+ def _get_fun_data(mapping, data, fun, xlim, n):
41
+ aes_x_value = None
42
+ if mapping is not None and 'x' in mapping.as_dict():
43
+ aes_x_value = mapping.as_dict()['x']
44
+
45
+ if isinstance(aes_x_value, str) and data is not None:
46
+ xs = data[aes_x_value]
47
+ elif hasattr(aes_x_value, '__iter__'):
48
+ xs = aes_x_value
49
+ else:
50
+ xs = _get_default_xrange(xlim, n)
51
+
52
+ if fun is None:
53
+ ys = [None] * len(xs)
54
+ else:
55
+ ys = [fun(x) for x in xs]
56
+
57
+ if data is None:
58
+ return {_fun_x_name: xs, _fun_y_name: ys}
59
+ else:
60
+ if isinstance(data, dict):
61
+ return {**data, **{_fun_y_name: ys}}
62
+ elif pd is not None and isinstance(data, pd.DataFrame):
63
+ return data.assign(**{_fun_y_name: ys})
64
+ elif pl is not None and isinstance(data, pl.DataFrame):
65
+ return data.with_columns(**{_fun_y_name: pl.Series(values=ys)})
66
+ else:
67
+ raise Exception("Unsupported type of data: {0}".format(data))
68
+
69
+
70
+ def _get_mapping(mapping):
71
+ mapping_dict = mapping.as_dict() if mapping is not None else {}
72
+ x_mapping_dict = {'x': _fun_x_name}
73
+ y_mapping_dict = {'y': _fun_y_name}
74
+
75
+ return aes(**{**x_mapping_dict, **mapping_dict, **y_mapping_dict})
76
+
77
+
78
+ def geom_function(mapping=None, *, data=None, stat=None, geom=None, position=None, show_legend=None, inherit_aes=None,
79
+ manual_key=None,
80
+ tooltips=None,
81
+ fun=None, xlim=None, n=None,
82
+ color_by=None,
83
+ **other_args):
84
+ """
85
+ Compute and draw a function.
86
+
87
+ Parameters
88
+ ----------
89
+ mapping : ``FeatureSpec``
90
+ Set of aesthetic mappings created by `aes() <https://lets-plot.org/python/pages/api/lets_plot.aes.html>`__ function.
91
+ Aesthetic mappings describe the way that variables in the data are
92
+ mapped to plot "aesthetics".
93
+ data : dict or Pandas or Polars ``DataFrame``
94
+ The data to be used in this layer. Specify to describe the definition area of a function.
95
+ If None, the default, the data will not be used at all.
96
+ stat : str, default='identity'
97
+ The statistical transformation to use on the data generated by the function.
98
+ Supported transformations: 'identity' (leaves the data unchanged),
99
+ 'smooth' (performs smoothing - linear default),
100
+ 'density2d' (computes and draws 2D kernel density estimate).
101
+ geom : str, default='line'
102
+ The geometry to display the function, as a string.
103
+ position : str or ``FeatureSpec``, default='identity'
104
+ Position adjustment.
105
+ Either a position adjustment name: 'dodge', 'jitter', 'nudge', 'jitterdodge', 'fill',
106
+ 'stack' or 'identity', or the result of calling a position adjustment function
107
+ (e.g., `position_dodge() <https://lets-plot.org/python/pages/api/lets_plot.position_dodge.html>`__ etc.).
108
+ show_legend : bool, default=True
109
+ False - do not show legend for this layer.
110
+ inherit_aes : bool, default=True
111
+ False - do not combine the layer aesthetic mappings with the plot shared mappings.
112
+ manual_key : str or ``layer_key``
113
+ The key to show in the manual legend.
114
+ Specify text for the legend label or advanced settings using the
115
+ `layer_key() <https://lets-plot.org/python/pages/api/lets_plot.layer_key.html>`__ function.
116
+ tooltips : ``layer_tooltips``
117
+ Result of the call to the
118
+ `layer_tooltips() <https://lets-plot.org/python/pages/api/lets_plot.layer_tooltips.html>`__ function.
119
+ Specify appearance, style and content.
120
+ Set tooltips='none' to hide tooltips from the layer.
121
+ fun : function
122
+ A function of one variable in Python syntax.
123
+ xlim : list of float, default=[0.0, 1.0]
124
+ Range of the function. Float array of length 2.
125
+ n : int, default=512
126
+ Number of points to interpolate along the x axis.
127
+ color_by : {'fill', 'color', 'paint_a', 'paint_b', 'paint_c'}, default='color'
128
+ Define the color aesthetic for the geometry.
129
+ other_args
130
+ Other arguments passed on to the layer.
131
+ These are often aesthetics settings used to set an aesthetic to a fixed value,
132
+ like color='red', fill='blue', size=3 or shape=21.
133
+ They may also be parameters to the paired geom/stat.
134
+
135
+ Returns
136
+ -------
137
+ ``LayerSpec``
138
+ Geom object specification.
139
+
140
+ Notes
141
+ -----
142
+ ``geom_function()`` understands the following aesthetics mappings:
143
+
144
+ - x : x-axis value.
145
+ - alpha : transparency level of a layer. Accept values between 0 and 1.
146
+ - color (colour) : color of the geometry. For more info see `Color and Fill <https://lets-plot.org/python/pages/aesthetics.html#color-and-fill>`__.
147
+ - linetype : type of the line. Accept codes or names (0 = 'blank', 1 = 'solid', 2 = 'dashed', 3 = 'dotted', 4 = 'dotdash', 5 = 'longdash', 6 = 'twodash'), a hex string (up to 8 digits for dash-gap lengths), or a list pattern [offset, [dash, gap, ...]] / [dash, gap, ...]. For more info see `Line Types <https://lets-plot.org/python/pages/aesthetics.html#line-types>`__.
148
+ - size : line width.
149
+
150
+ ----
151
+
152
+ To hide axis tooltips, set 'blank' or the result of `element_blank() <https://lets-plot.org/python/pages/api/lets_plot.element_blank.html>`__
153
+ to the ``axis_tooltip``, ``axis_tooltip_x`` or ``axis_tooltip_y`` parameter of the `theme() <https://lets-plot.org/python/pages/api/lets_plot.theme.html>`__.
154
+
155
+ Examples
156
+ --------
157
+ .. jupyter-execute::
158
+ :linenos:
159
+ :emphasize-lines: 9
160
+
161
+ import numpy as np
162
+ from scipy.stats import norm
163
+ from lets_plot import *
164
+ LetsPlot.setup_html()
165
+ np.random.seed(42)
166
+ x = np.random.normal(size=500)
167
+ ggplot() + \\
168
+ geom_density(aes(x='x'), data={'x': x}) + \\
169
+ geom_function(fun=norm.pdf, xlim=[-4, 4], color="red")
170
+
171
+ |
172
+
173
+ .. jupyter-execute::
174
+ :linenos:
175
+ :emphasize-lines: 5-6
176
+
177
+ from lets_plot import *
178
+ LetsPlot.setup_html()
179
+ data = {'x': list(range(-5, 6))}
180
+ ggplot() + \\
181
+ geom_function(aes(x='x', color='y', size='y'), \\
182
+ data=data, fun=lambda t: t**2, show_legend=False) + \\
183
+ scale_color_gradient(low="red", high="green") + \\
184
+ scale_size(range=[1, 4], trans='reverse')
185
+
186
+ |
187
+
188
+ .. jupyter-execute::
189
+ :linenos:
190
+ :emphasize-lines: 4-5
191
+
192
+ from math import sqrt
193
+ from lets_plot import *
194
+ LetsPlot.setup_html()
195
+ fun_layer = lambda fun: geom_function(fun=fun, xlim=[-2, 2], n=9, \\
196
+ stat='density2d', geom='density2d')
197
+ gggrid([
198
+ ggplot() + fun_layer(lambda t: t),
199
+ ggplot() + fun_layer(lambda t: t**2),
200
+ ggplot() + fun_layer(lambda t: 2**t),
201
+ ggplot() + fun_layer(lambda t: sqrt(4 - t**2)) + coord_fixed(ratio=2),
202
+ ], ncol=2)
203
+
204
+ """
205
+ fun_stat = stat if stat is not None else 'identity'
206
+ fun_geom = geom if geom is not None else 'line'
207
+
208
+ return _geom(fun_geom,
209
+ mapping=_get_mapping(mapping),
210
+ data=_get_fun_data(mapping, data, fun, xlim, n),
211
+ stat=fun_stat,
212
+ position=position,
213
+ show_legend=show_legend,
214
+ inherit_aes=inherit_aes,
215
+ manual_key=manual_key,
216
+ sampling=None,
217
+ tooltips=tooltips,
218
+ color_by=color_by,
219
+ **other_args)
@@ -0,0 +1,393 @@
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
+ import base64
6
+ import io
7
+
8
+ from .core import aes
9
+ from .geom import _geom
10
+ from .scale import scale_gradientn
11
+ from .scale import scale_grey
12
+ from .util import as_boolean
13
+ from .._type_utils import is_ndarray
14
+
15
+ try:
16
+ import png
17
+ except ImportError:
18
+ png = None
19
+
20
+ try:
21
+ import numpy
22
+ except ImportError:
23
+ numpy = None
24
+
25
+ try:
26
+ from palettable.matplotlib import matplotlib as palettable
27
+ except ImportError:
28
+ palettable = None
29
+
30
+ __all__ = ['geom_imshow']
31
+
32
+
33
+ def _hex2rgb(hex_c, alpha):
34
+ hex_s = hex_c.lstrip('#')
35
+ list_rgb = [int(hex_s[i:i + 2], 16) for i in (0, 2, 4)]
36
+ if alpha is not None:
37
+ list_rgb.append(int(alpha + 0.5))
38
+ return list_rgb
39
+
40
+
41
+ def _hex2rgb_arr_uint8(hex_c, alpha=None):
42
+ """
43
+ Create 'palette' for PyPNG PNG writer
44
+ """
45
+ return numpy.array(_hex2rgb(hex_c, alpha), dtype=numpy.uint8)
46
+
47
+
48
+ def _normalize_2D(image_data, norm, vmin, vmax, min_lum):
49
+ """
50
+ Take numpy 2D array of float or int-s and
51
+ return 2D array of ints with the target range [0..255].
52
+ Values outside the target range will be later clipped.
53
+ """
54
+ min_lum = max(0, min_lum)
55
+ max_lum = 255 - min_lum
56
+
57
+ vmin = float(vmin if vmin is not None else numpy.nanmin(image_data))
58
+ vmax = float(vmax if vmax is not None else numpy.nanmax(image_data))
59
+ if vmin > vmax:
60
+ raise ValueError("vmin value must be less then vmax value, was: {} > {}".format(vmin, vmax))
61
+
62
+ normalize = as_boolean(norm, default=True)
63
+
64
+ # Make a copy via `numpy.copy()` or via `arr.astype()`
65
+ # - prevent modification of the original image
66
+ # - work around read-only flag in the original image
67
+
68
+ if normalize:
69
+ if vmin == vmax:
70
+ image_data = numpy.copy(image_data)
71
+ image_data[True] = 127
72
+ else:
73
+ # float array for scaling
74
+ if image_data.dtype.kind == 'f':
75
+ image_data = numpy.copy(image_data)
76
+ else:
77
+ image_data = image_data.astype(numpy.float32)
78
+
79
+ image_data.clip(vmin, vmax, out=image_data)
80
+
81
+ ratio = max_lum / (vmax - vmin)
82
+ image_data -= vmin
83
+ image_data *= ratio
84
+ image_data += min_lum
85
+ else:
86
+ # no normalization
87
+ image_data = numpy.copy(image_data)
88
+ image_data.clip(min_lum, max_lum, out=image_data)
89
+ vmin = float(numpy.nanmin(image_data))
90
+ vmax = float(numpy.nanmax(image_data))
91
+
92
+ return (image_data, vmin, vmax)
93
+
94
+
95
+ def geom_imshow(image_data, cmap=None, *,
96
+ norm=None, alpha=None,
97
+ vmin=None, vmax=None,
98
+ extent=None,
99
+ compression=None,
100
+ show_legend=True,
101
+ color_by="paint_c",
102
+ ):
103
+ """
104
+ Display image specified by ndarray with shape.
105
+
106
+ - (M, N) - grey-scale image
107
+ - (M, N, 3) - color RGB image
108
+ - (M, N, 4) - color RGB image with alpha channel
109
+
110
+ This geom is not as flexible as `geom_raster() <https://lets-plot.org/python/pages/api/lets_plot.geom_raster.html>`__
111
+ or `geom_tile() <https://lets-plot.org/python/pages/api/lets_plot.geom_tile.html>`__
112
+ but vastly superior in the terms of rendering efficiency.
113
+
114
+ Parameters
115
+ ----------
116
+ image_data : ndarray
117
+ Specify image type, size and pixel values.
118
+ Supported array shapes are:
119
+
120
+ - (M, N): an image with scalar data. The values are mapped to colors (greys by default) using normalization. See parameters ``norm``, ``cmap``, ``vmin``, ``vmax``.
121
+ - (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
122
+ - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int).
123
+
124
+ The first two dimensions (M, N) define the rows and columns of the image.
125
+ Out-of-range values are clipped.
126
+ cmap : str, optional
127
+ Name of colormap. For example "viridis", "magma", "plasma", "inferno", or any other colormap
128
+ which is supported by the Palettable package (https://github.com/jiffyclub/palettable)
129
+ This parameter is ignored for RGB(A) images.
130
+ norm : bool, default=True
131
+ True - luminance values in grey-scale image will be scaled to [0-255] range using a linear scaler.
132
+ False - disables scaling of luminance values in grey-scale image.
133
+ This parameter is ignored for RGB(A) images.
134
+ alpha: float, optional
135
+ The alpha blending value, between 0 (transparent) and 1 (opaque).
136
+ vmin, vmax : number, optional
137
+ Define the data range used for luminance normalization in grey-scale images.
138
+ This parameter is ignored for RGB(A) images or if parameter ``norm=False``.
139
+ extent : list of 4 numbers: [left, right, bottom, top], optional
140
+ Define image's bounding box in terms of the "data coordinates".
141
+
142
+ - ``left``, ``right``: coordinates of pixels' outer edge along the x-axis for pixels in the 1-st and the last column.
143
+ - ``bottom``, ``top``: coordinates of pixels' outer edge along the y-axis for pixels in the 1-st and the last row.
144
+
145
+ The default is: [-0.5, ncol-0.5, -0.5, nrow-0.5]
146
+ compression : int, optional
147
+ The compression level to be used by the ``zlib`` module.
148
+ Values from 0 (no compression) to 9 (highest).
149
+ Value None means that the ``zlib`` module uses
150
+ the default level of compression (which is generally acceptable).
151
+ show_legend : bool, default=True
152
+ Greyscale images only.
153
+ False - do not show legend for this layer.
154
+ color_by : {'fill', 'color', 'paint_a', 'paint_b', 'paint_c'}, default='paint_c'
155
+ Define the color aesthetic used by the legend shown for a greyscale image.
156
+
157
+ Returns
158
+ -------
159
+ ``LayerSpec``
160
+ Geom object specification.
161
+
162
+ Notes
163
+ -----
164
+ This geom doesn't understand any aesthetics.
165
+ It doesn't support color scales either.
166
+
167
+ Examples
168
+ --------
169
+ .. jupyter-execute::
170
+ :linenos:
171
+ :emphasize-lines: 6
172
+
173
+ import numpy as np
174
+ from lets_plot import *
175
+ LetsPlot.setup_html()
176
+ np.random.seed(42)
177
+ image = np.random.randint(256, size=(64, 64, 4))
178
+ ggplot() + geom_imshow(image)
179
+
180
+ |
181
+
182
+ .. jupyter-execute::
183
+ :linenos:
184
+ :emphasize-lines: 7
185
+
186
+ import numpy as np
187
+ from lets_plot import *
188
+ LetsPlot.setup_html()
189
+ n = 64
190
+ image = 256 * np.linspace(np.linspace(0, .5, n), \\
191
+ np.linspace(.5, .5, n), n)
192
+ ggplot() + geom_imshow(image, norm=False)
193
+
194
+ |
195
+
196
+ .. jupyter-execute::
197
+ :linenos:
198
+ :emphasize-lines: 6
199
+
200
+ import numpy as np
201
+ from lets_plot import *
202
+ LetsPlot.setup_html()
203
+ np.random.seed(42)
204
+ image = np.random.normal(size=(64, 64))
205
+ ggplot() + geom_imshow(image, vmin=-1, vmax=1)
206
+
207
+ """
208
+
209
+ if png is None:
210
+ raise ValueError("pypng is not installed")
211
+
212
+ if not is_ndarray(image_data):
213
+ raise ValueError("Invalid image_data: ndarray is expected but was {}".format(type(image_data)))
214
+
215
+ if image_data.ndim not in (2, 3):
216
+ raise ValueError(
217
+ "Invalid image_data: 2d or 3d array is expected but was {}-dimensional".format(image_data.ndim))
218
+
219
+ if alpha is not None:
220
+ if not (0 <= alpha <= 1):
221
+ raise ValueError(
222
+ "Invalid alpha: expected float in range [0..1] but was {}".format(alpha))
223
+
224
+ if compression is not None:
225
+ if not (0 <= compression <= 9):
226
+ raise ValueError(
227
+ "Invalid compression: expected integer in range [0..9] but was {}".format(compression))
228
+
229
+ greyscale = (image_data.ndim == 2)
230
+ if greyscale:
231
+ # Greyscale image
232
+
233
+ has_nan = numpy.isnan(image_data.max())
234
+ min_lum = 0 if not (has_nan and cmap) else 1 # index 0 reserved for NaN-s
235
+
236
+ (image_data, greyscale_data_min, greyscale_data_max) = _normalize_2D(image_data, norm, vmin, vmax, min_lum)
237
+ height, width = image_data.shape
238
+ has_nan = numpy.isnan(image_data.max())
239
+
240
+ if cmap:
241
+ # colormap via palettable
242
+ if not palettable:
243
+ raise ValueError(
244
+ "Can't process `cmap`: please install 'Palettable' (https://pypi.org/project/palettable/) to your "
245
+ "Python environment. "
246
+ )
247
+
248
+ # prepare palette
249
+ palette = None
250
+ if not has_nan:
251
+ alpha_ch_val = 255 if alpha is None else 255 * alpha
252
+ cmap_256 = palettable.get_map(cmap + "_256")
253
+ palette = [_hex2rgb_arr_uint8(c, alpha_ch_val) for c in cmap_256.hex_colors]
254
+ else:
255
+ alpha_ch_val = 255 if alpha is None else 255 * alpha
256
+ cmap_255 = palettable.get_map(cmap + "_255")
257
+ # transparent color at index 0
258
+ palette = [numpy.array([0, 0, 0, 0], dtype=numpy.uint8)] \
259
+ + [_hex2rgb_arr_uint8(c, alpha_ch_val) for c in cmap_255.hex_colors]
260
+
261
+ # replace indexes with palette colors
262
+ if has_nan:
263
+ # replace all NaN-s with 0 (index 0 for transparent color)
264
+ numpy.nan_to_num(image_data, copy=False, nan=0)
265
+ image_data = numpy.take(palette, numpy.round(image_data).astype(numpy.int32), axis=0)
266
+ else:
267
+ # Greyscale
268
+ alpha_ch_scaler = 1 if alpha is None else alpha
269
+ is_nan = numpy.isnan(image_data)
270
+ im_shape = numpy.shape(image_data)
271
+ alpha_ch = numpy.zeros(im_shape, dtype=image_data.dtype)
272
+ alpha_ch[is_nan == False] = 255 * alpha_ch_scaler
273
+ image_data[is_nan] = 0
274
+ image_data = numpy.repeat(image_data[:, :, numpy.newaxis], 3, axis=2) # convert to RGB
275
+ image_data = numpy.dstack((image_data, alpha_ch)) # convert to RGBA
276
+ else:
277
+ # Color RGB/RGBA image
278
+ # Make a copy:
279
+ # - prevent modification of the original image
280
+ # - drop read-only flag
281
+ image_data = numpy.copy(image_data)
282
+ if image_data.dtype.kind == 'f':
283
+ image_data *= 255
284
+
285
+ height, width, nchannels = image_data.shape
286
+
287
+ if nchannels == 3:
288
+ alpha_ch_scaler = 1 if alpha is None else alpha
289
+ # RGB image: add alpha channel (RGBA)
290
+ alpha_ch = numpy.full((height, width, 1), 255 * alpha_ch_scaler, dtype=image_data.dtype)
291
+ image_data = numpy.dstack((image_data, alpha_ch))
292
+ elif nchannels == 4 and alpha is not None:
293
+ # RGBA image: apply alpha scaling
294
+ image_data[:, :, 3] *= alpha
295
+
296
+ # Make sure all values are ints in range 0-255.
297
+ image_data.clip(0, 255, out=image_data)
298
+
299
+ # Image extent with possible axis flipping.
300
+ # The default image bounds include 1/2 unit size expand in all directions.
301
+ ext_x0, ext_x1, ext_y0, ext_y1 = -.5, width - .5, -.5, height - .5
302
+ if extent:
303
+ try:
304
+ ext_x0, ext_x1, ext_y0, ext_y1 = [float(v) for v in extent]
305
+ except ValueError as e:
306
+ raise ValueError(
307
+ "Invalid `extent`: list of 4 numbers expected: {}".format(e)
308
+ )
309
+
310
+ if ext_x0 > ext_x1:
311
+ # copy after flip to work around this numpy issue: https://github.com/drj11/pypng/issues/91
312
+ image_data = numpy.flip(image_data, axis=1).copy()
313
+ ext_x0, ext_x1 = ext_x1, ext_x0
314
+
315
+ if ext_y0 > ext_y1:
316
+ image_data = numpy.flip(image_data, axis=0)
317
+ ext_y0, ext_y1 = ext_y1, ext_y0
318
+
319
+ # Make sure each value is 1 byte and the type is numpy.int8.
320
+ # Otherwise, pypng will produce broken colors.
321
+ if image_data.dtype.kind == 'f':
322
+ # Can't cast directly from float to np.int8.
323
+ image_data += 0.5
324
+ image_data = image_data.astype(numpy.int16)
325
+
326
+ if image_data.dtype != numpy.int8:
327
+ image_data = image_data.astype(numpy.int8)
328
+
329
+ # Reshape to 2d-array:
330
+ image_2d = image_data.reshape(-1, width * 4) # always 4 channels (RGBA)
331
+
332
+ # PNG writer
333
+ png_bytes = io.BytesIO()
334
+ png.Writer(
335
+ width=width,
336
+ height=height,
337
+ greyscale=False,
338
+ alpha=True,
339
+ bitdepth=8,
340
+ compression=compression
341
+ ).write(png_bytes, image_2d)
342
+
343
+ href = 'data:image/png;base64,' + str(base64.standard_b64encode(png_bytes.getvalue()), 'utf-8')
344
+
345
+ # The Legend (colorbar)
346
+ show_legend = as_boolean(show_legend, default=True)
347
+ normalize = as_boolean(norm, default=True)
348
+ legend_title = ""
349
+ color_scale = None
350
+ color_scale_mapping = None
351
+ if greyscale and show_legend:
352
+ # aes(color=[greyscale_data_min, greyscale_data_max])
353
+ color_scale_mapping = aes(**{color_by: [greyscale_data_min, greyscale_data_max]})
354
+ if cmap and normalize:
355
+ cmap_32 = palettable.get_map(cmap + "_32")
356
+ # color_scale = scale_color_gradientn(colors=cmap_32.hex_colors, name=legend_title)
357
+ color_scale = scale_gradientn(aesthetic=color_by, colors=cmap_32.hex_colors, name=legend_title)
358
+ elif cmap and not normalize:
359
+ cmap_256 = palettable.get_map(cmap + "_256")
360
+ start = max(0, round(greyscale_data_min))
361
+ end = min(255, round(greyscale_data_max))
362
+ cmap_hex_colors = cmap_256.hex_colors[start:end]
363
+ if len(cmap_hex_colors) > 32:
364
+ # reduce number of colors to 32
365
+ indices = numpy.linspace(0, len(cmap_hex_colors) - 1, 32, dtype=int)
366
+ cmap_hex_colors = [cmap_hex_colors[i] for i in indices]
367
+
368
+ # color_scale = scale_color_gradientn(colors=cmap_hex_colors, name=legend_title)
369
+ color_scale = scale_gradientn(aesthetic=color_by, colors=cmap_hex_colors, name=legend_title)
370
+ else:
371
+ start = 0 if normalize else greyscale_data_min / 255.
372
+ end = 1 if normalize else greyscale_data_max / 255.
373
+ # color_scale = scale_color_grey(start=start, end=end, name=legend_title)
374
+ color_scale = scale_grey(aesthetic=color_by, start=start, end=end, name=legend_title)
375
+
376
+ # Image geom layer
377
+ geom_image_layer = _geom(
378
+ 'image',
379
+ mapping=color_scale_mapping,
380
+ href=href,
381
+ xmin=ext_x0,
382
+ ymin=ext_y0,
383
+ xmax=ext_x1,
384
+ ymax=ext_y1,
385
+ show_legend=show_legend,
386
+ inherit_aes=False,
387
+ color_by=color_by if (show_legend and greyscale) else None,
388
+ )
389
+
390
+ if (color_scale is not None):
391
+ geom_image_layer = geom_image_layer + color_scale
392
+
393
+ return geom_image_layer